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

Java 序列流:Java 对象的序列化和反序列化详解

1.概念

序列化是指将一个对象转换为一个字节序列(包含对象的数据对象的类型对象中存储的属性等信息),它可以将多个InputStream对象串联起来,使得它们可以被当作一个单一的输入流来处理,以便在网络上传输或保存到文件中,或者在程序之间传递。在 Java 中,序列化通过实现 java.io.Serializable 接口来实现,只有实现了 Serializable 接口open in new window的对象才能被序列化。

当你序列化对象时,你把它包装成一个特殊文件,可以保存、传输或存储。反序列化则是打开这个文件,读取序列化的数据,然后将其还原为对象,以便在程序中使用。

序列流的基本特性包括:

  • 懒加载:序列流的操作是懒加载,意味着它们不会立即执行,而是在需要结果时才执行。
  • 不可变:一旦创建了序列流,就不能修改它。如果需要修改,必须创建一个新的流。
  • 并行处理:序列流支持并行处理,可以利用多核处理器提高性能。

2.ObjectOutputStream

java.io.ObjectOutputStream 继承自 OutputStream 类,因此可以将序列化后的字节序列写入到文件、网络等输出流中。

来看 ObjectOutputStream 的构造方法:
ObjectOutputStream(OutputStream out)

该构造方法接收一个 OutputStream 对象作为参数,用于将序列化后的字节序列输出到指定的输出流中。例如:

FileOutputStream fos = new FileOutputStream("file.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);

 使用示例如下:

public class Employee implements Serializable {public String name;public String address;public transient int age; // transient瞬态修饰成员,不会被序列化
}

接下来,来聊聊 writeObject (Object obj) 方法,该方法是 ObjectOutputStream 类中用于将对象序列化成字节序列并输出到输出流中的方法,可以处理对象之间的引用关系、继承关系、静态字段和 transient 字段。

public class ObjectOutputStreamDemo {public static void main(String[] args) {Person person = new Person("沉默王二", 20);try {FileOutputStream fos = new FileOutputStream("logs/person.dat");ObjectOutputStream oos = new ObjectOutputStream(fos);oos.writeObject(person);oos.close();} catch (IOException e) {e.printStackTrace();}}
}
class Person implements Serializable {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}
}

一个对象要想序列化,必须满足两个条件:

  • 该类必须实现java.io.Serializable 接口open in new window,否则会抛出NotSerializableException 。
  • 该类的所有字段都必须是可序列化的。如果一个字段不需要序列化,则需要使用transient 关键字open in new window进行修饰。

3.ObjectInputStream 

ObjectInputStream 可以读取 ObjectOutputStream 写入的字节流,并将其反序列化为相应的对象(包含对象的数据对象的类型对象中存储的属性等信息)。

主要功能就是将ObjectOutputStream序列化的字节流转换回java对象。

使用步骤:

1、创建对象输入流:

