java序列化接口Serializable

article/2025/9/14 2:48:56

Serializable接口说明

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

为了允许序列化不可序列化类的子类型,子类型可以承担保存和还原父类型的公开状态(public),受保护状态(protected)和(如果可以访问)包字段。 只有在以下情况下,子类型才能承担此责任:扩展的类具有可访问的无参(no-arg)构造函数,用于初始化类的状态。 如果不是这种情况,声明出来的序列化类是错误的, 错误将会在运行时(runtime)被检测到。

在反序列化期间,不可序列化类的字段将使用公开的(public)或受保护的(protected)无参(no-arg)构造函数进行初始化。 子类必须可访问无参数构造函数那是可序列化的。 可序列化子类的字段将从流中恢复。

在遍历类字段时,可能会遇到一个对象,该对象不会支持序列化Serializable接口。 在这种情况下会抛出NotSerializableException并将识不可序列化的对象类名称。

类在序列化和反序列化过程必须执行具有这些确切要求的特殊方法签名:

     1、private void writeObject(java.io.ObjectOutputStream out)throws IOException

     2、private void readObject(java.io.ObjectInputStream in)throws IOException,ClassNotFoundException;

            3、private void readObjectNoData( ) throws ObjectStreamException;

writeObject方法负责写入对象,以便readObject方法可以还原它。默认的保存机制可以通过调用来调用对象的字段out.defaultWriteObject。该方法不需要考虑它自身的状态属于它的超类(superclasses)或子类(subclasses)。通过使用writeObject方法或使用DATAOutput支持的原始数据类型将各个字段状态保存到ObjectOutputStream。

readObject方法负责从流中读取并恢复类类属性字段。它可以调用in.defaultReadObject来调用恢复对象的非静态(non-static)和非瞬态(non-transient)字段。defaultReadObject方法使用流中的信息将保存在流中的对象的字段与当前对象中相应命名的字段一起分配。当类添加新字段的时候,会发生这种情况。 该方法不需要关注属于其超类(superclasses)或子类(subclasses)的状态。通过使用writeObject方法或使用DATAOutput支持的原始数据类型将各个字段状态保存到ObjectOutputStream。

readObjectNoData方法负责在序列化流没有将给定类列为反序列化对象的超类的情况下初始化其特定类的对象状态。当接收方使用与发送方不同的反序列化实例类版本,并且接收方的版本扩展了发送方版本未扩展的类时,可能会发生这种情况。如果序列化流被篡改,也可能发生这种情况;因此,readObjectNoData对于正确初始化反序列化对象非常有用,尽管源流“恶意”或不完整。

在将对象写入流时,需要指定备用对象的可序列化类应使用精确签名实现此特殊方法:

             ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;

如果方法存在,并且可以从中定义的方法访问正在序列化的对象的类。因此,该方法可以具有私有(private)、受保护(protected)和包私有(package-private)访问。对这个方法的子类访问遵循java可访问性规则。

当从流中读取替换的实例时,需要指定替换的类应该用精确的签名实现这个特殊方法:

            ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;

            此readResolve方法遵循相同的调用规则和可访问性规则为writeReplace。

序列化运行时与每个可序列化的类关联一个版本号,称为serialVersionUID,在反序列化期间使用该版本号来验证序列化对象的发送者和接收者是否已加载了该对象的与序列化兼容的类。如果接收方已为具有不同对象的对象加载了一个类serialVersionUID比相应发送者的类要大,则反序列化将导致InvalidClassException。 一个可序列化的类可以通过以下方式显式声明其自己的serialVersionUID,声明一个名为serialVersionUID 字段,该字段必须为静态(static),不可变(final),且类型为long。

            ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

如果可序列化的类没有显示声明serialVersionUID,则序列化运行时将根据Java对象序列化规范计算默认的serialVersionUID值基于类的各个方面。但是,强烈建议,所有可序列化的类都显示声明serialVersionUID值,因为默认的serialVersionUID计算可能因编译器实现不同使类详细信息高度敏感,导致在反序列化期间意外 InvalidClassException。因此,在不同的Java编译器中保证一致的serialVersionUID值实现时,可序列化的类必须声明一个显式的serialVersionUID值。还强烈建议明确serialVersionUID声明使用private修饰符,其中可能,因为此类声明仅适用于立即声明class--serialVersionUID字段不能用作继承成员。数组类不能声明显式的serialVersionUID,因此它们始终具有默认计算值,但需要匹配数组类将免除serialVersionUID值。

