一、创建和销毁对象
1、考虑用静态工厂方法代替构造器
- 优点
- 有名称,易于管理和理解
- 不必每次都创建一个新对象
- 可以返回任何子类型的对象
- 例如写服务端接口,根据用户的参数init不同的service存入Map,key就是用户参数,需要哪个业务就用那个service
- 参数实例化的时候使代码变得更加简介
- new hashMap< String, Object>();如果静态工厂方法通过泛型返回这个,就不用写这么多了。现在好像也不用非要指明了,这是一种思想
- 缺点
- 最主要的是必须要有构造器,否则不能被子类化(不能被继承)
- 与一般的静态方法实际上没有任何区别,这个就需要约定
- 代码见下载包的chapter02-item01,下载地址:effective java中文第2版示例代码
2、遇到多个构造器参数时要考虑用构建器
- 其他常用方法的缺点
- 普通构造器,参数多了的话各种组合麻烦不说,而且名称都一样,只能通过传递的参数来判断是什么逻辑
- javaben模式,set的话挺直观,但是有严重的缺点,构造被分到了几个调用中,不一定能保证javabean处于一致的状态,需要保证线程安全
- 构建器(Builder)
- 优点
- 缺点
- 实例化对象的时候需要先创建构造器
- 参数少的时候有些冗余
- 描述
- 让用户利用所有必要的参数调用构造器(或静态工厂),得到一个builder对象,然后客户端在builder对象上调用类似于setter的方法,来设置每个相关的可选参数,最后客户端调用无参的build方法来生成不可变的对象
- 内部类实现,类似于mybatis generator生成的bean
- 代码
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;
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
public static class Builder {
private final int servingSize;
private final int servings;
private int calories = 0;
private int fat = 0;
private int carbohydrate = 0;
private int sodium = 0;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val) {
calories = val;
return this;
}
public Builder fat(int val) {
fat = val;
return this;
}
public Builder carbohydrate(int val) {
carbohydrate = val;
return this;
}
public Builder sodium(int val) {
sodium = val;
return this;
}
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
public static void main(String[] args) {
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8)
.calories(100).sodium(35).carbohydrate(27).build();
}
}
3、用私有构造器或者枚举类型强化Singleton属性
- 单例模式的一般实现
- 模式看了一篇文章: 单例模式的七种写法
- 本书的作者推荐使用枚举类型,并且称为实现Singleton的最佳方法
- 更加简洁
- 无偿提供了序列化机制
- 绝对防止多次实例化
- 防止反射攻击
- 代码
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() {
System.out.println("Whoa baby, I'm outta here!");
}
public static void main(String[] args) {
Elvis elvis = Elvis.INSTANCE;
elvis.leaveTheBuilding();
}
}
4、通过私有构造器强化不可实例化的能力
- 解释:有些类比如静态类,工具类不想被实例化,这些类实例化毫无意义,但是编译器会默认一个构造方法,在已发行的API里面就经常能看到一堆毫无意义的方法。
- 解决:显式的声明构造方法,然后定义为private
- 缺点:继承它的子类将没有默认的超类构造方法可用,都需要声明构造方法
- 其他:在私有构造方法中加:throw new AssertionError();会防止类内部实例化,并不是必须的
- 个人看法:估计这个我们是用不到了。思想很好,在多用组合少用继承的大方向下这个很能提现专业
5、避免创建不必要的对象
private static final Date BOOM_START;
private static final Date BOOM_END;
static {
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
BOOM_START = gmtCal.getTime();
gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
BOOM_END = gmtCal.getTime();
}
public boolean isBabyBoomer() {
return birthDate.compareTo(BOOM_START) >= 0
&& birthDate.compareTo(BOOM_END) < 0;
}
- 解释:将只需要创建一次的对象放入静态域里防止多次创建
- 注意:
- 防止无意识的自动装箱,在运算的时候使用基本类型
- 小对象只做很少量的显式工作,所以小对象的创建和回收动作是非常廉价的,能提升程序的清晰性、简洁性和功能性,一般是好事。所以要看现实,注意不要创建不必要的对象
6、消除过期的对象引用
- java是自动内存管理的,但是有一些漏洞
- 如果一个栈先是增长,然后再收缩,那么从栈中弹出来的对象将不会当作垃圾回收
- 代码:将弹出的对象设为null即可
Object result = elements[--size];
element[size] = null;
- 总结
- 不用过分小心,如果是一个不那么复杂的方法,方法结束则自动清理了
- 一般而言,一旦元素被释放了,则该元素中包含的任何对象引用都应该被清空
7、避免使用终结方法