饿汉式
public class Singleton1 {
/**
* 声明final static,保证全局可访问且不可变。<br>
* 声明transient,保证不会被序列化,当然也就不会反序列化生成对象了。
*/
private final static transient Singleton1 instance = new Singleton1();
private Singleton1() {
/**
* 防止通过反射生成对象
*/
if (instance != null) {
throw new RuntimeException("");
}
}
public static Singleton1 getInstance() {
return instance;
}
}
不管你用不用,只要类初始化了,对象就在那里。
懒汉式
public class Singleton2 {
/**
* volatile:保证对象生成后,其它线程立即可见,避免多余的加锁流程。<br>
* transient:保证不会被序列化,当然也就不会反序列化生成对象了。
*/
private static volatile transient Singleton2 instance = null;
private Singleton2() {
/**
* 防止通过反射生成对象
*/
if (instance != null) {
throw new RuntimeException("");
}
}
public static Singleton2 getInstance() {
//用到了双重检测和加锁,目的是最大化的提高程序的执行速度
if (instance == null) {// 检测对象是否已经生成,此时不加锁。
synchronized (Singleton2.class) {//加锁
if (instance == null)// 加锁情况下检测对象是否已经生成,防止线程在等待锁时其它线程已经生成了对象
instance = new Singleton2();
}
}
return instance;
}
}
只有在使用时才会生成对象,但会导致线程加锁,线程竞争严重时不建议使用该方式。
静态内部类
public class Singleton3 {
private static class SingletonHolder {
private final transient static Singleton3 instance = new Singleton3();
}
private Singleton3() {
/**
* 防止通过反射生成对象
*/
if (SingletonHolder.instance != null) {
throw new RuntimeException("");
}
}
public static Singleton3 getInstance() {
return SingletonHolder.instance;
}
}
同时具有懒汉和饿汉两个模式的优点:避免多线程竞争,而且只在使用时才实例化对象。
枚举式
public enum Singleton4 {
INSTANCE;
public void doSomething() {
System.out.println("aha");
}
}
public static void main(String[] args) {
Singleton4.INSTANCE.doSomething();
}
代码安全无竞争,写法简洁到想哭。
反思
我觉得没有必要关注太细的细节,比如防止反射,防止序列化;大部分情况下,我们在开发代码时都已经预知了单例的使用场景。
大神闫宏在设计模式一书中提到一种单例模式:构造函数是public的。这样我们可以在使用单例时调用getInstance方法,使用多例时直接new一个就好。因为这个类的使用场景是预先知道的。谁又能说这不是一个很好的单例模式?
不必在意茴香豆有几种写法,会写茴香豆几个字就好。