概念
创建出来的这些对象都存在于JVM中的堆(heap)内存中,只有JVM处于运行状态的时候,这些对象才可能存在。当JVM停止,这些对象也就随之消失。
java序列化可以帮我们实现:将这些对象持久化,并且在需要的时候重新将对象读取出来
Java序列化是指把Java对象转换为字节序列的过程
,而Java反序列化是指把字节序列恢复为Java对象
的过程:
- 序列化:对象序列化的最主要的用处就是在传递和保存对象的时候,
保证对象的完整性和可传递性
。序列化是把对象转换成有序字节流
,以便在网络上传输或者保存在本地文件中。核心作用是对象状态的保存与重建
。 - 反序列化:客户端从文件中或网络上获得序列化后的对象字节流,
根据字节流中所保存的对象状态及描述信息,通过反序列化重建对象
。
序列化和反序列化的优点
数据持久化:序列化允许将对象的状态保存到文件或数据库中,从而实现数据的持久化。即使程序关闭,数据仍然可以被恢复。
- 将一个已经实例化的类转成文件存储,下次需要实例化的时候只要反序列化即可将类实例化到内存中并保留序列化时类中的所有变量和状态
网络传输/支持分布式系统:通过序列化,可以将对象转换为字节流,方便在网络中传输。接收方可以通过反序列化将字节流转换回对象,简化了数据交换的过程
。
- 在分布式系统中,序列化和反序列化是实现远程方法调用(如 RMI、gRPC 等)的基础,允许不同的服务之间进行对象传递。
跨平台兼容性:序列化后的数据可以在不同的平台和编程语言之间传输。只要遵循相同的序列化协议,数据可以在不同的系统中被正确解析。
- 序列化以后就都是字节流了,无论原来是什么东西,都能变成一样的东西,就可以进行通用的格式传输或保存,传输结束以后,要再次使用,就进行反序列化还原,这样对象还是对象,文件还是文件。
缓存机制:序列化可以用于缓存对象状态,以提高性能。将对象序列化后存储在内存中,可以快速恢复,而无需重新计算。
- 将一个已经实例化的类转成文件存储,下次需要实例化的时候只要反序列化即可将类实例化到内存中并保留序列化时类中的所有变量和状态
易于实现:许多编程语言(如 Java、Python、C# 等)都提供了内置的序列化和反序列化机制,使得开发者可以方便地实现这些功能,而无需编写复杂的代码。
实现Java序列化与反序列化
概述
Java类通过实现java.io.Serialization接口来启用序列化功能,未实现此接口的类将无法将其任何状态或者信息进行序列化或者反序列化
。
注意
- 序列化接口没有方法或者字段,仅用于标识可序列化的语义
当试图对一个对象进行序列化时,如果遇到一个没有实现java.io.Serialization接口的对象时,将抛出NotSerializationException异常
- 如果要序列化的类有父类,要想将在父类中定义过的变量序列化下来,那么父类也应该实现java.io.Serialization接口
序列化保存的是对象的状态,静态变量属于类的状态
,因此序列化并不保存静态变量
。
实现序列化
Serializable
要序列化类,就需要实现 Serializabel接口
import java.io.Serializable;public class Person implements Serializable { //本类可以序列化private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}public String toString() {return "姓名:" + this.name + ",年龄" + this.age;}
}
Externalizable
Externalizable继承了Serializable
,该接口中定义了两个抽象方法:writeExternal()与readExternal()
- 进行序列化与反序列化的时候需要开发人员重写writeExternal()与readExternal()方法
- 如果不重写的话,对类进行序列化然后再进行反序列化之后对象的属性都恢复成了默认值
writeExternal(ObjectOutput out):
- 作用:
该方法用于将对象的状态写入到输出流中。开发者需要在这个方法中实现对象的序列化逻辑,明确指定哪些字段需要被序列化
。 - 参数: ObjectOutput out 是一个输出流,用于写入对象的状态。
readExternal(ObjectInput in)
:
- 作用:该方法用于从输入流中读取对象的状态。开发者需要在这个方法中实现对象的反序列化逻辑,
明确指定如何恢复对象的状态
。 - 参数: ObjectInput in 是一个输入流,用于读取对象的状态。
注意:在使用Externalizable进行序列化的时候,在读取对象时,会调用被序列化类的无参构造器去创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中。所以,实现Externalizable接口的类必须要提供一个public的无参的构造器
。
示例如下:
import java.io.Serializable;public class Person implements Externalizable { //本类可以序列化private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}public String toString() {return "姓名:" + this.name + ",年龄" + this.age;}@Overridepublic void writeExternal(ObjectOutput out) throws IOException {// 必须重写,否则反序列化后会变成默认值,恢复不了之前的内容out.writeObject(name);out.writeInt(age);}@Overridepublic void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {// 必须重写,否则反序列化后会变成默认值,恢复不了之前的内容name = (String)in.readObject();age = in.readInt();}
}
transient
该 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中
,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。
主要特点:
忽略序列化
: 被标记为 transient 的字段在序列化时不会被存储,这对于不需要持久化的敏感信息(如密码)或临时数据(如计算结果)非常有用。默认值
: 在反序列化时,transient 字段会被初始化为其类型的默认值。
示例如下
import java.io.Serializable;public class Person implements Serializable { //本类可以序列化private transient String name;//该值不会被序列化,反序列化后,该值被设为初始值nullprivate int age;public Person(String name, int age) {this.name = name;this.age = age;}public String toString() {return "姓名:" + this.name + ",年龄" + this.age;}
}