单例模式是一种创建型设计模式。单例模式的思想是:保证一个类只有一个实例,且这个实例由该类自行实例化并向外提供给用户。
饿汉式单例
类加载时,实例随之被创建。这种方式节省了运行时间,但可能浪费空间。
public class SingletonEager implements Serializable {
private static final long serialVersionUID = 3066869660783277053L;
// 类加载时就实例化
private static SingletonEager instance = new SingletonEager();
// 将构造器私有化,禁止外部使用关键字new来创建对象
private SingletonEager() {
// 防止反射漏洞
synchronized (SingletonEager.class) {
if (instance != null) throw new RuntimeException("WARNING: Reflection Attack!!");
}
}
// 向外部提供本类唯一的实例
public static SingletonEager getInstance() { return instance; }
// 防止序列化漏洞
private Object readResolve() { return instance; }
}
懒汉式单例
类加载时不实例化对象,第一次使用时才实例化。这种延迟加载的方式保证了不浪费内存空间,但第一次调用时较慢。使用这种方式也要注意线程安全的问题。下面的代码使用了双重检测来保证线程安全。
public class SingletonLazy implements Serializable {
private static final long serialVersionUID = 1929748534333081202L;
// 类加载时并未实例化,volatile关键字保证实例不被本地线程缓存,系统直接从内存中读取实例
private volatile static SingletonLazy instance;
// 将构造器私有化,禁止外部使用关键字new来创建对象
private SingletonLazy() {
// 防止反射漏洞
synchronized (SingletonLazy.class) {
if (instance != null) throw new RuntimeException("WARNING: Reflection Attack!!");
}
}
// 向外部提供本类唯一的实例
public static SingletonLazy getInstance() {
// 双重检测机制保证线程安全
if (instance == null) {
synchronized (SingletonLazy.class) {
if (instance == null) {
instance = new SingletonLazy();
}
}
}
return instance;
}
// 防止序列化漏洞
private Object readResolve() { return instance; }
}
静态内部类方式实现单例
静态内部类只有在被调用时才加载,实现了延迟加载,而且既然是静态的,也保证了唯一性。这种方式并没有使用synchronized关键字实现同步来保证线程安全,而只是执行一个域的访问,时间成本比较低。但这种方式无法防止反射攻击。
public class SingletonInnerClass implements Serializable {
private static final long serialVersionUID = 6915239702260968108L;
// 将构造器私有化,禁止外部使用关键字new来创建对象,但仍然可以反射机制调用此构造器来创建对象
private SingletonInnerClass() {}
// 将实例放在静态内部类中生成,保证了对象唯一性
private static class InstanceHelper {
private static SingletonInnerClass instance = new SingletonInnerClass();
}
// 向外部提供本类唯一的实例
public static SingletonInnerClass getInstance() {
return InstanceHelper.instance;
}
// 防止序列化漏洞
private Object readResolve() { return InstanceHelper.instance; }
}
枚举方式实现单例
使用枚举来实现单实例控制会更加简洁,而且无偿地提供了序列化机制,并由JVM从根本上提供保障,绝对防止多次实例化,是更简洁、高效、安全的实现单例的方式。强烈推荐这种方式。
public enum SingletonEnum {
// 定义一个元素,这个元素就代表了一个唯一的实例
INSTANCE;
// 单例内部的其它行为
public String toString() { return "Hello geek"; }
}