java序列化之writeObject 和readObject

article/2025/9/14 2:52:38

什么是序列化和反序列化?

序列化:将对象转化为字节的过程称为序列化过程。
反序列化:将字节转化为对象的过程称为反序列化。

序列化主要应用于网络传输和数据存储的场景。在java中,只有类实现了java.io.serializable接口,该类才能被序列化。
示例Demo1.java:

package com.example.demo;import java.io.*;public class Demo1 {public static class Person implements Serializable {private static final long serialVersionUID = 233858934995755239L;private String firstName;private String lastName;public Person(String firstName,String lastName){System.out.println("Init Person...");this.firstName = firstName;this.lastName = lastName;}public String toString() {return String.format("Pearson.toString(): firstName=%s,lastName=%s", firstName, lastName);}}public static void main(String[] args) {String firstName="Li",LastName="Kelly";Person person  = new Person(firstName, LastName);System.out.println("序列化前:"+person.toString());ObjectOutputStream outStream;ObjectInputStream inStream;String filePath = "D:/demo/person.obj";try {//创建一个ObjectOutputStream输出流outStream = new ObjectOutputStream(new FileOutputStream(filePath));//将对象序列化到文件filePathoutStream.writeObject(person);inStream = new ObjectInputStream(new FileInputStream(filePath));Person readObject = (Person)inStream.readObject();System.out.println("反序列化后:"+readObject.toString());} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}

输出:

Init Person...
序列化前:Pearson.toString(): firstName=Li,lastName=Kelly

反序列化后:Pearson.toString(): firstName=Li,lastName=Kelly

可见对象反序列化,并不没有调用类的构造方法(反序列化没有输出Init Person...)。

注:

如果一个可序列化的类的成员不是基本类型,也不是String类型,那这个引用类型也必须是可序列化的,也即继承serializable,否则,会导致此类不能序列化。

比如上面的代码中,我们给Person类加一个Dog对象,这个Dog没有继承serializable。

package com.example.demo;import java.io.*;public class Demo1 {public static class Dog{}public static class Person implements Serializable {private static final long serialVersionUID = 233858934995755239L;private String firstName;private String lastName;private Dog dog;public Person(String firstName,String lastName, Dog dog){System.out.println("Init Person...");this.firstName = firstName;this.lastName = lastName;this.dog = dog;}public String toString() {return String.format("Pearson.toString(): firstName=%s,lastName=%s", firstName, lastName);}}public static void main(String[] args) {String firstName="Li",LastName="Kelly";Dog dog= new Dog();Person person  = new Person(firstName, LastName,dog);System.out.println("序列化前:"+person.toString());ObjectOutputStream outStream;ObjectInputStream inStream;String filePath = "D:/demo/person.obj";try {//创建一个ObjectOutputStream输出流outStream = new ObjectOutputStream(new FileOutputStream(filePath));//将对象序列化到文件filePathoutStream.writeObject(person);inStream = new ObjectInputStream(new FileInputStream(filePath));Person readObject = (Person)inStream.readObject();System.out.println("反序列化后:"+readObject.toString());} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}

此时执行代码会报java.io.NotSerializableException: com.example.demo.Demo1$Dog的错误。让Dog继承serializable则不会报错。

问:

①同一个对象序列化多次,会将这个对象序列化多次吗?否。java序列化同一对象,并不会将此对象序列化多次得到多个对象。

            outStream = new ObjectOutputStream(new FileOutputStream(filePath));//将对象序列化到文件filePathoutStream.writeObject(person);outStream.writeObject(person);inStream = new ObjectInputStream(new FileInputStream(filePath));Person readObject1 = (Person)inStream.readObject();Person readObject2 = (Person)inStream.readObject();System.out.println(readObject1==readObject2);System.out.println("反序列化后:"+readObject1.toString());

output:

Init Person...
序列化前:Pearson.toString(): firstName=Li,lastName=Kelly
true
序列化后:Pearson.toString(): firstName=Li,lastName=Kelly

ReadObject1和ReadObject2是同一对象。

②有些时候,我们有这样的需求,某些属性不需要序列化。怎么做?使用transient关键字选择不需要序列化的字段。

示例:

package com.example.demo;import java.io.*;public class Demo1 {public static class Dog implements Serializable{}public static class Person implements Serializable {private static final long serialVersionUID = 233858934995755239L;private String firstName;private transient String lastName;private transient int age;private Dog dog;public Person(String firstName,String lastName, Dog dog, int age){System.out.println("Init Person...");this.firstName = firstName;this.lastName = lastName;this.dog = dog;this.age = age;}public String toString() {return String.format("Pearson.toString(): firstName=%s,lastName=%s,age=%s", firstName, lastName,age);}}public static void main(String[] args) {String firstName="Li",LastName="Kelly";Dog dog= new Dog();Person person  = new Person(firstName, LastName,dog,23);System.out.println("序列化前:"+person.toString());ObjectOutputStream outStream;ObjectInputStream inStream;String filePath = "D:/demo/person.obj";try {//创建一个ObjectOutputStream输出流outStream = new ObjectOutputStream(new FileOutputStream(filePath));//将对象序列化到文件filePathoutStream.writeObject(person);inStream = new ObjectInputStream(new FileInputStream(filePath));Person readObject = (Person)inStream.readObject();System.out.println("反序列化后:"+readObject.toString());} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}

输出:

Init Person...
序列化前:Pearson.toString(): firstName=Li,lastName=Kelly,age=23
反序列化后:Pearson.toString(): firstName=Li,lastName=null,age=0

从输出我们看到,使用transient修饰的属性(此处是lastName、age),java序列化时,会忽略掉此类字段。

反序列化出的对象,被transient修饰的属性是默认值。对于引用类型,值是null;基本类型,值是0;boolean类型,值是false。

③使用transient虽然简单,但是将此属性完全隔离在了序列化之外。有没有一种机制,可以定制对象序列化的方式?

答:有。通过重写writeObject与readObject方法,可以自己选择哪些属性需要序列化, 哪些属性不需要。如果writeObject使用某种规则序列化,则相应的readObject需要相反的规则反序列化,以便能正确反序列化出对象。

示例:

package com.example.demo;import java.io.*;public class Demo1 {public static class Dog implements Serializable{}public static class Person implements Serializable {private static final long serialVersionUID = 233858934995755239L;private String firstName;private transient String lastName;private transient int age;private Dog dog;public Person(String firstName,String lastName, Dog dog, int age){System.out.println("Init Person...");this.firstName = firstName;this.lastName = lastName;this.dog = dog;this.age = age;}public String toString() {return String.format("Pearson.toString(): firstName=%s,lastName=%s,age=%s", firstName, lastName,age);}private void writeObject(ObjectOutputStream out) throws IOException {//将名字反转写入二进制流out.writeObject(new StringBuffer(this.lastName).reverse());out.writeInt(age);}private void readObject(ObjectInputStream ins) throws IOException,ClassNotFoundException{//将读出的字符串反转恢复回来this.lastName = ((StringBuffer)ins.readObject()).reverse().toString();this.age = ins.readInt();}}public static void main(String[] args) {String firstName="Li",lastName="Kelly";Dog dog= new Dog();Person person  = new Person(firstName, lastName, dog,23);System.out.println("序列化前:"+person.toString());ObjectOutputStream outStream;ObjectInputStream inStream;String filePath = "D:/demo/person.obj";try {//创建一个ObjectOutputStream输出流outStream = new ObjectOutputStream(new FileOutputStream(filePath));//将对象序列化到文件filePathoutStream.writeObject(person);inStream = new ObjectInputStream(new FileInputStream(filePath));Person readObject = (Person)inStream.readObject();System.out.println("反序列化后:"+readObject.toString());} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}

在上面的例子中,我们给Person类增加了writeObject和readObject方法。

private void writeObject(ObjectOutputStream out) throws IOException {//将名字反转写入二进制流out.writeObject(new StringBuffer(this.lastName).reverse());out.writeInt(age);}private void readObject(ObjectInputStream ins) throws IOException,ClassNotFoundException{//将读出的字符串反转恢复回来this.lastName = ((StringBuffer)ins.readObject()).reverse().toString();this.age = ins.readInt();}

输出:

Init Person...
序列化前:Pearson.toString(): firstName=Li,lastName=Kelly,age=23
反序列化后:Pearson.toString(): firstName=null,lastName=Kelly,age=23

在writeObject里,首先out.writeObject(new StringBuffer(this.lastName).reverse());这句的作用是将名字反转写入二进制流。我们打开D:/demo/person.obj文件,看下lastName的形式是不是反转的。

person.obj文件是乱码的,但是我们仍然可以看到我们想要的,Kelly变成了yllek。

writeObject第二句是out.writeInt(age);age属性我们加了transient关键字,理论上这个属性应该不会被序列化。然而我们在这里加上了这行代码,所以age又可以被序列化。

除了这个,我们还可以看到writeObject和readObject都是private方法,但是它却被外部类(ObjectOutputStream和ObjectInputStream)调用,并且它们既不存在与Java.lang.Object,也没有在Serializeable中声明,那它们是如何被ObjectOutputStream和ObjectInputStream调用的呢?
答:利用反射机制。ObjectOutputStream和ObjectInputStream使用了反射来寻找是否声明了这两个方法。因为它们使用getPrivateMethod,所以这些方法不得不被声明为priate以至于供ObjectOutputStream来使用。 

注:Write的顺序和read的顺序需要对应。譬如writeObject有多个字段都用writeInt写入流中,那么readint需要按照顺序将其赋值。

例:

private void writeObject(ObjectOutputStream out) throws IOException {//将名字反转写入二进制流out.writeObject(new StringBuffer(this.lastName).reverse());out.writeInt(age);out.writeObject(this.firstName);}private void readObject(ObjectInputStream ins) throws IOException,ClassNotFoundException{//将读出的字符串反转恢复回来this.lastName = ((StringBuffer)ins.readObject()).reverse().toString();this.firstName =(String) ins.readObject();this.age = ins.readInt();}

此时代码报错:

java.io.OptionalDataException
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1585)

现在主要在公众号PostgreSQL运维技术 更新,欢迎来踩~

悄悄放一张:

PostgreSQL运维技术 

 


http://chatgpt.dhexx.cn/article/9L3eNWDT.shtml

相关文章

java序列化总结

目录 对象序列化是什么 为什么需要序列化与反序列化 序列化及反序列化相关知识 Java 序列化中如果有些字段不想进行序列化,怎么办? Java序列化接口 java.io.Serializable 使用序列化和serialVersionUID进行类重构 Java外部化接口 java.io.Externa…

java序列化接口Serializable

Serializable接口说明 类的可序列化性通过实现(implements) java.io.Serializable可序列化接口。 没有实现这个接口的类不会将其任何状态序列化或反序列化。 可序列化类的所有子类型本身可序列化。 序列化接口没有方法或字段只用于识别可序列化的语义。 为了允许序列化不可序…

Java序列化之serialVersionUID

Java序列化之serialVersionUID 今天讲一讲Java对象中的serialVersionUID,先从序列化讲起。 什么是序列化 序列化,简单的说,就是将一个对象转化(编码)成可以传输的输出流(字节流)。而反序列化…

一文了解Java序列化与反序列化

目录 序列化示例有父类的对象序列化Serializable和Externalizable区别序列化和反序列化实现serialVersionUID不一致有什么问题1、先注释掉反序列化代码, 执行序列化代码, 然后User类新增一个属性sex2、再注释掉序列化代码执行反序列化代码3、指定serialVersionUID 序列化字段修…

一篇搞懂java序列化Serializable

序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程。 一、序列化和反序列化的概念 把对象转换为字节序列的过程称为对象的序列化。 把字节序列恢复为对象的过程称为对象的反序列化。 对象的序列化主要有两种用途: 1) 把对象…

Java序列化与数据传输

1)什么是序列化 ① 序列化:Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。 堆内存中的java对象数据&…

java序列化的作用

1.java序列化需要实现Serializaible接口或者Externalizable接口 2.java实现序列化的作用: 1.方便在远程调用时对象的解码与编码,就像new对象之间直接调用,不需要像传输对象之间像JSON转换一样转来转去 2.序列化的能力:为了在程序中能直接以对象的形式进…

Java序列化与反序列化

参考链接: Java 序列化与反序列化_Jacks丶的博客-CSDN博客_java反序列化 1 序列化与反序列化的概念 Java 序列化是指:将对象转化成一个字节序列(二进制数据)的过程。 将序列化对象写入文件之后,可以从文件中读取出来,并且对它…

Java序列化

一、什么是序列化?为什么要序列化? Java 序列化就是指将对象转换为字节序列的过程,而反序列化则是只将字节序列转换成目标对象的过程。 我们都知道,在进行浏览器访问的时候,我们看到的文本、图片、音频、视频等都是通…

【JAVA基础】java基础之-序列化详解

写在前面的话 脑子是个好东西,可惜的是一直没有搞懂脑子的内存删除机制是什么,所以啊,入行多年,零零散散的文章看了无数,却总是学习了很多也忘了很多。 痛定思痛的我决定从今天开始系统的梳理下知识架构,记…

JAVA的序列化

絮叨 Java序列化其实这个用的多不多,我觉得看公司的技术栈吧,如果用的是cloud那套,估计接触的会少点,但是也不是说没有,如果是dubbo那套的话,就多点,上次我们说Netty的编码解码的时候说到了谷歌…

MySQL卸载重新安装会遇到的问题及解决方式

好家伙,全踩雷了: 一.使用cd命令无法进入到其他盘符? 解决:在cd后加/d,两者之间用空格隔开 二.初始化时没有给出密码? 解决:仔细检查my.ini配置文件中的配置命令,一般是配置命令写错…

如何将MySQL卸载干净?

前言 前几天不知道什么情况电脑被莫名下载了MySQL8.0版本导致与本机的MySQL5.0版本冲突,导致MySQL无法正常运行,后来因为没有卸载干净MySQL导致一直下载不成功,导致我晚上失眠,现在让我们进入正题,看看如何将他卸载干…

【通关MySQL】Win11如何将MySQL卸载干净?

✨哈喽,进来的小伙伴们,你们好耶!✨ 🚀🚀系列专栏:【通关MySQL】 ✈️✈️本篇内容: MySQL如何卸载干净? ⛵⛵作者简介:一名双非本科大三在读的科班Java编程小白,道阻且长&#xff…

如何能将mysql卸载干净

本篇文章介绍一下如何干净卸载mysql。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。 第七步出现找不到相应的文件夹。第七步:路径是C:\Users\你的用户名字\AppData\Roaming\MySQL 有可能是隐藏的,需要显示隐藏…

如何将mysql卸载干净

一、在控制面板中卸载mysql软件 二、卸载过后删除C:\Program Files (x86)\MySQL该目录下剩余了所有文件,把mysql文件夹也删了 三、windowsR运行“regedit”文件,打开注册表 四、删除注册表:HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Service…

MySQL卸载安装

目录 MySQL卸载安装 1. MySQL卸载(8.0版本) 1.1停止MySQL服务 1.2 卸载MySQL相关组件 1.3删除MySQL安装目录 1.4删除MySQL数据目录 1.5 删除MySQL注册表信息(重要) 2. MySQL安装(5.7.24版本) 2.1安装…

mysql卸载不_mysql卸载不干净解决方法

一、在控制面板中卸载mysql软件,此时mysql没有卸载干净。 二、卸载过后删除C:Program Files (x86)MySQL该目录下剩余了所有文件,把mysql文件夹也删了 三、windowsR运行“regedit”文件,打开注册表 四、删除注册表:HKEY_LOCAL_MACH…

MySQL的卸载与安装(附管理工具Navicat SQLyog)

本文MySQL为5.1.51版本,附全文所需文件压缩包(百度云 提取码:8778),其中SQLyog和Navicat为MySQL的图形化工具,任选其一安装即可(建议Navicat) 1.MySQL卸载 如果之前没有安装过MySQ…

【 Windows、Mac 】Mysql 卸载、安装

文章目录 Windows Mysql 卸载、安装一、彻底卸载mysql二、安装mysql 5.7三、安装 mysql 8四、MySql服务开机自启动失败 Mac 安装 mysql一、下载对应mysql版本二、安装三、重置密码 Windows Mysql 卸载、安装 一、彻底卸载mysql 1、关闭 MySql 服务。 2、在控制面板卸载mysq…