第1条:考虑用静态工厂方法代替构造器
最合适的解决访华四是Builder模式:
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 static class Builder{ //Required parameters private final int servingSize; private final int servings; //Optional parameters - initialized to default values 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); } } private NutritionFacts(Builder builder){ servingSize = builder.servingSize; servings = builder.servings; calories = builder.calories; fat = builder.fat; sodium = builder.sodium; carbohydrate = builder.carbohydrate; } }
NutritionFacts cocaCola = new NutritionFacts.Builder(240,0).calories(100).sodium(35).carbohydrate(27).build();
还可以使用单个builder构建多个对象,做成抽象工厂。1.5以后还可以使用泛型。
可以看出,Builder模式比重叠构造器可能更加冗长,所以只有在参数足够多的时候才使用。
第3条:用私有构造器或者枚举类型强化Singleton属性
一般来说,实现singleTon模式,是将构造方法用private修饰,并将要singleton的类设置一个public static final引用指向一个唯一的实例对象。但这种方式可能被反射破解,改进的方式是在私有构造器中设置一个判断,如果要生成第二个实例,则抛出异常。
另一种方法,是使用静态工厂方法,将真正的唯一实例保存在一个private static final引用中,和前面的方法相比,虽然前面的方法是用了公有域方法,可以实现内联提高效率,但是现代JVM都对public static方法做了调用内联,因此效率没有损失。另外,工厂方法提高了灵活性,为进一步修改(去掉singleton,控制实例)提供了修改空间。
从1.5起,第三种方法出现了:
public enum Elvis{
INSTANCE;
}
这种方式功能上与公有域方法相近,但更简洁,且提供了序列化机制,可以抵御反射攻击,是实现Singleton的最佳方法。
第4条:通过私有构造器强化不可实例化的能力
对于某些仅提供静态工具方法的工具类来说,实例化是没有意义的,这时应该显式的提供私有构造器杜绝将其实例化的行为。甚至在私有构造器中抛出throw new AssertionError()异常,这个写法有点误导,所以需要加入注释说明。
这种用法也有副作用,及被修饰的类是无法被继承的(子类化)
第5条:避免创建不必要的对象
静态工厂优先于构造器,因为静态工厂可能会重用对象,而构造器总是构造新的对象
例如对于日期比较工具,获得Calendar的实例不要写在实例方法中,而应该写在静态代码区中,这样仅生成一次可以反复重用。这样的效率差距可能达上百倍或更多。
优先使用基本类型而不是包装类
第6条:消除过期的对象引用
如果用数组作为栈容器,push对象超过数组上限就生成一个新的更大的数组。先进栈再弹栈的元素将不会被回收(在数组没有被重新分配的情况下,数组还是保留着这些元素的引用),这被称为过期引用。
用手动置空的方式可以消灭过期引用,但不要过度使用,这回造成代码零乱的情况,不是一种好的代码风格。
消除过期引用最好的办法是让包含该引用的变量结束其生命周期。
只要类是自己管理内存,程序员就应该警惕内存泄漏问题。
另一个很可能发生内存泄漏的地方是缓存。可以用WeakHashMap代表缓存,当缓存中的项过期后,它们就会被自动删除。只有当所要的缓存项的生命周期由该键的外部引用而不是由值决定时,WeakHashMap才能用。
由于缓存的数据有价值与否是一个见仁见智的问题,可以将清楚工作交给一个后台线程(Timer/ScheduledThreadPoolExecutor)
第7条:避免使用finalize方法
finalize方法的缺点:
不能保证被及时执行。用finalize方法关闭已经打开的文件是严重错误的。
finalize方法的执行时间点在不同的JVM中表现大相径庭,所以使用它,对可移植性是有害的。
JAVA也不保证finalize方法一定会被执行
未被捕获的异常在finalize方法中抛出,会被忽略,且finalize方法会终止。
使用finalize方法有严重的性能问题
替代方式是,提供一个显式的终止方法,要求该类的客户端在每个实例不再使用时调用。比如InputStream的close方法。最好结合finally方法使用