设计模式之单例模式

懒汉式(支持多线程)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class SingletonLazyLoad {
private volatile static SingletonLazyLoad singleton;
private SingletonLazyLoad() {
}
public static SingletonLazyLoad getInstance() {
if (singleton == null) {
synchronized(SingletonLazyLoad.class) {
if (singleton == null) {
singleton=new SingletonLazyLoad();
}
}
}
return singleton;
}
}

饿汉式(支持多线程)

1
2
3
4
5
6
7
8
public class Singleton {
private static final Singleton instance=new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return instance;
}
}

静态内部类

1
2
3
4
5
6
7
8
9
public class Singleton {  
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton () {}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}

枚举

1
2
3
4
5
public enum Singleton {  
INSTANCE;
public void whateverMethod() {
}
}

总结

在所有的单例实现方式中,枚举是一种在代码写法上最简单的方式,之所以代码十分简洁,是因为Java给我们提供了enum关键字,我们便可以很方便的声明一个枚举类型,而不需要关心其初始化过程中的线程安全问题,因为枚举类在被虚拟机加载的时候会保证线程安全的被初始化。

使用非枚举的方式实现单例,都要自己来保证线程安全,所以,这就导致其他方法必然是比较臃肿的。

除此之外,在序列化方面,Java中有明确规定,枚举的序列化和反序列化是有特殊定制的。这就可以避免反序列化过程中由于反射而导致的单例被破坏问题。

普通的Java类的反序列化过程中,会通过反射调用类的默认构造函数来初始化对象。所以,即使单例中构造函数是私有的,也会被反射给破坏掉。由于反序列化后的对象是重新new出来的,所以这就破坏了单例。

但是,枚举的反序列化并不是通过反射实现的。所以,也就不会发生由于反序列化导致的单例破坏问题。

看官可在此打赏