当前位置: 首页 > news >正文

在序列化、反序列化下如何保持单例(Singleton)模式

1、序列化、反序列化

在 Java 中,当一个对象被序列化后再被反序列化,通常情况下会创建一个新的对象实例。这是因为序列化将对象的状态保存到字节流中,而反序列化则是将字节流重新转化为对象。在这个过程中,通常会使用类的构造函数创建一个新的对象,并将保存的状态设置给这个新对象。

这意味着,默认情况下,在序列化和反序列化过程中,会产生新的对象实例,而不是保持原有的对象实例。这可能会导致一些问题,特别是在设计为单例(Singleton)的类或者一些需要保持引用相等性的场景下。

2、DoubleChecked单例

public class DoubleCheckedSingleton implements Serializable{private volatile static DoubleCheckedSingleton instance;private static final long serialVersionUID = 1L;private DoubleCheckedSingleton() {// 私有构造方法}public static DoubleCheckedSingleton getInstance() {if (instance == null) {synchronized (DoubleCheckedSingleton.class) {if (instance == null) {instance = new DoubleCheckedSingleton();}}}return instance;}
}

示例中,通过使用 volatile 关键字修饰 instance,确保了线程间的可见性。在 getInstance() 方法中,首先检查 instance 是否为 null,如果为 null,才会进入同步代码块。在同步代码块内,再次检查 instance 是否为 null,这是为了防止其他线程已经在等待同步锁的情况下创建了实例。如果没有其他线程已经创建了实例,就在同步代码块内创建实例。这种方式可以减少同步的次数,提高性能。

需要注意的是,虽然现代的Java版本中双重检查锁定通常是线程安全的,但在某些特殊情况下仍可能出现问题,如序列化、反射等情况。

解决上述代码的这个问题,可以在类中实现 readResolve() 方法,确保在反序列化时返回同一个对象实例,从而维护对象的单例特性。

public class DoubleCheckedSingleton implements Serializable{private volatile static DoubleCheckedSingleton instance;private static final long serialVersionUID = 1L;private DoubleCheckedSingleton() {// 私有构造方法}public static DoubleCheckedSingleton getInstance() {if (instance == null) {synchronized (DoubleCheckedSingleton.class) {if (instance == null) {instance = new DoubleCheckedSingleton();}}}return instance;}
// 重写 readResolve 方法,确保反序列化时返回同一个单例对象protected Object readResolve() {return instance;}
}

这个示例中,我添加了实现 Serializable 接口的代码,并重写了 readResolve() 方法,以确保在反序列化时返回同一个单例对象。这样,即使在序列化和反序列化过程中,也能保持单例的一致性。

3、静态内部类和枚举单例

如果你更关注简单性和可靠性,也可以考虑使用静态内部类或枚举单例来实现线程安全的单例模式。

因为这两种方法都充分利用了Java语言的特性来保证线程安全性,同时也能有效地处理序列化、反射等问题

静态内部类单例

public class StaticInnerClassSingleton {private StaticInnerClassSingleton() {// 私有构造方法}private static class SingletonHolder {private static final StaticInnerClassSingleton instance = new StaticInnerClassSingleton();}public static StaticInnerClassSingleton getInstance() {return SingletonHolder.instance;}
}

序列化

import java.io.Serializable;public class StaticInnerClassSingleton implements Serializable {private StaticInnerClassSingleton() {// 私有构造方法}private static class SingletonHolder {private static final StaticInnerClassSingleton instance = new StaticInnerClassSingleton();}public static StaticInnerClassSingleton getInstance() {return SingletonHolder.instance;}// 添加此方法以支持序列化protected Object readResolve() {return getInstance();}
}

这个实现中,静态内部类 SingletonHolder 仅在需要时才会被加载,确保了懒加载的特性。

同时,由于类加载器的机制,这种方式可以保证线程安全。

静态内部类只会被加载一次,因此在多线程环境中也能够确保单例实例的唯一性。

枚举单例

public enum EnumSingleton {INSTANCE;// 添加需要的方法和属性public void doSomething() {// 实现方法}
}

序列化

import java.io.Serializable;public enum EnumSingleton implements Serializable {INSTANCE;// 添加需要的方法和属性public void doSomething() {// 实现方法}
}

使用枚举单例可以保证在任何情况下都只有一个实例被创建,包括在多线程环境下以及在序列化、反射等特殊情况下。

枚举类的实例创建是线程安全的,而且枚举类不会被反射破坏,并且可以处理序列化和反序列化,保证了单例模式的可靠性。

http://www.lryc.cn/news/125069.html

相关文章:

  • 【数据结构】二叉树篇|超清晰图解和详解:二叉树的最近公共祖先
  • android ndk clang交叉编译ffmpeg动态库踩坑
  • 简单记录牛客top101算法题(初级题C语言实现)BM24 二叉树的中序遍历 BM28 二叉树的最大深度 BM29 二叉树中和为某一值的路径
  • 前后端分离------后端创建笔记(05)用户列表查询接口(上)
  • 性能测试|App性能测试需要关注的指标
  • Termux SFTP 进行远程文件传输
  • Sqlite3简介
  • K8S调度
  • vue+element多层表单校验prop和rules
  • Dubbo 核心概念和架构
  • 【数据结构OJ题】反转链表
  • Java8 Stream 之groupingBy 分组讲解
  • 优哲SSD大文件写性能测试
  • Python基础教程: json序列化详细用法介绍
  • 一张图看懂 USDT三种类型地址 Omni、ERC20、TRC20的区别
  • SegFormer之模型训练
  • Azure资源命名和标记决策指南
  • 【在一个升序数组中插入一个数仍升序输出】
  • 图像去雨、去雪、去雾论文学习记录
  • YARN框架和其工作原理流程介绍
  • 多维时序 | MATLAB实现ZOA-CNN-BiGRU-Attention多变量时间序列预测
  • centos上下载redis
  • 黑马项目一阶段面试58题 Web14题(二)
  • 软考高项-思维导图34-36(计算机高级系统项目管理师)
  • C++的stack和queue+优先队列
  • Ubuntu 18.04.6 Android Studio Giraffe adb logcat 无法使用
  • Python采集天气数据,做可视化分析【附源码】
  • 优维低代码实践:自定义模板
  • 电商3D产品渲染简明教程
  • 探索未来:元宇宙与Web3的无限可能