Effective Java-第二章
第1章
如何最有效地使用Java程序设计语言机器基本类库:java.lang,java.util,java.util.concurrent和java.io。
Sun JDK1.6_05版本
第2章 创建和销毁对象
创建和销毁对象:何时以及如何创建对象,何时以及如何避免创建对象,如何确保它们能够适时地销毁,以及如何管理对象销毁之前必须进行的各种清理动作。
1. 考虑用静态工厂方法代替构造器
为了让客户端获取对象的一个实例,最常用的方法就是提供一个公共的构造器,还有另一种方法:类可以提供一个公共的静态工厂方法(static factory method),它只是一个返回类的实例的静态方法。
注意静态工厂方法与设计模式中的工程方法模式不同。
优点
- 静态工厂方法与构造器不同的第一大优势在于,它们又名称:如果构造器的参数本身没有确切的描述正被返回的对象,那么具有适当名称的静态工厂会更容易使用,产生的客户端代码也更易于阅读。例如:BigInteger(int, int, Random)返回的BigInteger可能为素数,如果用名为BigInteger.probablePrime的静态工厂方法来表示,显得更为清楚。
- 静态工厂方法与构造器不同的第二大优势在于,不必再每次调用它们的时候都创建一个新对象:不可变类可以使用预先创建好的实例,或则将创建好的实例缓存。
- 静态工厂方法与构造器不同的第三大优势在于,它们可以返回原返回类型的任何子类型的对象:选择返回对象的类时就有更大的灵活性。例如Java Collections Framework的集合接口有32个便利实现,分别提供了不可修改的集合,同步集合等。几乎所有这些实现都通过静态工厂方法在不可实例化的类java.util.Collections中导出,所有返回对象的类都是非公有的。
- 静态工厂方法的第四大优势在于,在创建参数化类型实例的时候,它们使代码变得更加简洁。
缺点
- 类如果不含公有的或受保护的构造器,就不能被子类化。
- 它们与其他的静态方法实际上没有任何区别。静态工厂方法的一些惯用名称:valueOf,of,getInstance,newInstance,getType,newType
总结:静态工厂更加合适,因此切忌第一反应就是提供公有的构造器,而不先考虑静态工厂。
2. 遇到多个构造器参数时要考虑用构造器
静态工厂和构造器有个共同的局限性:他们都不能很好地扩展到大量的可选参数。
面对很多参数的类,如何创建类?
(1)重载构造函数
package org.github.effective.p2;
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
public NutritionFacts(int servingSize, int servings) {
this(servingSize, servings, 0);
}
public NutritionFacts(int servingSize, int servings, int calories) {
this(servingSize, servings, calories, 0);
}
public NutritionFacts(int servingSize, int servings, int calories, int fat) {
this(servingSize, servings, calories, fat, 0);
}
public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) {
this(servingSize, servings, calories, fat, sodium, 0);
}
public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbonhydrate) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
this.carbohydrate = carbonhydrate;
}
}
重载构造器模式可行,但是当有许多参数的时候,客户端代码会很难编写,并且仍然较难阅读。
(2)JavaBeans模式
调用一个无参构造器来创建对象,然后调用setter方法来设置每个必要的参数,以及每个相关的可选参数。
缺点:
因为构造过程被分到了几个调用中,在构造过程中JavaBean可能处于不一致的状态。
JavaBean模式阻止了把类做成不可变的可能。
(3)Builder模式
不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造前期得到一个builder对象,然后客户端在Builder对象上调用类似于setter的方法,来设置每个相关的可选参数,最后客户端调用无参的build方法来生成不可变的对象。这个Builder是它构建的类的静态成员类。
package org.github.effective.p2;
public class NutritionFacts2 {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
public static class Builder {
private final int servingSize;
private final int servings;
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val) {
this.calories = val;
return this;
}
public Builder fat(int val) {
this.fat = val;
return this;
}
public Builder carbohydrate(int val) {
this.carbohydrate = val;
return this;
}
public Builder sodium(int val) {
this.sodium = val;
return this;
}
public NutritionFacts2 build() {
return new NutritionFacts2(this);
}
}
private NutritionFacts2(Builder builder) {
this.servingSize = builder.servingSize;
this.servings = builder.servings;
this.calories = builder.calories;
this.fat = builder.fat;
this.sodium = builder.sodium;
this.carbohydrate = builder.carbohydrate;
}
public static void main(String[] args) {
NutritionFacts2 cocaCola = new NutritionFacts2.Builder(240, 8).calories(100).sodium(50).carbohydrate(27).build();
}
}
总结:如果累的构造器或静态工厂中具有多个参数,这几这种类时,Builder模式就是不错的选择,特别是当大多数参数都是可选的时候。
3. 用私有构造器或枚举类型强化Singleton属性
Singleton指仅仅被实例化一次的类,通常被用来代表那些本质上唯一的系统组件。
创建单例的几种方法:
(1)
public class Elvis {
public static final Elvis INSTANCE = new Elvis()
private Elvis(){}
public void m(){......}
}
但是享有特权的客户端可以借助AccessibleObject.setAccessible方法,通过反射机制调用私有构造器。
(2)
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis(){}
public static Elvis getInstance(){
return INSTANCE;
}
public void m() {......}
}
为了利用其中一种方法实现的Singleton类变成是可序列号的。仅仅在声明上加上implements Serializble是不够的,为了维护并保证Singleton,必须声明所有实例域都是transient的,并提供一个readResolve方法。否则每次反序列化一个序列化的实例时,都会创建一个新的实例。
(3)包含单个元素的枚举类型
public enum Elvis {
INSTANCE;
public void m(){.......}
}
这种方法在功能上与公有域方法相近,但是它更加简洁,无偿提供了序列化机制,防止多次实例化,即使是在面对复杂的序列化或反射攻击的时候。虽然这种方法还没有广泛采用,但是单元素的枚举类型已经成为实现Singleton的最佳方法。
4. 通过私有构造器强化不可实例化的能力
工具类不希望别实例化,实例对它没有任何意义。
public class Utility {
private Utility() {
throw new AssertionError()
}
}
由于显式的构造器是私有的,所有不可以在该类的外部访问它,AssertionError不是必须的,但是它可以避免不小心在类的内部调用构造器。保证该类在任何情况下都不会被实例化。
5. 避免创建不必要的对象
一般来说,最好能重用对象而不是在每次需要的时候就创建一个相同功能的新对象。如果对象是不可变的(immutable),就始终可以被重用。
6. 消除过期的对象引用
清空对象引用应该是一种例外,而不是一种规范行为。消除过期引用最好的方法是让包含该引用的变量结束其生命周期。
7. 避免使用终结方法
finalizer方法通常是不可预测的,也是很危险的,一般情况下是不必要的。
x.参考文档
《Effective Java中文版 第2版》
Effective Java-第二章的更多相关文章
- 如何创建和销毁对象(Effective Java 第二章)
最近有在看Effective Java,特此记录下自己所体会到的东西,写篇博文会更加的加深印象,如有理解有误的地方,希望不吝赐教. 这章主题主要是介绍:何时以及如何创建对象,何时以及如何避免创建对象, ...
- [Effective Java]第二章 创建和销毁对象
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...
- 对于所有对象都通用方法的解读(Effective Java 第二章)
这篇博文主要介绍覆盖Object中的方法要注意的事项以及Comparable.compareTo()方法. 一.谨慎覆盖equals()方法 其实平时很少要用到覆盖equals方法的情况,没有什么特殊 ...
- “全栈2019”Java第二章:安装JDK11(Windows)
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 文章原文链接 "全栈2019"Java第二章:安装JDK11(Windows) 下一 ...
- Effective Java 第二版 Enum
/** * Effective Java 第二版 * 第30条:用enum代替int常量 */ import java.util.HashMap;import java.util.Map; publi ...
- Java 第二章 变量
第二章 变量 变量称为:是计算机语言中能储存计算机结果或能表示值抽象概念 .变量可以通过变量名访问 int money ; //变量 money=1000; //赋值 int money=1000: ...
- Java第二章----对象和类
从第一章到第二章整整隔了一个月的时间,这速度也是慢的无语了.因为这个月负责开发公司一个SaaS类型APP,忙的昏天暗地终于上线了,这才有时间写个博客.本章还是以概念为主,有点枯燥重在理解. 第一节:对 ...
- Java 第二章 变量、数据类型和运算符
第二章 变量.数据类型和运算符 什么是变量: 变量代表一块内存区域,变量类型不一样,这一块内存的大小也不一样. #在编程语言里面,你可以通过定义变量,向内存里添加数据或者修改内存已有的数据. ...
- 《Effective Java 第二版》读书笔记
想成为更优秀,更高效程序员,请阅读此书.总计78个条目,每个对应一个规则. 第二章 创建和销毁对象 一,考虑用静态工厂方法代替构造器 二, 遇到多个构造器参数时要考虑用builder模式 /** * ...
- Effective java第一章引言
菜鸟一枚,开始读第一本书<Effective Java>(第二版)~ 看引言就有好多名词不懂(>_<) 导出的API由所有可在定义该API的包之外访问的API元素组成.一个包的 ...
随机推荐
- C# 获取文件版本
var versionInfo = FileVersionInfo.GetVersionInfo(filePath); String productVersion = versionInfo.Prod ...
- Toad 常用快捷键
F9 执行全部sql Ctrl_Enter 执行当前sql Ctrl+T 补全table_name ...
- Synopsys EDA工具在LinuxMint 18(Ubuntu 16.04.2)安装注意事项
Synopsys家的工具官方对Linux发行版支持为RHEL 5/6/7及SUSE 12/13,对于2014版本的工具(DC.ICC.PT.VCS.HSPICE等).其实,在Debian系及衍生版本上 ...
- JavaScript 创建类/对象的几种方式
在JS中,创建对象(Create Object)并不完全是我们时常说的创建类对象,JS中的对象强调的是一种复合类型,JS中创建对象及对对象的访问是极其灵活的. JS对象是一种复合类型,它允许你通过变量 ...
- OSG 中文解决方案 【转】
概述 本文只限于 windows 环境下. OSG 在 windows 下对中文支持已经非常的好了,但是可能很多人并不知道如何去正确的使用.为了解决这些常见的问题,还有一些基础知识的普及.特此把 OS ...
- C# http Post 方法
摘自: http://geekswithblogs.net/rakker/archive/2006/04/21/76044.aspx Http Post in C# Searched out on t ...
- Spring框架学习(8)spring mvc上传下载
内容源自:spring mvc上传下载 如下示例: 页面: web.xml: <?xml version="1.0" encoding="UTF-8"?& ...
- Linux下解压tar.xz文件
xz -d glib-2.14.tar.xz tar -xvf glib-2.14.tar 前面一个是将xz文件解压成tar文件,后面一个是将tar文件解压. xz使用格式:压缩: xz -z fil ...
- 解决Oracle在Linux下Listener起不来,error 111错误
近来发生一个问题有点头疼,在linux上的Oracle数据库突然无法访问 主要报错如下: 基于本人的走歪路经验,分享一下我的解决思路: 首先,最直观的一点,监听器起不来,是不是数据库本身就没起来 se ...
- 更改Mysql数据库存储位置
默认安装位置 C:\Program Files\MySQL\MySQL Server 5.7 一.首先把mysql的服务先停掉. 二.更改MySQL配置文件My.ini中的数据库存储主路径 打开MyS ...