浅谈Java序列化

基本介绍

Java序列化机制允许将一个Java对象的状态转换为字节流,以便可以将其保存到磁盘或在网络上进行传输。稍后,这些字节流可以被反序列化以重建原来的对象。这个机制在远程方法调用(RMI)、Java Beans,以及持久化等多种情景中非常有用。

基本原理

  1. 序列化(Serialization):是将对象的状态信息转换为可以存储或传输的形式的过程。在Java中,序列化的对象必须实现 java.io.Serializable 接口。这个接口是一个标记接口,它不包含任何方法。

  2. 反序列化(Deserialization):是将已存储的数据(如文件,数据库)或通过网络接收的字节流还原为对象的过程。

实现序列化

要让一个Java对象可序列化,需要实现 Serializable 接口。例如:

public class User implements Serializable {private String name;private transient int age; // 使用 transient 关键字标记的字段不会被序列化// 构造器、getter、setter等
}

在上面的例子中,User 类通过实现 Serializable 接口变成可序列化的。transient 关键字用于防止字段被序列化。

序列化的过程

序列化是通过 ObjectOutputStream 类实现的。例如:

User user = new User("John", 30);
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("user.ser"))) {out.writeObject(user);
}

反序列化的过程

反序列化是通过 ObjectInputStream 类实现的。例如:

User user;
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("user.ser"))) {user = (User) in.readObject();
}

特殊处理

  • transient 关键字:如果不想某个字段被序列化,可以使用 transient 关键字标记它。
  • 版本控制:通过 serialVersionUID 字段提供序列化版本控制。如果序列化对象的版本与反序列化时的版本不匹配,将抛出 InvalidClassException
  • 自定义序列化:可以通过重写 writeObjectreadObject 方法来自定义序列化过程。
  • 使用 readResolvewriteReplace 方法:这些方法允许在序列化和反序列化过程中进行特殊的操作,比如维护单例模式。

序列化的用途

  • 持久化:将对象的状态保存到存储媒介(如硬盘)。
  • 深复制:通过序列化和反序列化创建对象的深拷贝。
  • 通信:在网络中传输对象的状态,例如在客户端和服务器之间。

注意事项

  • 并非所有的对象都可以序列化。对象的所有属性以及属性的属性都必须是可序列化的。
  • 静态字段不会被序列化,因为它们属于类的状态,而不是对象的状态。
  • 序列化可能引发性能问题和安全问题,因此需要谨慎使用。

Java序列化机制是一种强大的工具,但同时也需要理解它的工作原理和适用场景,以便正确和高效地使用。

readResolve 和 writeReplace 方法

在Java序列化机制中,readResolvewriteReplace 方法用于自定义对象的序列化和反序列化过程。这些方法在java.io.Serializable接口中并不是必需的,但如果类中定义了它们,它们会在序列化和反序列化过程中自动被调用。

writeReplace 方法

writeReplace 方法用于序列化过程。当一个对象被序列化时,如果该对象的类定义了 writeReplace 方法,这个方法会被调用,并且它返回的对象会代替原始对象被序列化。

使用场景
  • 替换序列化对象:在序列化之前,可能需要替换为另一个对象。例如,当序列化代理模式(Serialization Proxy Pattern)被使用时,可以通过 writeReplace 方法返回一个代理类的实例。
  • 保护数据:在序列化敏感数据前,可以用 writeReplace 来替换或过滤数据。
示例
public class SensitiveData implements Serializable {private String sensitiveInfo;public SensitiveData(String info) {this.sensitiveInfo = info;}// 这个方法在序列化之前被调用private Object writeReplace() throws ObjectStreamException {// 返回一个替代对象进行序列化return new SafeDataProxy("Data is Protected");}private static class SafeDataProxy implements Serializable {private String safeInfo;public SafeDataProxy(String safeInfo) {this.safeInfo = safeInfo;}}
}

在上面的例子中,writeReplace 方法返回了一个内部静态类 SafeDataProxy 的实例。这个代理类不包含任何敏感信息。当 SensitiveData 类的对象被序列化时,实际上序列化的是 SafeDataProxy 对象。

readResolve 方法

readResolve 方法用于反序列化过程。当一个对象被反序列化时,如果其类定义了 readResolve 方法,这个方法会在反序列化后立即被调用,它返回的对象会替换从流中读取的对象。

使用场景
  • 维护单例属性:在单例模式下,防止反序列化创建新的实例。
  • 替换反序列化对象:在反序列化后,可能需要替换为另一个对象,例如恢复代理对象为原始对象。
