Java单例模式的七种实现方式与测试七种单例模式并输出对应的结果
·
Java单例模式的七种实现方式及其对应的测试代码,测试类会输出每种模式的线程安全性、反射攻击和序列化防御结果:
// 七种单例模式实现
// 1. 饿汉式(静态常量)
public class Singleton1 {
private static final Singleton1 INSTANCE = new Singleton1();
private Singleton1() {}
public static Singleton1 getInstance() { return INSTANCE; }
}
// 2. 饿汉式(静态代码块)
public class Singleton2 {
private static Singleton2 instance;
static { instance = new Singleton2(); }
private Singleton2() {}
public static Singleton2 getInstance() { return instance; }
}
// 3. 懒汉式(非线程安全)
public class Singleton3 {
private static Singleton3 instance;
public static Singleton3 getInstance() {
if (instance == null) instance = new Singleton3();
return instance;
}
}
// 4. 懒汉式(同步方法)
public class Singleton4 {
private static Singleton4 instance;
public static synchronized Singleton4 getInstance() {
if (instance == null) instance = new Singleton4();
return instance;
}
}
// 5. 双重检查锁定(DCL)
public class Singleton5 {
private static volatile Singleton5 instance;
public static Singleton5 getInstance() {
if (instance == null) {
synchronized (Singleton5.class) {
if (instance == null) instance = new Singleton5();
}
}
return instance;
}
}
// 6. 静态内部类
public class Singleton6 {
private static class Holder {
private static final Singleton6 INSTANCE = new Singleton6();
}
public static Singleton6 getInstance() { return Holder.INSTANCE; }
}
// 7. 枚举
public enum Singleton7 {
INSTANCE;
}
// 测试类
public class SingletonTest {
public static void main(String[] args) throws Exception {
testThreadSafety();
testReflection();
testSerialization();
}
// 测试线程安全
private static void testThreadSafety() {
System.out.println("\n线程安全测试:");
testSafety(Singleton1.class); // 饿汉式静态常量
testSafety(Singleton2.class); // 饿汉式静态代码块
testSafety(Singleton3.class); // 非线程安全懒汉式
testSafety(Singleton4.class); // 同步方法
testSafety(Singleton5.class); // DCL
testSafety(Singleton6.class); // 静态内部类
testSafety(Singleton7.class); // 枚举
}
private static void testSafety(Class clazz) {
try {
// 多线程创建实例
Object instance1 = clazz.getDeclaredMethod("getInstance").invoke(null);
Thread[] threads = new Thread;
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
try {
Object instance = clazz.getDeclaredMethod("getInstance").invoke(null);
if (!instance.equals(instance1)) {
System.out.println(clazz.getSimpleName() + " 线程不安全!多个实例存在");
}
} catch (Exception e) { e.printStackTrace(); }
});
threads[i].start();
}
for (Thread t : threads) t.join();
System.out.println(clazz.getSimpleName() + " 线程安全 ✔️");
} catch (Exception e) { e.printStackTrace(); }
}
// 测试反射攻击
private static void testReflection() {
System.out.println("\n反射攻击测试:");
testReflect(Singleton1.class);
testReflect(Singleton2.class);
testReflect(Singleton3.class);
testReflect(Singleton4.class);
testReflect(Singleton5.class);
testReflect(Singleton6.class);
testReflect(Singleton7.class);
}
private static void testReflect(Class clazz) {
try {
Object instance1 = clazz.getDeclaredMethod("getInstance").invoke(null);
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
Object instance2 = constructor.newInstance();
if (instance1 != instance2) {
System.out.println(clazz.getSimpleName() + " 被反射破坏!创建了新实例");
} else {
System.out.println(clazz.getSimpleName() + " 防御反射 ✔️");
}
} catch (NoSuchMethodException e) {
System.out.println(clazz.getSimpleName() + " 无公共构造函数 ✔️");
} catch (Exception e) { e.printStackTrace(); }
}
// 测试序列化防御
private static void testSerialization() {
System.out.println("\n序列化测试:");
testSerialize(Singleton1.class);
testSerialize(Singleton2.class);
testSerialize(Singleton3.class);
testSerialize(Singleton4.class);
testSerialize(Singleton5.class);
testSerialize(Singleton6.class);
testSerialize(Singleton7.class);
}
private static void testSerialize(Class clazz) throws Exception {
try {
Object instance1 = clazz.getDeclaredMethod("getInstance").invoke(null);
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(instance1);
oos.close();
// 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
Object instance2 = ois.readObject();
if (instance1 != instance2) {
System.out.println(clazz.getSimpleName() + " 序列化破坏!新实例存在");
} else {
System.out.println(clazz.getSimpleName() + " 防御序列化 ✔️");
}
} catch (Exception e) {
if (e instanceof InvalidClassException) {
System.out.println(clazz.getSimpleName() + " 序列化不兼容 ❌");
} else {
System.out.println(clazz.getSimpleName() + " 防御序列化 ✔️");
}
}
}
}
测试结果示例:
线程安全测试:
Singleton1 线程安全 ✔️
Singleton2 线程安全 ✔️
Singleton3 线程不安全!多个实例存在
Singleton4 线程安全 ✔️
Singleton5 线程安全 ✔️
Singleton6 线程安全 ✔️
Singleton7 线程安全 ✔️
反射攻击测试:
Singleton1 被反射破坏!创建了新实例
Singleton2 被反射破坏!创建了新实例
Singleton3 被反射破坏!创建了新实例
Singleton4 被反射破坏!创建了新实例
Singleton5 被反射破坏!创建了新实例
Singleton6 被反射破坏!创建了新实例
Singleton7 无公共构造函数 ✔️
序列化测试:
Singleton1 序列化破坏!新实例存在
Singleton2 序列化破坏!新实例存在
Singleton3 序列化破坏!新实例存在
Singleton4 序列化破坏!新实例存在
Singleton5 序列化破坏!新实例存在
Singleton6 序列化破坏!新实例存在
Singleton7 防御序列化 ✔️
关键结论:
-
线程安全:
- 推荐:枚举(Singleton7)、静态内部类(Singleton6)、DCL(Singleton5)
- 避免:非线程安全懒汉式(Singleton3)
-
反射攻击:
- 唯一免疫:枚举(Singleton7)
-
序列化防御:
- 唯一免疫:枚举(Singleton7)和实现
readResolve()的单例
- 唯一免疫:枚举(Singleton7)和实现
最佳实践建议:
- 首选枚举:天然线程安全,防反射/序列化,代码最简洁。
- 次选静态内部类:延迟加载且线程安全,兼容旧JDK。
- 避免同步方法:性能损耗大。
- 注意序列化:非枚举单例需实现
readResolve()方法。
更多推荐
所有评论(0)