根据哈希值可以看出,反射破坏了单例的特性,因此懒汉式V3版诞生了
package singleton; public class LazySingleton3 { private static boolean initialized = false; private LazySingleton3() { synchronized (LazySingleton3.class) { if (initialized == false) { initialized = !initialized; } else { throw new RuntimeException("单例已被破坏"); } } } static class SingletonHolder { private static final LazySingleton3 instance = new LazySingleton3(); } public static LazySingleton3 getInstance() { return SingletonHolder.instance; } }此时再运行一次测试类,出现如下提示
java.lang.reflect.InvocationTargetException at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at test.LazySingleton3Test.main(LazySingleton3Test.java:21) Caused by: java.lang.RuntimeException: 单例已被破坏 at singleton.LazySingleton3.<init>(LazySingleton3.java:12) ... 5 more Instance 1 hash:359023572这里就保证了,反射无法破坏其单例特性
(3)懒汉式v4在分布式系统中,有些情况下你需要在单例类中实现 Serializable 接口。这样你可以在文件系统中存储它的状态并且在稍后的某一时间点取出。
让我们测试这个懒汉式V3版在序列化和反序列化之后是否仍然保持单例。
先将
public class LazySingleton3修改为
public class LazySingleton3 implements Serializable上测试类如下
package test; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInput; import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; import singleton.LazySingleton3; public class LazySingleton3Test { public static void main(String[] args) { try { LazySingleton3 instance1 = LazySingleton3.getInstance(); ObjectOutput out = null; out = new ObjectOutputStream(new FileOutputStream("filename.ser")); out.writeObject(instance1); out.close(); //deserialize from file to object ObjectInput in = new ObjectInputStream(new FileInputStream("filename.ser")); LazySingleton3 instance2 = (LazySingleton3) in.readObject(); in.close(); System.out.println("instance1 hashCode=" + instance1.hashCode()); System.out.println("instance2 hashCode=" + instance2.hashCode()); } catch (Exception e) { e.printStackTrace(); } } }输出如下
instance1 hashCode=2051450519 instance2 hashCode=1510067370显然,我们又看到了两个实例类。为了避免此问题,我们需要提供 readResolve() 方法的实现。readResolve()代替了从流中读取对象。这就确保了在序列化和反序列化的过程中没人可以创建新的实例。
因此,我们提供懒汉式V4版代码如下
package singleton; import java.io.Serializable; public class LazySingleton4 implements Serializable { private static boolean initialized = false; private LazySingleton4() { synchronized (LazySingleton4.class) { if (initialized == false) { initialized = !initialized; } else { throw new RuntimeException("单例已被破坏"); } } } static class SingletonHolder { private static final LazySingleton4 instance = new LazySingleton4(); } public static LazySingleton4 getInstance() { return SingletonHolder.instance; } private Object readResolve() { return getInstance(); } }此时,在运行测试类,输出如下
instance1 hashCode=2051450519 instance2 hashCode=2051450519这表示此时已能保证序列化和反序列化的对象是一致的
总结本文给出了多个版本的单例模式,供我们在项目中使用。实际上,我们在实际项目中一般从懒汉式v2、懒汉式v3、懒汉式v4中,根据实际情况三选一即可,并不是非要选择懒汉式v4作为单例来实现。
最后,希望大家有所收获。