1、考虑用静态工厂方法代替构造器(静态工厂方法与设计模式中的工厂方法模式不同)
对于类而言,为了让客户端获取它自身的一个实例,最常用的方法是提供一个公有的构造器。还有一种方法,类可以提供一个公有的静态工厂方法,它只是一个返回类的实例的静态方法。
下面是一个来自Boolean的示例:
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
优点:
a、它有名称
b、不必在每次调用它们的时候都创建一个新的对象
c、它可以返回原返回类型的任何子类型的对象
d、在创建参数化类型实例的时候,它们使代码变的更加简洁
缺点:
a、类如果不含公有的或者受保护的构造器,就不能被子类化
b、它们与其他的静态方法实际上没有任何区别
2、遇到多个构造器参数时要考虑用构建器
(考虑用一个类表示包装食品外面显示的营养成分标签。这些标签中有几个域是必须的:每份的含量、每罐的含量以及每份的卡路里,还有超过20个可选的:总脂肪量、饱和脂肪量、转化脂肪、胆固醇、钠等等)
静态工厂和构造器有个共同的局限性:它们都不能很好地扩展到大量的可选参数。
方法一、重叠构造器模式
第一个构造方法提供一个参数,第二个构造器提供二个参数,以此类推。
重叠构造器模式可行,但是当有许多参数的时候,客户端代码会很难编写,并且仍然较难以阅读。
方法二、JavaBean模式
调用一个无参构造器来创建对象,然后调用setter方法来设置参数的值
缺点:构造过程被分到了几个调用中,在构造过程中JavaBean可能处于不一致的状态。JavaBean模式阻止了把类做成不可变的可能,这就需要程序员付出额外的努力来确保它的线程安全。
方法三、Builder模式
不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器,得到一个builder对象。然后客户端在build对象上调用类似setter的方法,来设置每个相关的可选参数。最后,客户端调用无参的build方法来生成不可变的对象。这个build是它构建的类的静态成员类。
public class EJ {
// 必须属性
private final int servingSize;
private final int servings;
// 可选属性
private final int fat;
private final int sodium;
public static class Builder {
private final int servingSize;
private final int servings;
private int fat = 0;
private int sodium = 0;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder fat(int val) {
fat = val;
return this;
}
public Builder sodium(int val) {
sodium = val;
return this;
}
public EJ build() {
return new EJ(this);
}
}
public EJ(Builder builder) {
this.servingSize = builder.servingSize;
this.servings = builder.servings;
this.fat = builder.fat;
this.sodium = builder.sodium;
}
public static void main(String[] args) {
EJ ej = new EJ.Builder(1, 90).fat(10).sodium(12).build();
}
}
3、用私有构造器或者枚举类型强化Singleton属性
JAVA 1.5之前,实现Singleton有两种方法。这两种方法都是把构造器保持为私有的,并导出共有的静态成员,以便允许客户端能够访问该类的唯一实例。
方法一、共有静态成员是个final域
public class EJ {
public static final EJ INSTANCE = new EJ();
private EJ() {
}
}
(享有特殊权限的客户端可以借助AccessibleObject.setAccessible方法,通过反射机制调用私有构造器。如果要抵御这种攻击,可以修改构造器,让它在被要求创建第二个实例的时候抛出异常)
方法二、共有的成员是个静态工厂方法:
public class EJ {
private static final EJ INSTANCE = new EJ();
private EJ() {
}
public static EJ getInstance() {
return INSTANCE;
}
}
JAVA 1.5后
方法三、包含单个元素的枚举类型
public enum EJ {
INSTANCE;
}
4、通过私有构造器强化不可实例化的能力
有时候你可能需要编写只包含静态方法和静态域的类。这样的工具类不希望被实例化,实例对它没有任何意义。
public class EJ {
private EJ() {
throw new AssertionError();
}
}
5、避免创建不必要的对象
最好重用对象而不是在每次需要的时候就创建一个相同功能的新对象。如果对象是不可变的,它就始终可以被重用。
6、消除过期的对象引用
过期对象的引用会导致内存泄露
a、只要类是自己管理内存,程序员就应该警惕内存泄露问题。一旦元素被释放掉,则该元素中包含的任何对象引用都应该被清空。
b、内存泄露的另一个常见来源是缓存。可以用WeakHashMap代表缓存。
c、内存泄露的第三个常见来源是监听器和其他回调。(如果你实现了一个API,客户端在这个API中注册回调,却没有显示地取消注册,那么除非你采取某些动作,否则它们会积聚。可以将它们保存为WeakHashMap中的键)
7、避免使用终结方法
缺点:
a、终结方法的缺点在于不能保证会被及时地执行,甚至不不保证会被执行。
b、使用终结方法有一个非常严重的性能损失。
优点:
a、当对象的所有者忘记调用前面段落中建议的显示方法时,终结方法可以充当“安全网”。虽然这样并不能保证终结方法会被及时的调用,但是在客户端无法通过显示的终止方法来正常结束操作的情况下,迟一点释放关键资源总比永远不释放要好。但是终结方法如果发现资源没有被释放应该在日志中记录一条警告,这表示客户端代码中的一个Bug。
b、第二种用途与对象的本地对等体有关。本地对等体是一个本地对象,普通对象通过本地方法委托给一个本地对象。因为本地对等体不是一个普通对象,所以垃圾回收器不会知道它,当它的java对等体被回收的时候,它不会被回收。
注意:“终结方法链”并不会被自动执行。如果类有终结方法,并且子类覆盖了终结方法,子类的终结方法必须手工调用超类的终结方法。你应该在一个try块中终结子类,finally调用父类的终结方法。也可以使用“终结方法守卫者”:
public class Foo {
// 终结方法守卫者
private final Object finalizerBuardian = new Object(){
@Override
protected void finalize() throws Throwable {
// Finalize outer Foo object
}
};
}
把原先在Foo的finalize方法中的操作放到内部类的finalize中