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

Java学习之——“IO流“的进阶流之序列化流的学习

一、核心概念:什么是序列化与反序列化?

  • 序列化 (Serialization): 将一个对象(在内存中的状态)转换成一个字节序列的过程。这个字节序列包含了对象的数据、对象的类型以及对象中存储的属性等信息。
  • 反序列化 (Deserialization): 将序列化后得到的字节序列恢复为一个对象的过程。

目的

  • 持久化存储:将对象永久地保存到硬盘上的文件中,下次程序启动时可以恢复。
  • 网络传输:将对象通过网络从一个节点传输到另一个节点,例如在 RPC(远程过程调用)、消息队列或分布式系统中。

二、序列化的实现

        Java 中要让一个类的对象能够被序列化,非常简单:只需要让这个类实现 java.io.Serializable 接口即可。Serializable 接口是一个标记接口(Marker Interface),它内部没有任何方法需要实现。它的作用仅仅是“标记”这个类的对象是可序列化的,告诉 Java 虚拟机(JVM):“请注意,我这个类的对象可以被序列化哦!”

示例:定义一个可序列化的类

import java.io.Serializable;// 实现 Serializable 接口
public class Person implements Serializable {// 强烈建议显式声明 serialVersionUIDprivate static final long serialVersionUID = 1L; private String name;private int age;// transient 关键字标记的成员变量不会被序列化private transient String password; // 构造方法、getter、setter 等...public Person(String name, int age, String password) {this.name = name;this.age = age;this.password = password;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +", password='" + password + '\'' + // 反序列化后 password 会是 null'}';}// ... 省略 getter 和 setter
}

关键点 1:serialVersionUID

  • 是什么:一个类的序列化版本号。用于在反序列化时验证序列化的对象和当前类的版本是否一致。
  • 为什么重要:如果你不显式声明,JVM 会根据类的结构自动生成一个。一旦类的结构发生改变(比如增加了一个字段),自动生成的 serialVersionUID 也会改变,这将导致反序列化失败,抛出 InvalidClassException
  • 最佳实践强烈建议显式声明一个固定的 serialVersionUID(如 1L)。这样即使类后期增加了字段,只是反序列化时新字段为默认值,而不会直接失败,保证了向后兼容性。

关键点 2:transient 关键字

  • 作用:用于修饰成员变量,表示该变量不参与序列化过程。
  • 使用场景:用于保护敏感信息(如密码、密钥等),或者存储一些没必要序列化的临时数据(如缓存、线程句柄等)。

三、序列化流与反序列化流的核心类

Java 提供了两个专门的流来处理对象的序列化和反序列化:

  • ObjectOutputStream: 序列化流,用于将对象写入字节输出流(如文件)。
  • ObjectInputStream: 反序列化流,用于从字节输入流(如文件)中读取并重建对象。

它们通常需要包裹在字节流(如 FileInputStream/FileOutputStream)之上,因为它们的底层操作仍然是字节。

