什么是序列化
序列化分为序列化和反序列化。把对象转换为字节序列的过程称为对象序列化,把字节序列转化回对象的过程称为对象反序列化。
因此我们可以把对象从内存中序列化为字节序列然后存储到硬盘中,而后再将字节序列从硬盘读入内存后反序列化为对象。
这个过程都是JVM独立操作的,也就是说,在一个平台序列化的对象可以在另一个平台上反序列化该对象。
什么时候用序列化
-
把内存中的对象当前状态保存到一个文件或者数据库中时;
-
把对象用套接字在网络中传输时;
序列化怎么使用
序列化的使用非常简单,只用实现java.io.Serializable接口即可。
类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义
我们来看看效果:
我们先写一个User类
public class User implements Serializable {
//序列化ID 后面再说这个用处
private static final long serialVersionUID = -6627166148134959586L;
private Integer id;
private String name;
private Date birthday;
public User() {
}
public User(Integer id, String name, Date birthday) {
this.id = id;
this.name = name;
this.birthday = birthday;
}
//...Getter&Setter&toString
}
接着在测试类中测试:
public class Main {
public static void main(String[] args) {
f1();//先将对象序列化
f2();//再将对象反序列化
}
//反序列化
public static void f2() {
try {
File file = new File("E://User.txt");
InputStream inputStream = new FileInputStream(file);
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
User user = (User) objectInputStream.readObject();
System.out.println(user);//反序列化后输出user对象
System.out.println("反序列化成功");
} catch (Exception e) {
e.printStackTrace();
}
}
//序列化
public static void f1() {
User user = new User(7, "James", new Date());
try {
File file = new File("E://User.txt");
OutputStream outputStream = new FileOutputStream(file);
ObjectOutputStream objectInputStream = new ObjectOutputStream(outputStream);
objectInputStream.writeObject(user);
System.out.println("序列化成功");
} catch (Exception e) {
e.printStackTrace();
}
}
}
//最后输出
序列化成功
User{id=7, name='James', birthday=Sun Apr 07 11:00:51 CST 2019}
反序列化成功
运行完f1()方法后E盘下将生成一个User.txt,就是user对象序列化后的结果。
运行f2()将对象反序列化回来输出user对象。
java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
只有实现了Serializable和Externalizable接口的类的对象才能被序列化。Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以采用默认的序列化方式 。
serialVersionUID的作用
序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。如果接收者加载的该对象的类的 serialVersionUID 与对应的发送者的类的版本号不同,则反序列化将会导致 InvalidClassException
。
也就是说,serialVersionUID就是序列化文件的版本号(或者叫做标识符),用来验证类是否一致。
假设我们没有设置serialVersionUID;当我们修改了User类(例如添加和修改了某些属性)。
这里我先
- 执行 f1();完成序列化;
- 修改User类, 将birthday属性去掉,
- 执行f2();完成反序列化,
- 就会报以下错误:
java.io.InvalidClassException: serializable.User; local class incompatible:
stream classdesc serialVersionUID = 7499957730358482013, local class serialVersionUID = 5048640181306359140
当我们没有设置serialVersionUID时候,他会默认提供一个serialVersionUID,也就是 7499957730358482013,如果我们不修改类,这个值是不变的,当我们修改了类之后,serialVersionUID变为5048640181306359140 与User.txt文件里的7499957730358482013不一致,就导致了错误。
当我们设置了serialVersionUID,就不会出现这些问题。
总结:
要实现序列化,需要实现Serializable接口即可。
serialVersionUID的取值默认是根据类的内容自动生成的。对源代码进行修改后,也会影响serialVersionUID的取值。这里便有了两种用途:
- 一是希望类的不同版本对序列化兼容,这时我们需要确保不同类有着相同的serialVersionUID,即我们手动指定相同serialVersionUID;
- 二是不希望类的不同版本对序列化兼容(出于安全考虑),这时我们需要确保不同的类有着不同的serialVersionUID,即我们不手动指定serialVersionUID,或者手动指定不同的serialVersionUID
面试题:
什么是JAVA序列化?
对象转换为字节序列的过程称为对象序列化,把字节序列转化回对象的过程称为对象反序列化。我们有时候需要将对象进行网络传输或者保存到本地时候,传输对象显然是不合适的,这时候我们需要把对象变成字节序列(流)的形式,就是序列化的过程,或者需要把接收到的字节序列转换回对象,这就是反序列化过程。
如何实现序列化?
要实现序列化,只需要实现Serializable接口即可。Serializable接口是一个没有方法或字段的接口,仅用于表示可序列化的语义。
请解释Serializable接口的作用?
类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化Serializable 接口没有方法或字段,仅用于标识可序列化的语义。