单例模式
java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍五种:饿汉式单例、懒汉式单例、双重锁检查式单例、静态内部类单例、枚举单例。
无论如何使用哪种写法,其最根本的目的在于创造一个类的单一实例,其次是性能与代码的简洁,所以饿汉式与枚举单例是我个人认为最好用的写法。
饿汉式
public class Singleton {
private Singleton() {}
private static final Singleton singleton = new Singleton();
public static Singleton getInstance() {
return singleton;
}
}
因其在类初始化的时候就生成实例,所以称其为饿汉式,且jvm保证了其线程安全。有的文章占认为这种写法用资源;如果后期这个单例没有被使用,会造成资源浪费,我认为这点缺点与其优点相比不值一提。
懒汉式
public class Singleton {
private Singleton() {}
private static Singleton singleton = null;
public static Singleton getInstance() {
if (singleton== null) {
singleton= new Singleton();
}
return singleton;
}
}
懒汉式顾名思义,在出现调用getInstance方法的时候才会实例化对象,所以他的优缺点正好与饿汉式相反,线程不安全但不会造成资源浪费。也正如我上文所说,我认为单例模式的根本目的创造一个类的单一实例,所以线程不安全是不能接受的,不推荐使用懒汉式单例。
双重锁检查式
public class Singleton {
private Singleton() {}
private static Singleton singleton = null;
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton3();
}
}
}
return singleton;
}
}
双重锁的特点是线程安全,且确保了只有第一次调用单例时才会做同步,且避免了锁加在整个方法上导致的每次都同步的性能损耗,但仍降低了程序响应速度和性能。
静态内部类式
public class Singleton {
private Singleton() {}
private static class LazyHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static final Singleton getInstance() {
return LazyHolder.INSTANCE;
}
}
静态内部类利用了jvm类加载机制保证了线程安全,且外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化INSTANCE ,所以静态内部类既节省了内存开销又保证了线程安全,但它的一个致命弱点是无法传参。
枚举单例
public enum Singleton {
INSTANCE;
public void getInstance() {
}
}
枚举类是jdk1.5引入的,它最大的特点是没有构造方法。前面的四种单例模式写法,即使都将构造器私有化,但这在反射攻击和反序列化面前都不起作用,仍通过获取构造器来创建多个实例,而枚举类则不存在这个问题,且枚举类本身为单例模式,所以单元素的枚举类型已经成为实现Singleton的最佳方法。