FileInputStream fileIn =new FileInputStream("example.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);

2、读取对象:使用readObject的方法从流中读取对象。注意,读取的对象需要强制类型转换到正确的类型。

ObjectInputStream in =new ObjectInputStream(fileIn);
Object obj =in.readObject();
Employee emp =(Employee) obj;

整体演示:

String filename = "logs/person.dat"; // 待反序列化的文件名
try (FileInputStream fileIn = new FileInputStream(filename);ObjectInputStream in = new ObjectInputStream(fileIn)) {// 从指定的文件输入流中读取对象并反序列化Object obj = in.readObject();// 将反序列化后的对象强制转换为指定类型Person p = (Person) obj;// 打印反序列化后的对象信息System.out.println("Deserialized Object: " + p);
} catch (IOException | ClassNotFoundException e) {e.printStackTrace();
}

我们首先指定了待反序列化的文件名(前面通过 ObjectOutputStream 序列化后的文件),然后创建了一个 FileInputStream 对象和一个 ObjectInputStream 对象。接着我们调用 ObjectInputStream 的 readObject 方法来读取指定文件中的对象,并将其强制转换为 Person 类型。最后我们打印了反序列化后的对象信息。

4.transient

transient关键字是一个变量修饰符,用于声明一个字段不会被序列化。当一个对象被序列化时,默认状态下,对象的所有非静态和非瞬态字段都会被保存在字节流中。然而,如果你不希望某个字段被序列化,可以将其声明为transient。

 关键用途:

  • 保护敏感数据:如果你有一个包含敏感信息到字段(比如密码,信用卡等等)。
  • 避免序列化不必要的字段:有些字段可能在序列化时没有意义,或者不应该被保留在对象状态中。
public class TransientTest {public static void main(String[] args) {User user = new User();user.setUsername("我是大帅比");user.setPasswd("123456");System.out.println("read before Serializable: ");System.out.println("username: " + user.getUsername());System.err.println("password: " + user.getPasswd());try {ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("user.txt"));os.writeObject(user); // 将User对象写进文件os.flush();os.close();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}try {ObjectInputStream is = new ObjectInputStream(new FileInputStream("user.txt"));user = (User) is.readObject(); // 从流中读取User的数据is.close();System.out.println("\nread after Serializable: ");System.out.println("username: " + user.getUsername());System.err.println("password: " + user.getPasswd());} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}}
}class User implements Serializable {private static final long serialVersionUID = 8294180014912103005L;  private String username;private transient String passwd;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPasswd() {return passwd;}public void setPasswd(String passwd) {this.passwd = passwd;}}

运行结果:

read before Serializable:
username: 沉默王二
password: 123456 
read after Serializable:
username: 沉默王二
password: null

transitent的使用小结:

一旦字段被 transient 修饰,成员变量将不再是对象持久化的一部分,该变量的值在序列化后无法访问。

transient 关键字只能修饰字段,而不能修饰方法和类。

被 transient 关键字修饰的字段不能被序列化,一个静态变量(static关键字open in new window修饰)不管是否被 transient 修饰,均不能被序列化。

 

相关文章:

  • 基于 HTML+ECharts 实现智慧运维数据可视化大屏(含源码)
  • SpringCloud 负载均衡
  • linux、windows、macos,命令终端清屏
  • 从0到1:理发店预约剪发小程序开发笔记(上)
  • SpringBoot接入mongodb例子,并有增删改查功能
  • 四、GD32 MCU 常见外设介绍 (5) TIMER 模块介绍
  • Vue中的diff算法
  • 华为嵌入式面试题及参考答案(持续更新)
  • PyTorch 中的一个函数:torch.pow
  • 完美捕捉趋势!立足市场必备“指数通行红绿灯”!股票量化分析工具QTYX-V2.8.7...
  • 数据库(MySQL)-视图、存储过程、触发器
  • WPF MVVM框架:CommunityToolkit.Mvvm包使用介绍
  • 【Python】sqlite加密库pysqlcipher3编译安装步骤
  • C#/WinFrom TCP通信+ 网线插拔检测+客服端异常掉线检测
  • Linux中,MySQL的用户管理
  • “Material Design”设计规范在 ComponentOne For WinForm 的全新尝试!
  • 【跃迁之路】【733天】程序员高效学习方法论探索系列(实验阶段490-2019.2.23)...
  • AzureCon上微软宣布了哪些容器相关的重磅消息
  • ComponentOne 2017 V2版本正式发布
  • java架构面试锦集:开源框架+并发+数据结构+大企必备面试题
  • JDK 6和JDK 7中的substring()方法
  • Linux编程学习笔记 | Linux IO学习[1] - 文件IO
  • October CMS - 快速入门 9 Images And Galleries
  • vue-loader 源码解析系列之 selector
  • 仿天猫超市收藏抛物线动画工具库
  • 前嗅ForeSpider中数据浏览界面介绍
  • 如何学习JavaEE,项目又该如何做?
  • 限制Java线程池运行线程以及等待线程数量的策略
  • 消息队列系列二(IOT中消息队列的应用)
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • 阿里云服务器购买完整流程
  • 数据库巡检项
  • 选择阿里云数据库HBase版十大理由
  • # 手柄编程_北通阿修罗3动手评:一款兼具功能、操控性的电竞手柄
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • $.ajax中的eval及dataType
  • (3)(3.5) 遥测无线电区域条例
  • (Matlab)遗传算法优化的BP神经网络实现回归预测
  • (Mirage系列之二)VMware Horizon Mirage的经典用户用例及真实案例分析
  • (python)数据结构---字典
  • (附源码)spring boot建达集团公司平台 毕业设计 141538
  • (附源码)spring boot校园拼车微信小程序 毕业设计 091617
  • (全注解开发)学习Spring-MVC的第三天
  • (一)u-boot-nand.bin的下载
  • (原)本想说脏话,奈何已放下
  • .bat批处理(二):%0 %1——给批处理脚本传递参数
  • .net core MVC 通过 Filters 过滤器拦截请求及响应内容
  • .net dataexcel 脚本公式 函数源码
  • .NET 程序如何获取图片的宽高(框架自带多种方法的不同性能)
  • .NET 命令行参数包含应用程序路径吗?
  • .NET教程 - 字符串 编码 正则表达式(String Encoding Regular Express)
  • .NET平台开源项目速览(15)文档数据库RavenDB-介绍与初体验
  • .sys文件乱码_python vscode输出乱码
  • .vue文件怎么使用_vue调试工具vue-devtools的安装
  • @Data注解的作用