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

18.Java 序列化与反序列化

18.Java 序列化与反序列化

概述

在Java中,序列化是将对象的状态信息转换为可以存储或传输的格式的过程,而反序列化则是将这种格式转换回Java对象的过程。

序列化

要使一个对象支持序列化,该对象必须实现java.io.Serializable接口。这个接口是一个标记接口,不包含任何方法,但它告诉JVM该对象可以被序列化。

反序列化

反序列化是将之前序列化的对象数据转换回Java对象的过程。这需要使用ObjectInputStream来读取序列化数据。

主要用途和功能

  1. 对象持久化:将对象状态保存到文件或数据库中
  2. 网络传输:在不同JVM之间传输对象
  3. 缓存机制:缓存计算结果或对象状态
  4. RMI(远程方法调用):Java RMI 底层使用序列化传输对象

操作步骤

  1. 创建项目结构:按照上述项目结构创建文件夹和文件
  2. 编写实体类:实现Serializable接口,并定义需要序列化的字段
  3. 编写工具类
    • SerializationUtils:包含序列化和反序列化方法
  4. 编写主程序:演示序列化和反序列化流程
  5. 运行程序
    • 编译并运行Main
    • 观察控制台输出,验证序列化和反序列化结果
  6. 检查文件:程序运行后会生成user.ser文件

关键点说明

  1. Serializable接口:Java对象必须实现此接口才能被序列化
  2. serialVersionUID:用于版本控制,建议显式声明
  3. transient关键字:标记的字段不会被序列化
  4. 异常处理:序列化和反序列化可能抛出IOExceptionClassNotFoundException

项目结构

在这里插入图片描述

实现步骤

1. 创建实体类 (User.java)

package com.example.model;import java.io.Serializable;
import java.util.Date;/*** 用户实体类,实现Serializable接口以支持序列化*/
public class User implements Serializable {// 序列化版本UID,用于版本控制private static final long serialVersionUID = 1L;private String username;private transient String password; // transient字段不会被序列化private int age;private Date registerDate;// 构造方法public User() {}public User(String username, String password, int age, Date registerDate) {this.username = username;this.password = password;this.age = age;this.registerDate = registerDate;}// Getter和Setter方法public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Date getRegisterDate() {return registerDate;}public void setRegisterDate(Date registerDate) {this.registerDate = registerDate;}@Overridepublic String toString() {return "User{" +"username='" + username + '\'' +", age=" + age +", registerDate=" + registerDate+", 被transient 标记的字段password="+password+'}';}
}

2. 创建序列化工具类 (SerializationUtils.java)

package com.example.utils;import java.io.*;/*** 序列化和反序列化工具类*/
public class SerializationUtils {/*** 将对象序列化到文件* @param obj 要序列化的对象* @param filePath 文件路径* @throws IOException 如果发生I/O错误*/public static void serialize(Object obj, String filePath) throws IOException {try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath))) {oos.writeObject(obj);System.out.println("对象序列化成功,保存到: " + filePath);}}/*** 从文件反序列化对象* @param filePath 文件路径* @return 反序列化后的对象* @throws IOException 如果发生I/O错误* @throws ClassNotFoundException 如果类未找到*/public static Object deserialize(String filePath) throws IOException, ClassNotFoundException {try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath))) {Object obj = ois.readObject();System.out.println("对象反序列化成功,从: " + filePath);return obj;}}
}

4. 创建主程序 (Main.java)

package com.example;import com.example.model.User;
import com.example.utils.SerializationUtils;import java.io.IOException;
import java.util.Date;/*** 主程序,演示序列化和反序列化操作*/
public class Main {// 序列化文件路径private static final String SERIALIZED_FILE = "user.ser";public static void main(String[] args) throws IOException, ClassNotFoundException{// 1. 创建用户对象User user = new User("john_doe", "securePassword123", 30, new Date());System.out.println("原始对象: " + user);// 2. 序列化对象到文件SerializationUtils.serialize(user, SERIALIZED_FILE);// 3. 反序列化对象User deserializedUser = (User) SerializationUtils.deserialize(SERIALIZED_FILE);System.out.println("反序列化后的对象: " + deserializedUser);// 注意: password字段被标记为transient,不会被序列化System.out.println("原始对象密码: " + user.getPassword());System.out.println("反序列化对象密码: " + deserializedUser.getPassword()); // 输出null}
}

运行结果

生成了序列化结果的文件,user.ser
在这里插入图片描述
在这里插入图片描述

安全注意事项

  1. 反序列化漏洞:Java反序列化存在安全风险,不应反序列化不可信来源的数据
  2. 白名单验证:在实际应用中,应对反序列化的类进行白名单验证
  3. 替代方案:考虑使用JSON、XML等格式替代Java原生序列化
http://www.xdnf.cn/news/348067.html

相关文章:

  • Puppeteer vs Playwright:全面对比与最佳应用场景指南
  • GIS开发技术介绍
  • Filecoin中lotus节点的搭建部署
  • 【Axure高保真原型】中继器表格批量上传数据
  • 如何解决 Linux 系统文件描述符耗尽的问题
  • LaTeX印刷体 字符与数学符号的总结
  • 【MySQL】进阶知识详解
  • 全球异硬脂酸及其衍生物市场:绿色化学浪潮下的技术迭代与区域增长新逻辑
  • Codeforces Round 1012 (Div. 2)
  • MybatisPlus 发布 3.5.12 版本啦
  • 过曝区域信息补全
  • Python从入门到高手8.3节-元组的常用操作方法
  • 【战略合作】开封大学_阀门产业学院+智橙PLM
  • maven 依赖冲突异常分析
  • 17.thinkphp的分页功能
  • 开发者如何应对浏览器中的身份关联与反追踪问题?
  • 主成分分析(PCA)是什么?简易理解版
  • 使用Compose编排工具搭建Ghost博客系统
  • goner/otel 在Gone框架接入OpenTelemetry
  • [python] 函数1-函数基础
  • 软考职称政策再加码!已有多地发布通知!
  • SiC MOSFET同步Buck DC-DC变换器的宽频混合EMI滤波器设计
  • 【嵌入式开发-UART】
  • docker 安装 sqlserver2022 和注意点
  • 模拟散列表(算法题)
  • Vue3中emits和emit
  • Qwen3中的MoE是如何平衡专家负载的?
  • 跨线程和跨进程通信还有多种方式对比
  • JS 下载data:image/png;base64, 图片
  • 告别手动输入密码:基于SSHPass的自动化文件传输实践告别手动输入密码:基于SSHPass的自动化文件传输实践