一、 反序列化对Java单例的影响
解决方案:让Singleton继承Serializable, 并实现 private Object readResolve();
public class Singleton implement Serializable {
public static class Builder {
public static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return Builder.instance;
}
private Object readResolve(){
retutn Builder.instance;
}
}
原理解析
在反序列化时,ObjectInputStream 因为利用反射机制调用了 readObject --> readObject0 --> readOrdinary --> CheckResolve。在readOrdinady中调用了invokeReadResolve(),该方法使用反射机制创建新的对象,从而破坏了单例唯一性。 ###尊重版权
二、 java单例模式的实现方式
#####1. 饿汉式 public class Singleton {
private static Singleton = new Singleton();
private Singleton() {}
public static getSignleton(){
return singleton;
}
}
#####2. 懒汉式 public class Singleton { //注意volatiile的用法: 1. 保证在当前线程修改临界值之后在随后线程中起作用(可见性),及线程透明 volatile的第二层语义是禁止指令重排序优化。 private static volatile Singleton singleton = null;
private Singleton(){}
public static Singleton getSingleton(){
if(singleton == null){}
synchronized (Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
#####3. 静态内部类 public class Singleton { //因为静态内部类只会加载一次,所以保证了线程安全 private static class Holder { private static Singleton singleton = new Singleton(); }
private Singleton(){}
public static Singleton getSingleton(){
return Holder.singleton;
}
}
#####4. 枚举方式
public enum Singleton {
INSTANCE;
private String name;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
}
使用枚举除了线程安全和防止反射强行调用构造器之外,还提供了自动序列化机制,防止反序列化的时候创建新的对象。因此,Effective Java推荐尽可能地使用枚举来实现单例。