用私有构造器或者枚举类型强化Singleton属性
what?
Singleton
指就被实例化一次的类,也就是我们常听到单例模式。Singleton通常被用来代表一个无状态的对象
do?
实现Singleton有两种常见的方法。这两种方法都要保持构造器为私有的,并导出公有的静态成员,以便允许客户端能够访问该类的唯一实例。
one
使用公有的final域静态成员
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() { }
public void leaveTheBuilding() {
System.out.println("鸡你太美");
}
public static void main(String[] args) {
Elvis elvis = Elvis.INSTANCE;
elvis.leaveTheBuilding();
}
}
仅有的构造器是私有的,客户端不能通过构造器创建Elvis,这样子私有构造器仅被调用一次,就是程序加载Elvis类是实例化公有的静态的final域的INSTANCE的时候被调用。这样子保证了E1vis的全局唯-性:一旦E1vis类被实例化,将只会存在一个EIvis实例。
但是要提醒一点:享有特权的客户端可以借助Accessibleobjeet.setAecessible方法通过反射机制调用私有构造器。如果需要防止这种情况,我们可以修改构造器让它在被要求创建第二个实例的时候抛出异常。
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() {
throw new AssertionError("小黑子,哥哥下蛋给你吃");
}
public void leaveTheBuilding() {
System.out.println("鸡你太美");
}
public static void main(String[] args) {
Elvis elvis = Elvis.INSTANCE;
elvis.leaveTheBuilding();
}
}
two
提供一个公有方法:静态工厂方法
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() { }
public static Elvis getInstance() { return INSTANCE; }
public void leaveTheBuilding() {
System.out.println("有时间学点唱、跳、rap");
}
public static void main(String[] args) {
Elvis elvis = Elvis.getInstance();
elvis.leaveTheBuilding();
}
}
使用公有方法可以更加灵活,我们可以再自定义一些操作
other
上面的两种方式,如果我们要实现可序列化,单纯 implements Serializable 是不够,我们每次反序列化后都是获得一个新实例
为了维护并保证Singleton,我们在Elvis中添加readResolve方法,当我们通过反序列化readObject()方法获取对象时会去寻找readResolve()方法,如果该方法不存在则直接返回新对象,如果该方法存在则按该方法的内容返回对像。这个的细节以后再来填。
private Object readResolve(){
return INSTANCE;
}
当然还有个更好的模式,就是我们学习单例模式常看到的枚举
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() {
System.out.println("只因鸡你太美");
}
public static void main(String[] args) {
Elvis elvis = Elvis.INSTANCE;
elvis.leaveTheBuilding();
}
}
枚举类型编写起来会更加简洁,同时也无偿提供序列化机制,绝对防止多次实例化不同问题,也可以防止反射攻击。
如果Singleton不必须扩展一个超类,单元素的枚举类型往往是实现Singleton的最佳方法。
end
单例模式当然还有更加优化的方案,这里就立足于书本来讨论记录。序列化的细节以后来填。