前言

最近在阅读别人的源码时,发现很多实体类都实现了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接口?(史上最全、简单易懂)的更多相关文章

  1. Java 序列化和反序列化(三)Serializable 源码分析 - 2

    目录 Java 序列化和反序列化(三)Serializable 源码分析 - 2 1. ObjectStreamField 1.1 数据结构 1.2 构造函数 2. ObjectStreamClass ...

  2. Java 序列化和反序列化(二)Serializable 源码分析 - 1

    目录 Java 序列化和反序列化(二)Serializable 源码分析 - 1 1. Java 序列化接口 2. ObjectOutputStream 源码分析 2.1 ObjectOutputSt ...

  3. Java 序列化和反序列化(一)Serializable 使用场景

    目录 Java 序列化和反序列化(一)Serializable 使用场景 1. 最简单的使用:Serializable 接口 2. 序列化 ID 的问题 3. 静态字段不会序列化 4. 屏蔽字段:tr ...

  4. 序列化、反序列化(实体类或要序列化的对象类必须实现Serializable接口)

    package com.phone.shuyinghengxie; import java.io.Serializable; /* 一个类的对象要想序列化成功,必须满足两个条件: 该类必须实现 jav ...

  5. 谈谈序列化—实体bean一定要实现Serializable接口?

    导读:最近在做项目的过程中,发现一个问题,就是我们最开始的时候,传递参数包括返回类型,都有map类型.但是由于map每次都要匹配key值,很麻烦.所以在之后就将参数传递和返回类型全都改成了实体bean ...

  6. 【ASP.NET】DataContract序列化,反序列化对象中包含用接口声明的属性时的处理方法

    为此对象添加KnownType属性(Attribute).类型为用接口声明的属性(Property)的所有可能类型.  示例如下: public interface IKey { [DataMembe ...

  7. 深入分析Java的序列化与反序列化

    序列化是一种对象持久化的手段.普遍应用在网络传输.RMI等场景中.本文通过分析ArrayList的序列化来介绍Java序列化的相关内容.主要涉及到以下几个问题: 怎么实现Java的序列化 为什么实现了 ...

  8. Java 序列化 对象序列化和反序列化

    Java 序列化 对象序列化和反序列化 @author ixenos 对象序列化是什么 1.对象序列化就是把一个对象的状态转化成一个字节流. 我们可以把这样的字节流存储为一个文件,作为对这个对象的复制 ...

  9. java基础( 九)-----深入分析Java的序列化与反序列化

    序列化是一种对象持久化的手段.普遍应用在网络传输.RMI等场景中.本文通过分析ArrayList的序列化来介绍Java序列化的相关内容.主要涉及到以下几个问题: 怎么实现Java的序列化 为什么实现了 ...

随机推荐

  1. AutoML论文调研

    AutoLearn - Automated Feature Generation and Selection - 2017 ICDM 核心思想: 用特征之间两两回归的方法,发现相关特征的额外信息. 主 ...

  2. 图解|用好MySQL索引,你需要知道的一些事情

    我是蝉沐风. 这一篇文章来聊一聊如何用好MySQL索引. 为了更好地进行解释,我创建了一个存储引擎为InnoDB的表user_innodb,并批量初始化了500W+条数据.包含主键id.姓名字段(na ...

  3. Nginx多个域名配置ssl证书出错解决方案

    解决方案一: 验证通配符 SSL 证书 当涉及通配符 SSL 证书时,NET::ERR_CERT_COMMON_NAME_INVALID 错误会变得稍微复杂一些. 这种类型的证书旨在加密多个子域的数据 ...

  4. web自动化之selenium(三)文件上传

    1.上传标签为input #若上传文件的标签为<input>可以直接定位标签,然后send_keys(文件路径)可以直接上传 2.利用第三方软件Autoit上传 1.下载Autoit:ht ...

  5. Net中异步

    同步和异步1.同步是指只启动一个线程2.异步是指同时启动多个线程3.同步方法会卡界面,异步方法不会卡界面 原因:异步方法启动了子线程执行任务,主线程得到释放4.同步方法会慢.异步方法会快 原因:异步启 ...

  6. 字节跳动流式数据集成基于Flink Checkpoint两阶段提交的实践和优化

    背景 字节跳动开发套件数据集成团队(DTS ,Data Transmission Service)在字节跳动内基于 Flink 实现了流批一体的数据集成服务.其中一个典型场景是 Kafka/ByteM ...

  7. SQL学习日记

    目录 SQL学习日记 1. 常见的数据库对象 2. DDL 定义语句 3. DML 操作语句 4. DQL 查询语句 5. DCL 控制语句 SQL学习日记 1. 常见的数据库对象 对象名 关键字 描 ...

  8. JavaWeb 02_servlet基础

    1. servlet是什么?作用? 1) Servlet 是Sun公司制定的一套技术标准,包含与Web应用相关的一系列接口,是Web应用实现方式的宏观解决方案I而具体的Servlet容器负责提供标准的 ...

  9. isro

    靶机准备 将下载的压缩文件解压,打开vmx文件即可 将网络模式设置为NAT 扫描获得ip:192.168.164.185 kali:192.168.164.137 netdiscover -r 192 ...

  10. VS Code通过code runner插件编译运行多个cpp文件 | 链接编译.h文件

    1.多个cpp文件在同一级目录 参考:https://jingyan.baidu.com/article/2f9b480d7ceb3d01ca6cc224.html 此时可通过修改Code Runne ...