目录结🐕
前言
最近在阅读别人的源码时,发现很多实体类都实现了Serializable接口,而且还指定了serialVersionUID,心中不免有些疑问(说实话是因为自己菜才有疑问😀)
Ⅰ.序列化和反序列化到底是什么?
Ⅱ.到底什么时候需要进行序列化和反序列化?
Ⅲ.为什么实现Serializable接口就能实现序列化和反序列化呢?
Ⅳ.为什么实现了Serializable接口还要指定serialVersionUID的值呢?
1、什么是序列化和反序列化
序列化:将Java对象转换为字节序列的过程
反序列化:将字节序列恢复成Java对象的过程
2、什么时候需要进行序列化和反序列化
以下几点:将内存中的数据持久化到磁盘或者数据库时、浏览器与服务器交互时。
简单了说就是当我们持久化内存中的数据或者进行网络数据传输的时候需要进行序列化。(也就是保存数据),反序列化肯定就相反了。
2.1、服务器和浏览器交互时用到了Serializable接口吗?
我们知道服务端在与浏览器交互时,返回的是一个json格式的数据,而json格式本质就是字符串类型;下面是String类的源码
可以看出其String类源码实现了Serializable接口,并定义了serialVersionUID
2.2、Mybatis将数据持久化到数据库中用到了Serializable接口吗?
我们来看mybatis映射文件中的插入这条语句
<insert id="addUser" parameterType="User">
insert into user
(username,sex,address)
values(#{username},#{sex},#{address})
</insert>
从表面我们看不出什么地方实现类序列化接口,其实并不是将User对象持久化到数据库,而是将对象中的属性持久化到数据库中,而这些属性都实现了Serializable接口。
3、为什么实现了Serializable接口就能序列化和反序列化呢?
查看Serializable接口源代码
可以发现接口中什么也没有,这其实是Java中的标识语义,用来告诉JVM帮我在底层进行序列化和反序列化。如果不实现这个接口,就只能自己编写序列化和反序列化代码了呗,至于具体怎么写,找度娘呗😁
4、为什么实现类Serializable接口还要指定serialVersionUID的值呢?
1->2(序列化->反序列化)
1.序列化时:如果不指定,JVM会帮我们根据类中的属性生成一个serialVersionUID,然后进行序列化,最后进行持久化或者数据传输;
2.反序列化时:JVM会根据属性生成一个新的serialVersionUID,然后将这个新的serialVersionUID与序列化时的serialVersionUID进行比对,如果相同,则反序列化成功,否则失败!
当我们指定了serialVersionUID时,JVM也会生成一个serialVersionUID,但它会将我们指定的serialVersionUID赋给生成的,这样就保证了UID一致性。
4.1、如果不指定serialVersionUID会出现什么问题呢?
1.假设某个项目,实体类User中实现了Serializable接口,没有指定serialVersionUID;
2.假设JVM根据User类属性生成的UID为A;
3.当公司进行版本升级时,如果需要加入新的属性,那么此时JVM根据属性生成的UID为B,那么此时A和B不相同,就会导致出现反序列化失败。类属性不同,那么生成的serialVersionUID肯定就不一样了😀
4.2、代码演示
首先不得不提一下ObjectOutputStream和ObjectInputStream类(对象流),主要使用writeObject和readObject方法,完成流的写入与读取;
1.要求该对象的所以属性是可序列化的,如果是不可序列化的,必须是瞬态的,使用transient修饰;
2.该类的属性是可访问的(public、protected)或者有set和get方法用来恢复状态。
1.User类
package com.zsh;
import java.io.Serializable;
/**
* @author:抱着鱼睡觉的喵喵
* @date:2021/3/31
* @description:
*/
public class User implements Serializable {
private String username;
private String password;
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
2.测试类
package com.zsh;
import java.io.*;
/**
* @author:抱着鱼睡觉的喵喵
* @date:2021/3/31
* @description:
*/
public class TestSerializable {
public static void main(String[] args) throws IOException, ClassNotFoundException {
User user = new User();
user.setUsername("Ronin");
user.setPassword("123");
serialize(user);
User user2 = deserialize();
System.out.println(user2);
}
public static void serialize(User user) throws IOException {
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(new File("F:\\a.txt")));
os.writeObject(user);
os.close();
}
public static User deserialize() throws IOException, ClassNotFoundException {
ObjectInputStream oi = new ObjectInputStream(new FileInputStream(new File("F:\\a.txt")));
return (User)oi.readObject();
}
}
3.结果:
重点来了
当我在User类中新增一个属性title
package com.zsh;
import java.io.Serializable;
/**
* @author:抱着鱼睡觉的喵喵
* @date:2021/3/31
* @description:
*/
public class User implements Serializable {
private String username;
private String password;
private String title;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", title='" + title + '\'' +
'}';
}
}
关掉测试类中的序列化方法
package com.zsh;
import java.io.*;
/**
* @author:抱着鱼睡觉的喵喵
* @date:2021/3/31
* @description:
*/
public class TestSerializable {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// User user = new User();
// user.setUsername("Ronin");
// user.setPassword("123");
// serialize(user);
User user2 = deserialize();
System.out.println(user2);
}
// public static void serialize(User user) throws IOException {
// ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(new File("F:\\a.txt")));
// os.writeObject(user);
// os.close();
// }
public static User deserialize() throws IOException, ClassNotFoundException {
ObjectInputStream oi = new ObjectInputStream(new FileInputStream(new File("F:\\a.txt")));
return (User)oi.readObject();
}
}
发现出现了序列化和反序列化serialVersionUID不同的情况,由于我们加入了属性,导致反序列化时JVM根据属性生成的UID和序列化时的不同。
当我加入自定义serialVersionUID时,去掉title属性
package com.zsh;
import java.io.Serializable;
/**
* @author:抱着鱼睡觉的喵喵
* @date:2021/3/31
* @description:
*/
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
public class TestSerializable {
public static void main(String[] args) throws IOException, ClassNotFoundException {
User user = new User();
user.setUsername("Ronin");
user.setPassword("123");
serialize(user);
User user2 = deserialize();
System.out.println(user2);
}
public static void serialize(User user) throws IOException {
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(new File("F:\\a.txt")));
os.writeObject(user);
os.close();
}
public static User deserialize() throws IOException, ClassNotFoundException {
ObjectInputStream oi = new ObjectInputStream(new FileInputStream(new File("F:\\a.txt")));
return (User)oi.readObject();
}
}
结果:
当我加入新的属性title时
package com.zsh;
import java.io.Serializable;
/**
* @author:抱着鱼睡觉的喵喵
* @date:2021/3/31
* @description:
*/
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String username;
private String password;
private String title;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", title='" + title + '\'' +
'}';
}
}
package com.zsh;
import java.io.*;
/**
* @author:抱着鱼睡觉的喵喵
* @date:2021/3/31
* @description:
*/
public class TestSerializable {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// User user = new User();
// user.setUsername("Ronin");
// user.setPassword("123");
// serialize(user);
User user2 = deserialize();
System.out.println(user2);
}
// public static void serialize(User user) throws IOException {
// ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(new File("F:\\a.txt")));
// os.writeObject(user);
// os.close();
// }
public static User deserialize() throws IOException, ClassNotFoundException {
ObjectInputStream oi = new ObjectInputStream(new FileInputStream(new File("F:\\a.txt")));
return (User)oi.readObject();
}
}
结果:
由于我自定义了serialVersionUID,那么序列化和反序列化UID肯定就一样了😀
当我使用transient关键字修饰title属性时
package com.zsh;
import java.beans.Transient;
import java.io.Serializable;
/**
* @author:抱着鱼睡觉的喵喵
* @date:2021/3/31
* @description:
*/
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String username;
private String password;
private transient String title;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", title='" + title + '\'' +
'}';
}
}
测试类如下
package com.zsh;
import java.io.*;
/**
* @author:抱着鱼睡觉的喵喵
* @date:2021/3/31
* @description:
*/
public class TestSerializable {
public static void main(String[] args) throws IOException, ClassNotFoundException {
User user = new User();
user.setUsername("Ronin");
user.setPassword("123");
user.setTitle("序列化与反序列化transient关键字的作用");
serialize(user);
User user2 = deserialize();
System.out.println(user2);
}
public static void serialize(User user) throws IOException {
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(new File("F:\\a.txt")));
os.writeObject(user);
os.close();
}
public static User deserialize() throws IOException, ClassNotFoundException {
ObjectInputStream oi = new ObjectInputStream(new FileInputStream(new File("F:\\a.txt")));
return (User)oi.readObject();
}
}
结果:
可以发现title属性并没有被序列化
所以transient关键字的作用是:让属性不被序列化
5、除了transient,static修饰的也不会被序列化
因为序列化是针对对象而言的,而static修饰的属性是早于对象存在的,所以不会被序列化
一起加油呀😀😀
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,携手博客园推出1Panel与Halo联合会员
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步