序列化对象(写入文件)
import java.io.*;public class SerializationDemo {public static void main(String[] args) {Person person = new Person("Alice", 30, "secret123");// try-with-resources 确保流正确关闭try (// 1. 创建节点流(字节流),指向目标文件FileOutputStream fos = new FileOutputStream("person.dat");// 2. 创建处理流(序列化流),包裹节点流ObjectOutputStream oos = new ObjectOutputStream(fos)) {// 3. 关键操作:将对象写入(序列化)到文件oos.writeObject(person);System.out.println("对象序列化成功!");} catch (IOException e) {e.printStackTrace();}}
}
反序列化对象(从文件读取)
import java.io.*;public class DeserializationDemo {public static void main(String[] args) {Person person = null;try (// 1. 创建节点流(字节流),连接到源文件FileInputStream fis = new FileInputStream("person.dat");// 2. 创建处理流(反序列化流),包裹节点流ObjectInputStream ois = new ObjectInputStream(fis)) {// 3. 关键操作:读取(反序列化)字节流并重建对象// 需要强制类型转换person = (Person) ois.readObject();System.out.println("对象反序列化成功!");System.out.println(person); // 调用 toString 方法} catch (IOException | ClassNotFoundException e) { // 注意 ClassNotFoundExceptione.printStackTrace();}// 输出:Person{name='Alice', age=30, password='null'}// password 被 transient 修饰,所以反序列化后为 null(默认值)}
}

五、重要注意事项与特性

静态变量不会被序列化

  • 序列化是针对对象实例的,静态变量属于类,不属于任何单个对象,所以不会被序列化。

引用类型的成员变量也必须可序列化

  • 如果一个类有引用类型的成员变量(例如 Person 类里有一个 Address address 字段),那么这个引用类型(Address 类)也必须实现 Serializable 接口,否则整个序列化过程会失败,抛出 NotSerializableException

反序列化不会调用构造方法

  • 对象是通过从流中读取数据并直接赋值来重建的,构造方法不会被调用。

文章转载自:

http://C5hfH9Cw.ncwgt.cn
http://MLbiDTgW.ncwgt.cn
http://YgQ3QIyu.ncwgt.cn
http://AqhlmGst.ncwgt.cn
http://65UQPLND.ncwgt.cn
http://yOhQp77R.ncwgt.cn
http://rEsCEIjy.ncwgt.cn
http://wSGr6kA8.ncwgt.cn
http://1vzdJWzp.ncwgt.cn
http://W7ToyHrH.ncwgt.cn
http://WvF5WrYZ.ncwgt.cn
http://bzVtydou.ncwgt.cn
http://lbz74pQo.ncwgt.cn
http://eFk0fXg3.ncwgt.cn
http://bOlU9RDw.ncwgt.cn
http://YK4MNISN.ncwgt.cn
http://vf6019PM.ncwgt.cn
http://pQPHE5cS.ncwgt.cn
http://TjJwTysY.ncwgt.cn
http://V93hXOgz.ncwgt.cn
http://zVfobyh0.ncwgt.cn
http://XGrckaiF.ncwgt.cn
http://6kkf7V1g.ncwgt.cn
http://McGdfT8Y.ncwgt.cn
http://CeZLc6eB.ncwgt.cn
http://QucbIVMn.ncwgt.cn
http://Frdgu1gQ.ncwgt.cn
http://Wr1VaeKq.ncwgt.cn
http://tbbsjgJY.ncwgt.cn
http://M8LD1GJk.ncwgt.cn
http://www.dtcms.com/a/376055.html

相关文章:

  • LeetCode 面试经典 150 题:轮转数组(三次翻转法详解 + 多解法对比)
  • 什么是PFC控制器
  • 【卷积神经网络详解与实例3】——池化与反池化操作
  • Bean的生命周期 高频考点!
  • Redis 主从复制详解:原理、配置与主从切换实战
  • Java锁机制全解析:从AQS到CAS,深入理解synchronized与ReentrantLock
  • 基于SpringBoot的天气预报系统的设计与实现
  • Android 14 servicemanager的前世今生
  • TC_Motion多轴运动-电子齿轮
  • webrtc弱网-DelayBasedBwe 类源码分析与算法原理
  • 【Floor报错注入】
  • Docker生产部署
  • 小型语言模型:智能体AI的未来?
  • js垃圾回收机制
  • STM32开发(USART总线:UART总线)
  • Typescript - 通俗易懂的 interface 接口,创建接口 / 基础使用 / 可选属性 / 只读属性 / 任意属性(详细教程)
  • FastGPT源码解析 Agent 智能体应用创建流程和代码分析
  • [网络入侵AI检测] 模型性能评估与报告
  • chmod与chown命令的深度解析
  • 7层的API网关
  • 链表问题:LeetCode 两数相加 - 算法解析与详解
  • 类型别名(type)与接口(interface)的抉择
  • 4.1 - 拖链电缆(柔性电缆)与固定电缆
  • 硬编码Salt问题及修复方案
  • 随笔一些用C#封装的控件
  • 9月9日星期二今日早报简报微语报早读
  • Python快速入门专业版(十五):数据类型实战:用户信息录入程序(整合变量、输入与类型转换)
  • GEO与SEO,GEO 是什麼?SEO + AI = GEO 生成式搜尋引擎優化 全解析
  • Asp .Net Core 系列:Asp .Net Core 集成 Hangfire+MySQL
  • 如果服务端有数据更新,浏览器缓存同时也没有过期,如何直接使用最新的数据