以上内容翻译自jdk中java.io.Serializable接口声明注释,以上内容做简单总结,可以整理为如下几点:

  • 序列化和反序列类必须实现Serializable接口
  • 序列化类的引用类型的属性类必须实现Serializable接口
  • 序列化类须有无参构造方法
  • writeObject方法负责写入对象;readObject方法负责还原对象
  • 每个可序列化类都有一个serialVersionUID,建议显示声明,使用static fianl long 修饰符修饰

Serializable代码示例

序列化与反序列化

定义Student类,Student类实现Serializable接口,以便Student对象可以序列化与反序列化。

import java.io.Serializable;public class Student implements Serializable{private String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}public String toString() {return "[name:" + name + ";age:" + age + "]";}
}

这里的Student类实现(implements)了Serializable接口,但是没有定义serialVersionUID属性。

main方法测试

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;public class SerializableMain {public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {/*** 序列化 writeObject方法写对象*/Student stu = new Student("zhangsan", 15);ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("stu.txt"));oos.writeObject(stu);oos.close();/*** 反序列化 readObject方法还原对象*/ObjectInputStream ois = new ObjectInputStream(new FileInputStream("stu.txt"));Student stuSerializable = (Student) ois.readObject();ois.close();System.out.println(stuSerializable);}
}

序列化后,可以在工程根目录下看到对象文件stu.txt,打开后可以看到序列化对象stu的一些信息,由于是序列化后的对象,这里打开看到的是乱码。

反序列化后,在控制台,可以看到,打印出的stuSerializable对象信息。

[name:zhangsan;age:15]

至此,序列化与反序列化成功。

上面的Student类定义中,没有显示声明serialVersionUID属性,成功序列化与反序列化。但这个从编码规范和序列化安全角度,不提倡,还是显示声明serialVersionUID属性。

public class Student implements Serializable{/*** 序列化类版本id*/private static final long serialVersionUID = 6384476034643864037L;

transient关键字

transient关键字在Java中修饰临时属性,被transient关键字修饰的属性不会序列化对数据流中。

import java.io.Serializable;public class Student implements Serializable{private String name;private transient int age;public Student(String name, int age) {this.name = name;this.age = age;}public String toString() {return "[name:" + name + ";age:" + age + "]";}
}

再通过main方法测试,可以看到,反序列化得到的age为0,这里age为0原因即transient关键字修饰了age属性,age属性作为一个临时属性,在序列化的时候未序列化到数据流中。

[name:zhangsan;age:0]

对象属性序列化

在原有Student属性中增加一个对象属性Address,Address属性未实现implements java.io.Serialzable接口

public class Address {// 省private String provice;public Address(String provice) {this.provice = provice;}public String toString() {return "[provice:"+ provice+"]";}
}
import java.io.Serializable;public class Student implements Serializable{/*** 序列化类版本id*/private static final long serialVersionUID = 6384476034643864037L;private String name;private int age;private Address address;public Student(String name, int age, Address address) {this.name = name;this.age = age;this.address = address;}public String toString() {return "[name:" + name + ";age:" + age + ";address:" + address.toString() + "]";}
}

main方法测试

public class SerializableMain {public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {/*** 序列化 writeObject方法写对象*/Student stu = new Student("zhangsan", 15, new Address("China.Taiwan"));ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("stu.txt"));oos.writeObject(stu);oos.close();}
}

控制台抛异常,Exception in thread "main" java.io.NotSerializableException: com.serialize.Address,异常信息明确Address类没有序列化

Exception in thread "main" java.io.NotSerializableException: com.serialize.Addressat java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)at com.serialize.SerializableMain.main(SerializableMain.java:18)

在给Address类添加序列化属性

public class Address implements Serializable {/*** */private static final long serialVersionUID = 5879834545307777452L;

再通过main方法测试,可以得到序列化之前的对象信息

[name:zhangsan;age:15;address:[provice:China.Taiwan]]

 


http://chatgpt.dhexx.cn/article/wkkKUgjm.shtml

相关文章

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…

MySQL卸载、安装与使用

一、MySQL卸载 1.关闭服务 点击win,输入服务,找到MySQL的服务,右击停止该服务 2.删除服务 点击win,输入cmd,选择管理员身份运行。打开cmd,输入sc delete mysql 3删除配置的MySQL环境变量 MySQLHOME和…

MySQL卸载不干净问题

解决MySQL卸载不干净或安装的时候安装不成功,输入密码一直检查不通过问题 MySQL卸载不干净,之后安装就会遇到很多问题,我也遇到了这种情况,裝了好久,一到这里就会有问题,尝试卸载了很多次然后在从新裝&…