private static final long serialVersionUID = -671958543348052007L; // 防止因为修改类属性导致反序列化失败,最好设置一个serialVersionUID。
什么是 Serializable接口?
Serializable 接口是 Java 编程语言中的一个接口,用于标识类的对象可以被序列化(Serialization)。
序列化和反序列化
- 序列化:是将对象转换为字节流或文本格式,以便在网络上传输、存储到文件中或在不同的 Java 虚拟机之间进行通信。
- 反序列化:是将序列化后的数据重新转换为对象的过程,以便在程序中使用。
白话就是:Serializable序列化的标记
为什么需要Serializable接口?(可不用记忆)
Serializable 接口提供了一种标准化的机制,使得Java对象可以在不同的环境中进行序列化和反序列化操作,实现对象的持久化、数据传输和远程方法调用等功能。
- 对象持久化:通过将对象序列化为字节流,可以将对象保存到文件系统、数据库或其他持久化存储介质中。这样,在程序重新启动后,可以从存储介质中读取序列化的对象,并将其反序列化为内存中的对象,恢复对象的状态和数据。
- 对象传输:在分布式系统或网络通信中,需要将对象从一个节点传输到另一个节点。通过序列化对象,可以将对象转换为字节流,在网络中传输,并在接收端进行反序列化,重新创建对象。这样,可以方便地在不同的计算机或不同的 Java 虚拟机之间传递对象。
- 远程方法调用(RPC):在远程方法调用中,客户端和服务器之间需要传递参数和返回值。通过序列化和反序列化,可以将参数和返回值转换为字节流,在网络中传输。这样,可以实现跨网络的方法调用,使得客户端可以调用位于远程服务器上的方法,并获取返回结果。
- 对象复制:有时候需要创建一个对象的副本,可以通过序列化和反序列化来实现。通过将对象序列化为字节流,然后反序列化为另一个对象,可以在内存中创建一个与原始对象相同的副本。
上代码实验
├── Main.java
└── entity
└── User.java
定义一个User类
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* @author : zanglikun
* @date : 2024/6/12 下午4:13
* @desc : Copyright © zanglikun.com
*/
@Data // 自己引入Lombok哦,不引入自己手写构造Get/Set方法
@AllArgsConstructor
public class User {
private String userName;
}
创建一个Main类,并实现:将User新对象序列化一下到本地,同时反序列化一下。
import entity.User;
import lombok.SneakyThrows;
import java.io.*;
/**
* @author : zanglikun
* @date : 2024/6/12 下午4:13
* @desc : Copyright © zanglikun.com
*/
public class Main {
private static String filePath = "序列化学习.txt";
@SneakyThrows
public static void main(String[] args) {
User user = new User("张三");
serialize(user); // 序列化到本地文件
deserialization(); // 将本地文件发序列化
}
private static void serialize(User user) throws IOException {
// 将 User 对象序列化到文件中
try (FileOutputStream fos = new FileOutputStream(filePath)) {
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(user);
oos.close();
}
}
private static void deserialization() throws IOException, ClassNotFoundException {
// 从文件中反序列化 User 对象
try (FileInputStream fis = new FileInputStream(filePath)) {
ObjectInputStream ois = new ObjectInputStream(fis);
User deserializedUser = (User) ois.readObject();
System.out.println("反序列化后的用户名:" + deserializedUser.getUserName());
ois.close();
}
}
}
会报错
Exception in thread "main" java.io.NotSerializableException: entity.User
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at Main.serialize(Main.java:25)
at Main.main(Main.java:16)
修改User 添加:implements Serializable
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.Serializable;
/**
* @author : zanglikun
* @date : 2024/6/12 下午4:13
* @desc : Copyright © zanglikun.com
*/
@Data // 自己引入Lombok哦,不引入自己手写构造Get/Set方法
@AllArgsConstructor
public class User implements Serializable {
private String userName;
}
成功
反序列化后的用户名:张三
进程已结束,退出代码为 0
本次给User添加一个age参数
public class User implements Serializable {
private String userName;
private String age;
}
注释序列化代码,只执行反序列化代码
@SneakyThrows
public static void main(String[] args) {
// User user = new User("张三");
// serialize(user); // 序列化到本地文件
deserialization(); // 将本地文件发序列化
}
Exception in thread "main" java.io.InvalidClassException: entity.User; local class incompatible: stream classdesc serialVersionUID = 1874829408147107284, local class serialVersionUID = -4123255951891179523
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2005)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1852)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2186)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1669)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:503)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:461)
at Main.deserialization(Main.java:36)
at Main.main(Main.java:19)
因为:序列化的时候 serialVersionUID 是 JVM 自动根据 class的信息生成的,你现在User类的信息变量,就会导致 serialVersionUID 发生变化!当然反序列化检查 serialVersionUID 是否一致的时候,肯定不认了,就抛出异常:InvalidClassException 因为:序列化的时候 serialVersionUID 是JVM自动根据class的信息生成的,你现在User类的信息变量,就会导致 serialVersionUID 发生变化!当然反序列化检查 serialVersionUID 是否一致的时候,肯定不认了,就抛出异常:InvalidClassException
所以我们一开始序列化前,就需要显式指定 serialVersionUID ,这样在我们修改类信息的时候,JVM反序列化读取 serialVersionUID 一模一样 的就不会有问题!
那我们知道后,序列化、反序列化的时候需要操作什么?
在需要反序列化的实体类上,加上 implements Serializable
同时 在被反序列化的属性上加上
private static final long serialVersionUID = -671958543348052007L;
抛出疑问?
Java前后端交互为啥没加serialVersionUID,也没走序列化?
在Java中,如果需要将对象序列化为字节流或文本格式以便传输或存储,通常会实现 Serializable
接口,并且可以定义 serialVersionUID
来确保序列化和反序列化的兼容性。
在Spring项目中,当后端将Java对象转换为JSON字符串返回给前端时,通常不需要显式实现 Serializable
接口。这是因为在这种情况下,Java对象并不是被序列化为字节流,而是被转换为JSON字符串,这个过程是由JSON库(比如Jackson)来处理的,而不是Java的序列化机制。因此,虽然有数据转换的过程,但并不涉及Java对象的标准序列化和反序列化过程。
Jackson等序列化库实现的不是标准的序列化
在使用Jackson(或其他类似的库,比如Gson)将Java对象转换为JSON字符串时,实际上会涉及到一种形式的序列化过程,但这与Java标准序列化机制是有区别的。
在这种情况下,Jackson库会通过反射等机制检查Java对象的结构,并将其转换为对应的JSON字符串。这种序列化过程是为了将Java对象的数据结构转换为JSON格式,以便于在网络上传输或存储到文件中。
这种序列化过程与Java标准的序列化机制(实现 Serializable
接口)有所不同。Java标准的序列化机制是将对象转换为字节流,以便在Java环境中进行传输或持久化。而Jackson的序列化是将对象转换为JSON字符串,用于在不同系统之间传输数据。
因此,虽然Jackson的序列化过程确实涉及将Java对象转换为另一种格式(JSON字符串),但它并不使用Java标准的序列化机制。Jackson库是专门用于处理JSON数据的,它提供了一种更灵活和定制化的方式来转换Java对象和JSON数据之间的关系。
IDEA 设置创建类自动生成Serializable接口与serialVersionUID
第三方平台不会及时更新本文最新内容。如果发现本文资料不全,可访问本人的Java博客搜索:标题关键字。以获取最新全部资料 ❤
评论(0)