示例
public class Singleton implements Serializable {private static final long serialVersionUID = 1L;// 类的唯一实例private static final Singleton INSTANCE = new Singleton();// 私有构造函数,防止外部实例化private Singleton() {}// 提供一个全局访问点public static Singleton getInstance() {return INSTANCE;}// readResolve 方法,用于保持单例属性private Object readResolve() throws ObjectStreamException {// 返回已经存在的单例实例,而不是反序列化产生的新实例return INSTANCE;}
}

在上面的例子中,Singleton.INSTANCE 是单例模式中唯一的实例。这确保了即使在序列化和反序列化过程中,也始终只有这一个实例。

注意事项

  • 这些方法必须声明为 private,以避免外部调用。
  • 它们必须返回 Object 类型。
  • 在实现时要注意线程安全和对象一致性问题。
  • 这些方法不是 Serializable 接口的一部分,但如果它们存在于实现了 Serializable 接口的类中,序列化机制会自动识别和使用它们。

通过使用 writeReplacereadResolve 方法,开发者可以有效地控制和自定义Java对象的序列化和反序列化行为。

writeObject 和 readObject 方法

当需要对Java对象的序列化过程进行更详细的控制时,可以通过重写 writeObjectreadObject 方法来自定义序列化和反序列化过程。这对于处理复杂对象或需要特别处理的成员变量(如那些不支持默认序列化的变量)特别有用。

示例:自定义序列化和反序列化过程

假设我们有一个 Person 类,其中包含一些基本信息和一个不可序列化的字段(如线程)。我们可以自定义序列化和反序列化过程来处理这个不可序列化的字段。

