场景
设计模式-单例模式-饿汉式单例模式、懒汉式单例模式、静态内部类在Java中的使用示例:
设计模式-单例模式-饿汉式单例模式、懒汉式单例模式、静态内部类在Java中的使用示例_霸道流氓气质的博客-CSDN博客
上面静态内部类单例模式示例
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()方法,会产生两个不同的实例。
注:
博客:
霸道流氓气质的博客_CSDN博客-C#,架构之路,SpringBoot领域博主
关注公众号
霸道的程序猿
获取编程相关电子书、教程推送与免费下载。
实现
编写反射测试代码
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;
}
}
再次运行测试代码
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/135883.html