简介
单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供了一个全局访问点来访问该实例。
一、单例模式实现方式
1、懒汉式
public class LazySingleton {
// 因为new的过程会进行指令重排,需要使用保证可见性,所以使用volatile
private static volatile LazySingleton lazySingleton = null;
public static LazySingleton getLazySingleton(){
// volatile 保证此处的可见性
if (lazySingleton != null){
return lazySingleton;
}
synchronized (LazySingleton.class){
if (lazySingleton == null){
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
}
2、饿汉式
public class HungrySingleton {
private static volatile HungrySingleton hungrySingleton = new HungrySingleton();
private static HungrySingleton getHungrySingleton(){
return hungrySingleton;
}
}
因为饿汉式的类加载时候就会初始化,为了防止该单例不适用而导致资源浪费,可以使用静态内部类进行优化
public class LazySingleton {
// 防止被重写
// 调用getHungrySingleton()方法,则会先加载静态内部类(类加载机制)
private static final LazySingleton getHungrySingleton() {
return UpgradeHungrySingleton.hungrySingleton;
}
// 类加载,不加载内部类
private static final class UpgradeHungrySingleton {
private static final LazySingleton hungrySingleton = new LazySingleton();
}
}
3、枚举
使用枚举类实现单例模式是最简洁和安全的方式,因为枚举类型本身是线程安全的,并且只会实例化一次,并且无法被破坏
public enum Singleton {
INSTANCE;
// 可以添加其他方法
public void someMethod() {
// 方法实现
}
}
二、单例模式的失效场景
1、反射破坏单例
反射可以绕过构造方法的私有访问控制,从而再次创建实例。
import java.lang.reflect.Constructor;
public class SingletonReflection {
public static void main(String[] args) {
try {
Singleton instance1 = Singleton.getInstance();
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton instance2 = constructor.newInstance();
// 输出 false,说明创建了两个实例
System.out.println(instance1 == instance2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
// 私有构造方法防止实例化
}
public static Singleton getInstance() {
return INSTANCE;
}
}
2、序列化破坏单例
序列化和反序列化可能导致创建多个实例,即使类是单例的
import java.io.*;
public class SingletonSerialization {
public static void main(String[] args) throws Exception {
Singleton instance1 = Singleton.getInstance();
// 序列化
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("singleton.ser"));
out.writeObject(instance1);
out.close();
// 反序列化
ObjectInputStream in = new ObjectInputStream(new FileInputStream("singleton.ser"));
Singleton instance2 = (Singleton) in.readObject();
in.close();
// 输出 false,说明创建了两个实例
System.out.println(instance1 == instance2);
}
}
class Singleton implements Serializable {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
// 私有构造方法防止实例化
}
public static Singleton getInstance() {
return INSTANCE;
}
}
防止序列化破坏
调整单例类,添加 readResolve 方法,可以避免上述问题
class Singleton implements Serializable {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
// 私有构造方法防止实例化
}
public static Singleton getInstance() {
return INSTANCE;
}
// 防止反序列化时创建新的实例
protected Object readResolve() {
return INSTANCE;
}
}
3、多类加载器破坏单例
如果应用程序使用了多个类加载器加载同一个类,不同类加载器可能会产生不同的类实例。
ClassLoader classLoader1 = new CustomClassLoader();
ClassLoader classLoader2 = new CustomClassLoader();
Class<?> singletonClass1 = classLoader1.loadClass("Singleton");
Class<?> singletonClass2 = classLoader2.loadClass("Singleton");
Object instance1 = singletonClass1.getMethod("getInstance").invoke(null);
Object instance2 = singletonClass2.getMethod("getInstance").invoke(null);
// 输出 false,说明创建了两个实例
System.out.println(instance1 == instance2);
4、克隆破坏单例
如果 Singleton 类实现了 Cloneable 接口并重写 clone() 方法
class Singleton implements Cloneable {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
// 私有构造方法防止实例化
}
public static Singleton getInstance() {
return INSTANCE;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// 可能会生成新的实例
return super.clone();
}
}
public class SingletonClone {
public static void main(String[] args) throws Exception {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = (Singleton) instance1.clone();
// 输出 false,说明创建了两个实例
System.out.println(instance1 == instance2);
}
}
防止克隆破坏
重写 clone() 方法,直接返回单例实例
class Singleton implements Cloneable {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
// 私有构造方法防止实例化
}
public static Singleton getInstance() {
return INSTANCE;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// 防止克隆创建新实例
return INSTANCE;
}
}
小知识
枚举在 JVM 层面确实提供了很强的保证,使其成为实现单例模式时几乎不会被破坏的选择