package per.mjn.bean;import java.io.*;class Employee implements Serializable {private String name;private int age;private transient String socialSecurityNumber; // 敏感信息,不希望序列化public Employee(String name, int age, String ssn) {this.name = name;this.age = age;this.socialSecurityNumber = ssn;}private void writeObject(ObjectOutputStream oos) throws IOException {oos.defaultWriteObject(); // 默认序列化处理// 自定义序列化逻辑// 在这里,我们选择不序列化社会保险号码}private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {ois.defaultReadObject(); // 默认反序列化处理// 自定义反序列化逻辑// 因为社会保险号码没有被序列化,我们可能需要在这里进行某些操作,如设置一个默认值this.socialSecurityNumber = "未知";}// Getters and Setterspublic String getName() {return name;}public int getAge() {return age;}public String getSocialSecurityNumber() {return socialSecurityNumber;}
}public class Main {public static void main(String[] args) {// 创建 Employee 对象Employee employee = new Employee("John Doe", 30, "123-45-6789");// 序列化到文件try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("employee.ser"))) {oos.writeObject(employee);System.out.println("Employee object serialized.");} catch (IOException e) {e.printStackTrace();}// 反序列化从文件try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("employee.ser"))) {Employee deserializedEmployee = (Employee) ois.readObject();System.out.println("Employee object deserialized.");System.out.println("Name: " + deserializedEmployee.getName() + ", Age: " + deserializedEmployee.getAge()+ ", socialSecurityNumber: " + deserializedEmployee.getSocialSecurityNumber());} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}}
}

在上面的例子中:

  • writeObject 方法:用于自定义对象的序列化过程。首先,我们调用 defaultWriteObject 来处理默认的序列化。然后,可以添加任何特殊的序列化逻辑。

  • readObject 方法:用于自定义对象的反序列化过程。首先,我们调用 defaultReadObject 来处理默认的反序列化。然后,可以添加任何特殊的反序列化逻辑,例如重新初始化 transient 字段。

程序运行结果如下:

Employee object serialized.
Employee object deserialized.
Name: John Doe, Age: 30, socialSecurityNumber: 未知

注意事项

  • 这些方法应该是 private 的,以确保它们不会被类的外部代码调用。

  • writeObjectreadObject中调用defaultWriteObjectdefaultReadObject是重要的,因为这些方法处理那些没有特殊处理需求的字段。

  • 通过这种方式,我们可以细粒度地控制哪些字段如何序列化,以及在反序列化时如何恢复对象的状态。

这种方法提供了对Java对象序列化和反序列化过程的高级控制,允许开发者处理复杂的序列化场景,或在序列化过程中包含额外的逻辑。


静态字段不参与Java的序列化过程

静态字段不参与Java的序列化过程,原因在于静态字段不是对象实例的一部分,而是属于类的状态。在Java中,序列化的主要目的是保存对象的状态(即实例变量),以便可以在以后准确地重建该对象的实例。以下是不序列化静态字段的几个关键原因:

  1. 类级别的状态:静态字段属于类级别的状态,而不是对象级别的状态。它们在类加载时初始化,并且对该类的所有实例共享。序列化的目的是存储和恢复对象的个体状态,而静态字段的状态跨所有实例共享。

  2. 内存和类加载器:静态字段的值存储在方法区或类区域中,并且由类加载器管理。这意味着它们的生命周期与应用程序的类加载器相关,而不是与单个对象实例相关。

  3. 数据一致性:假设静态字段可以被序列化和反序列化,那么这可能导致数据一致性问题。因为静态字段是共享的,序列化和随后的反序列化可能会在不同时间点导致类的不同实例看到该静态字段的不同状态。

  4. 设计哲学:Java序列化的设计哲学是为了支持对象的持久性和远程通信。在这两种情况下,关注的是对象的当前状态,而不是类级别的静态信息。

  5. 安全性和封装性:静态字段通常用于维护类的内部状态和全局配置。允许它们通过序列化进行修改可能会破坏封装性,导致安全和维护问题。

因此,静态字段不被序列化,也意味着反序列化对象时不会影响这些字段的状态。它们的值将由当前JVM中的类定义或静态初始化器所确定。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/640094.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2017年认证杯SPSSPRO杯数学建模A题(第一阶段)安全的后视镜全过程文档及程序

2017年认证杯SPSSPRO杯数学建模 A题 安全的后视镜 原题再现: 汽车后视镜的视野对行车安全非常重要。一般来说,汽车的后视镜需要有良好的视野范围,以便驾驶员能够全面地了解车后方的道路情况。同时,后视镜也要使图像的畸变尽可能…

国产品牌GC6609与TM2209的参数分析,为什么适用于3D打印机,医疗器械等产品中

步进电机驱动的应用方案目前市场上大多选用国外品牌的电机驱动器,其中trinamic的TMC2208/2209在这一块的应用很广泛。但是由于市场越来越应激。,当前对于产品开发成本要求也越来越低,国产品地准出了相应的TMC2208/2209,因此trinam…

Vue3+ElementUI 多选框中复选框和名字点击方法效果分离

现在的需求为 比如我点击了Option A &#xff0c;触发点击Option A的方法&#xff0c;并且复选框不会取消勾选&#xff0c;分离的方法。 <el-checkbox-group v-model"mapWork.model_checkArray.value"> <div class"naipTypeDom" v-for"item …

3、git实战操练

1、马上要封包上线了&#xff0c;如何将这个版本的代码封存。 命令操作/含义git tagtag的创建&#xff0c;推到服务器git archive对源码进行打包 $ git archive master --formatzip --output master.zip 2、 明天就是上线日期&#xff0c;今天合并你已经做完的几个关联性不大…

国家急救日倡议活动暨120急救大课堂公益培训在京成功举办

2024年1月20日&#xff0c;由国家卫生健康委员会等多个相关部门指导&#xff0c;中国医院协会急救中心&#xff08;站&#xff09;分会主办&#xff0c;北京急救中心承办的“国家急救日”倡议活动暨急救科普大课堂公益培训系列活动&#xff0c;在全国范围内启动。 健康中国行动…

「优选算法刷题」:长度最小的子数组

一、题目 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [numsl, numsl1, ..., numsr-1, numsr] &#xff0c;并返回其长度。如果不存在符合条件的子数组&#xff0c;返回 0 。 示例 1&#xff1a; 输…

王晶感慨62岁刘德华的一日三餐,简直不是人过的日子

♥ 为方便您进行讨论和分享&#xff0c;同时也为能带给您不一样的参与感。请您在阅读本文之前&#xff0c;点击一下“关注”&#xff0c;非常感谢您的支持&#xff01; 文 |猴哥聊娱乐 编 辑|徐 婷 校 对|侯欢庭 你是否曾对那些身材保持得如此之好的人感到过羡慕&#xff1f…

使用vue-pdf插件加载pdf

安装&#xff1a; // 安装这个版本&#xff0c;其它版本会有千奇百怪的错&#xff0c;这个版本和4.0.0都是可以的 cnpm install vue-pdf4.2.0// 安装pdfjs-dist cnpm install pdfjs-dist2.5.207 使用&#xff1a; // 我的css样式是pxToRem&#xff0c;友友们使用可能样式会有…

HCIP之BGP联邦实验

华子目录 实验拓扑及要求规划网段和IP地址实验步骤配置IP地址先让IGP通建BGP邻居修改ospf下环回接口网络类型修改联邦之间的最大跳数每台运行BGP的路由器批量宣告路由修改本地下一跳测试 实验拓扑及要求 规划网段和IP地址 实验步骤 配置IP地址 r1配置&#xff0c;依次类推 […

什么叫单位矩阵?

单位矩阵&#xff08;Identity Matrix&#xff09;是一个特殊的方阵&#xff0c;其主对角线上的元素全为1&#xff0c;而其他元素全为0。单位矩阵通常用符号 I 或 E 表示。 一个nn 的单位矩阵的表示形式如下&#xff1a; 其中&#xff0c;主对角线上的元素全为1&#xff0c;…

C/Cxx-面向对象中Message的概念

在Programming Abstractions in C pp.129遇到了面向对象中messages这个概念&#xff1a; “In the object-oriented world, objects communicate by sending information and requests from one object to another. Collectively, these transmissions are called messages. Th…

Unity New Input System 及其系统结构和源码浅析【Unity学习笔记·第十二】

转载请注明出处&#xff1a;&#x1f517;https://blog.csdn.net/weixin_44013533/article/details/132534422 作者&#xff1a;CSDN|Ringleader| 主要参考&#xff1a; 官方文档&#xff1a;Unity官方Input System手册与API官方测试用例&#xff1a;Unity-Technologies/InputS…

「优选算法刷题」:在排序数组中查找元素的第一个和最后个位置

一、题目 给你一个按照非递减顺序排列的整数数组 nums&#xff0c;和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target&#xff0c;返回 [-1, -1]。 你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。 示例 1&a…

springcloud alibaba sentinel熔断降级

简介 随着微服务的流行&#xff0c;服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点&#xff0c;从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。 sentinel相当于hystrix的升级版&#xff0c;加入了web界面&#xff0c;能够实时在线的改变流…

小微企业科技创新之策略:人才、投入、模式、技术、支持四管齐下

对于小微企业来说&#xff0c;搞科技创新需要从多个方面入手。以下是一些可供参考的方法&#xff1a; 明确创新方向&#xff1a;首先&#xff0c;企业需要明确自己的创新方向和目标&#xff0c;这有助于聚焦资源&#xff0c;避免盲目投入。同时&#xff0c;企业需要对市场进行…

【2024开年必备】最全面的Macbook/苹果电脑必备实用软件推荐

刚刚入手Macbook/苹果电脑需要安装哪些软件呢&#xff1f;越来越多的人使用 Mac&#xff0c;各种功能、各式各样的 Mac 软件也是五花八门。刚拿到 Mac 的小伙伴们可能会有点迷茫&#xff0c;今天就帮大家分类整理一些装机必备好用的 App&#xff0c;希望可以帮助到大家&#xf…

leetcode:最接近的三数之和---(双指针,排序,数组)

题目&#xff1a; 给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数&#xff0c;使它们的和与 target 最接近。 返回这三个数的和。 假定每组输入只存在恰好一个解。 示例&#xff1a; 示例 1&#xff1a; 输入&#xff1a;nums [-1…

SpringBoot - SpringBoot手写模拟SpringBoot启动过程

依赖 建一个工程&#xff0c;两个Module: 1. springboot模块&#xff0c;表示springboot框架的源码实现 2. user包&#xff0c;表示用户业务系统&#xff0c;用来写业务代码来测试我们所模拟出来的SpringBoot 首先&#xff0c;SpringBoot是基于的Spring&#xff0c;所以我…

2024最新版Python 3.12.1安装使用指南

2024最新版Python 3.12.1安装使用指南 Installation and Configuration Guide to the latest version Python 3.12.1 in 2024 By Jackson Python编程语言&#xff0c;已经成为全球最受欢迎的编程语言之一&#xff1b;它简单易学易用&#xff0c;以标准库和功能强大且广泛外挂…

Go语言网络编程介绍以及案例运用

一.Go网络编程概述 1. 基本概念 TCP 和 UDP: Go语言支持TCP&#xff08;传输控制协议&#xff09;和UDP&#xff08;用户数据报协议&#xff09;。TCP提供可靠的、面向连接的通信&#xff0c;而UDP提供无连接的快速数据传输。并发: Go语言的并发模型是通过goroutines实现的。…