序列化和反序列化为什么要实现Serializable接口?(史上最全、简单易懂)
目录结
前言
最近在阅读别人的源码时,发现很多实体类都实现了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修饰的属性是早于对象存在的,所以不会被序列化
一起加油呀
序列化和反序列化为什么要实现Serializable接口?(史上最全、简单易懂)的更多相关文章
- Java 序列化和反序列化(三)Serializable 源码分析 - 2
目录 Java 序列化和反序列化(三)Serializable 源码分析 - 2 1. ObjectStreamField 1.1 数据结构 1.2 构造函数 2. ObjectStreamClass ...
- Java 序列化和反序列化(二)Serializable 源码分析 - 1
目录 Java 序列化和反序列化(二)Serializable 源码分析 - 1 1. Java 序列化接口 2. ObjectOutputStream 源码分析 2.1 ObjectOutputSt ...
- Java 序列化和反序列化(一)Serializable 使用场景
目录 Java 序列化和反序列化(一)Serializable 使用场景 1. 最简单的使用:Serializable 接口 2. 序列化 ID 的问题 3. 静态字段不会序列化 4. 屏蔽字段:tr ...
- 序列化、反序列化(实体类或要序列化的对象类必须实现Serializable接口)
package com.phone.shuyinghengxie; import java.io.Serializable; /* 一个类的对象要想序列化成功,必须满足两个条件: 该类必须实现 jav ...
- 谈谈序列化—实体bean一定要实现Serializable接口?
导读:最近在做项目的过程中,发现一个问题,就是我们最开始的时候,传递参数包括返回类型,都有map类型.但是由于map每次都要匹配key值,很麻烦.所以在之后就将参数传递和返回类型全都改成了实体bean ...
- 【ASP.NET】DataContract序列化,反序列化对象中包含用接口声明的属性时的处理方法
为此对象添加KnownType属性(Attribute).类型为用接口声明的属性(Property)的所有可能类型. 示例如下: public interface IKey { [DataMembe ...
- 深入分析Java的序列化与反序列化
序列化是一种对象持久化的手段.普遍应用在网络传输.RMI等场景中.本文通过分析ArrayList的序列化来介绍Java序列化的相关内容.主要涉及到以下几个问题: 怎么实现Java的序列化 为什么实现了 ...
- Java 序列化 对象序列化和反序列化
Java 序列化 对象序列化和反序列化 @author ixenos 对象序列化是什么 1.对象序列化就是把一个对象的状态转化成一个字节流. 我们可以把这样的字节流存储为一个文件,作为对这个对象的复制 ...
- java基础( 九)-----深入分析Java的序列化与反序列化
序列化是一种对象持久化的手段.普遍应用在网络传输.RMI等场景中.本文通过分析ArrayList的序列化来介绍Java序列化的相关内容.主要涉及到以下几个问题: 怎么实现Java的序列化 为什么实现了 ...
随机推荐
- 统计学中数据分布的偏度(skewness)和峰度(kurtosis)
- Bellman-Ford算法 求有边数限制的最短路
这个算法也是紧承我们之前讲过的关于图论的内容,我们在前面分析图的时候说过了对于不同的图论问题,我们会有不同的求解方法,那么这里我们讲到Bellman-Ford算法是用于解决有边数限制的求解最短路问题. ...
- Docker——部署常用镜像
部署nginx:暴露端口 #下载nginx docker pull nginx #运行nginx docker run -d --name nginx2 -p 8081:80 nginx -d #后台 ...
- ArcgisServer在Linux系统上更新许可
以下是引用Arcgis知乎的: [解决办法]:步骤1,删除旧许可.具体方法如下:切换至 arcgis server 账户,导航至[ArcGIS for Server 安装目录]/framework/r ...
- luoguP6622 [省选联考 2020 A/B 卷] 信号传递(状压dp)
luoguP6622 [省选联考 2020 A/B 卷] 信号传递(状压dp) Luogu 题外话: 我可能是傻逼, 但不管我是不是傻逼, 我永远单挑出题人. 题解时间 看数据范围可以确定状压dp. ...
- You Don't Know JS Yet Book 1 Notes
Get Started - 前言 But let me be clear: I don't think it's possible to ever fully know JS. That's not ...
- leetcode刷题1--动态规划法回文串2
题目是: Given a string s,partition s such that every substring of the partition is a palindrome Return ...
- 使用postman进行post请求传递中文导致后台接收乱码的问题
1.个人猜测估计是如果header里不指明编码的话,经过tomcat服务器时会导致转换乱码信息,这样就算你在filter里配置了EncodingFilter相关的过滤器也无济于事.. 解决方法就是在h ...
- 什么是可重入锁ReentrantLock?
举例来说明锁的可重入性 public class UnReentrant{ Lock lock = new Lock(); public void outer(){ lock.lock(); inne ...
- spring重点知识分享
前言: spring是一个轻量级的开源的控制反转(Inversion of Control,IOC)和面向切面(AOP)的容器框架,它的主要目的是简化企业开发.这两个模块使得java开发更加简单.IO ...
