场景
设计模式-单例模式-饿汉式单例模式、懒汉式单例模式、静态内部类在Java中的使用示例:
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/127555096
上面静态内部类单例模式示例
package com.ruoyi.demo.designPattern.reflectDesSingleton; /** * 兼顾饿汉式单例模式的内存浪费和synchnorized的性能问题,可以屏蔽这两个缺点 */ public class LazyInnerClassSingleton { //使用LazyInnerClassSingleton的时候,默认会先初始化内部类 //如果没使用,则内部类是不加载的 private LazyInnerClassSingleton(){} //static是为了使单例的空间共享,保证这个方法不会被重写、重载 public static final LazyInnerClassSingleton getInstance(){ //在返回结果以前,一定会加载内部类 return LazyHolder.LAZY; } //默认不加载 private static class LazyHolder{ private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton(); } }
注意:上面的构造方法除了加上private关键字,没有做任何处理。如果使用反射来调用其构造方法,再调用
getInstance()方法,会产生两个不同的实例。
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
关注公众号
霸道的程序猿
获取编程相关电子书、教程推送与免费下载。
实现
编写反射测试代码
package com.ruoyi.demo.designPattern.reflectDesSingleton; import java.lang.reflect.Constructor; public class LazyInnerClassSingletonTest { public static void main(String[] args) { try{ //进行破坏 //Class<?> clazz = LazyInnerClassSingleton.class; Class<?> clazz = BestLazyInnerClassSingleton.class; Constructor c = clazz.getDeclaredConstructor(null); //强制访问 c.setAccessible(true); //暴力初始化 Object o1 = c.newInstance(); //调用了两次构造方法,相当于"new"了两次,犯了原则性错误 Object o2 = c.newInstance(); System.out.println(o1 == o2); }catch (Exception e){ e.printStackTrace(); } } }
测试结果:
从测试结果来看,是创建了两个不同的实例,现在在其构造方法中做一些限制,一旦出现多次重复创建,则直接
抛出异常。
package com.ruoyi.demo.designPattern.reflectDesSingleton; /** * 兼顾饿汉式单例模式的内存浪费和synchnorized的性能问题,可以屏蔽这两个缺点 */ public class BestLazyInnerClassSingleton { //使用LazyInnerClassSingleton的时候,默认会先初始化内部类 //如果没使用,则内部类是不加载的 private BestLazyInnerClassSingleton(){ if(LazyHolder.LAZY!=null){ throw new RuntimeException("不允许创建多个实例"); } } //static是为了使单例的空间共享,保证这个方法不会被重写、重载 public static final BestLazyInnerClassSingleton getInstance(){ //在返回结果以前,一定会加载内部类 return LazyHolder.LAZY; } //默认不加载 private static class LazyHolder{ private static final BestLazyInnerClassSingleton LAZY = new BestLazyInnerClassSingleton(); } }
再次运行测试代码
序列化破坏单例
一个单例对象创建好后,有时候需要将对象序列化然后写入磁盘,下次再使用时再从磁盘中读取对象并进行反序列化,
将其转化为内存对象。反序列化后的对象会重新分配内存,即重新创建。
如果序列化的目标对象为单例对象,就违背了单例模式的初衷,相当于破坏了单例。
编写代码,这里使用饿汉式单例模式
package com.ruoyi.demo.designPattern.seriableDesSingleton; import java.io.Serializable; public class SeriableSingleton implements Serializable { //序列化就是将内存中的状态转换为字节码的形式 //从而转换一个I/O流,写入其他地方(磁盘、网络I/O) //内存中的状态会永久保存下来 //反序列化就是将已经持久化的字节码内容转换为I/O流 //通过I/O的转换,进而将读取的内容转换为Java对象 //在转换过程中会重新创建对象 public final static SeriableSingleton seriableSingleton = new SeriableSingleton(); private SeriableSingleton(){} public static SeriableSingleton getInstance(){ return seriableSingleton; } }
编写测试代码
package com.ruoyi.demo.designPattern.seriableDesSingleton; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class SeriableSingletonTest { public static void main(String[] args) { SeriableSingleton seriableSingleton1 = null; SeriableSingleton seriableSingleton2 = SeriableSingleton.getInstance(); FileOutputStream fileOutputStream = null; try{ fileOutputStream = new FileOutputStream("SeriableSingleton.obj"); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(seriableSingleton2); objectOutputStream.flush(); objectOutputStream.close(); FileInputStream fileInputStream = new FileInputStream("SeriableSingleton.obj"); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); seriableSingleton1 = (SeriableSingleton) objectInputStream.readObject(); objectInputStream.close(); System.out.println(seriableSingleton1); System.out.println(seriableSingleton2); System.out.println(seriableSingleton1 == seriableSingleton2); }catch (Exception e){ e.printStackTrace(); } } }
测试结果
从结果来看,手动创建的seriableSingleton2和序列化后再反序列化的seriableSingleton1不一致。
如何解决,只需要增加readResolve()方法即可。
package com.ruoyi.demo.designPattern.seriableDesSingleton; import java.io.Serializable; public class SeriableSingleton implements Serializable { //序列化就是将内存中的状态转换为字节码的形式 //从而转换一个I/O流,写入其他地方(磁盘、网络I/O) //内存中的状态会永久保存下来 //反序列化就是将已经持久化的字节码内容转换为I/O流 //通过I/O的转换,进而将读取的内容转换为Java对象 //在转换过程中会重新创建对象 public final static SeriableSingleton seriableSingleton = new SeriableSingleton(); private SeriableSingleton(){} public static SeriableSingleton getInstance(){ return seriableSingleton; } private Object readResolve(){ return seriableSingleton; } }
再次运行测试代码