1.考虑使用静态工厂方法代替构造器
静态工厂方法不同于设计模式中的静态工厂方法,含义是采用静态方法创建实例,静态方法可在本类中。
采用静态工厂方法的优点
①静态方法有名字,代码可读性强。
②静态方法可以创建单一实例对象。单例模式
③静态方法可以创建子类的实例对象,更加灵活。适用于基于接口的框架。例如Collection接口的静态 方法工具类Collections。服务提供者框架service-provider-register。
④静态方法创建泛型实例对象时,可以使代码变得更加简单。泛型的类型推导,例如集合。
采用静态工厂方法的缺点:
①类如果不含public或者protected的构造器,就不能被子类化。例如Collections中类就不能被子类化,但是这样鼓励了程序员使用组合,而不是继承。
②API中没有统一的标准规范,与其他静态方法实际没有什么区别。构造方法就形成统一标准:没有返回类型-与类名相同--创建对象。导致一个不知道有静态工厂方法存在的开发人员肯定还是会用构造方法,或者再写一个静态方法来创建对象。
2.遇到多个构造器参数时要考虑使用构建器
静态工厂与构造函数有个共同的局限性:不能很好的扩展到大量的可选参数。
①重叠构造器模式:一个全参数构造器与多个少参数构造器。
public Type(int x,int y,int z){
...
}
public Type(int x,int y){
this(x,y,0);
}
②JavaBeans实现getter与setter赋值,对象创建分到几个调用中,须额外确保线程安全问题。
③Builder模式:既能保证像重叠构造器模式那样安全性,也能保证像JavaBeans一样的可读性。
3.用私有构造器或者枚举类型强化单例模式
①饿汉式
②懒汉式
③加锁懒汉式
④内部静态类初始化保证单例
⑤枚举类(推崇)
4.通过私有构造器强化不可实例化的能力
编写只包含静态方法和静态域的类如Arrays,Collections,Math,这样的工具类不希望被实例化,实例对它完全没有意义。企图通过将类做成抽象类来强制该类不可被实例化,是不可行的,会给开发人员一股错觉,为继承而建立的类。
解决方法,私有化构造方法,并注释说明。
还可以在构造方法中抛错来防止内部方法调用私有方法建立对象。或者子类初始化。
5.避免创建不必要的对象
在创建不必要的对象时,最好能重用对象,而不是每次需要时创建一个新的对象。39条“当你应该创建对象的时候,请不要重用对象。”,重点在必要不必要,而不是重用。
①如果对象是不可变的,它始终可以被重用。
String str = new String("test");与String str = "test";
②除了重用不可变对象外,还可以重用那些已知不会被修改的可变对象
③有些情况就不够明显,会修改的可变对象,如Map的keySet应该重用。
④要优先使用基本类型而不是装箱基本类型,要当心无意识的自动装箱,会创建很多不必要的对象。如:
public Long sum = 0L;//由于Long 程序会创建10000个Long对象,改成long
for(long i = 0; i<10000L;i++){
sum += i;
}
6.消除过期的对象引用
①只要是类自己管理内存,程序员就应该警惕内存泄漏问题。
public Object pop(){
Object result = elements[--size];
//elements[size] == null;
return result;
}
②内存泄漏另一个常见来源时缓存,缓存时间,及旧数据清除。
③内存泄漏的第三个常见来源是监听器与其他回调。请求没有超时时间,API注册没有取消注册。
7.避免使用终结方法
①终结方法通常是不可预测的,也是很危险的,一般情况下是不必要的;
终结方法finalize是垃圾回收前执行的,执行优先级低,执行时间充满了不确定性。会导致finalize中资源清理滞留一段时间,影响性能。常用try、finally清理资源。
②不应该依赖终结方法来更新重要的持久状态;
由于终结方法不保证一定执行,所以可能会导致共享资源一直不释放,如数据库上的永久锁,会导致分布式系统宕机。
③使用终结方法有一个非常严重的性能损失,销毁对象慢,效率低。
④终结方法是垃圾回收算法的主要功能之一,不同JVM的垃圾回收算法会有差异,迁移导致出错。
⑤终结方法的合理用途:对象的本地对等体。
《Effective Java 第二版》