序列化和反序列化为什么要实现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的序列化 为什么实现了 ...
随机推荐
- python关于openpyxl库的常用使用介绍
from openpyxl import load_workbook #只能加载已存在的表格 wb=load_workbook("D:\zhijing_work\测试数据\测试文件\yeta ...
- 合并两个以单链表形式表示的关于x的多项式(基于c语言)
只写函数内部的,不懂得可以看前面一篇文章对链表的实现: pLinklist addBothLinklist(Linklist* first,Linklist* second){ Linklist *n ...
- Linux----虚拟机克隆、快照、删除、
克隆 已经安装一台linux系统 还想要更多的,直接克隆CentOS即可 使用vm ware 的克隆操作 注意: 使用前先关闭目前已开启的虚拟机 快照 作用: 虚拟系统出现异常,需要回到原先的状态,此 ...
- Docker——容器数据卷
为什么需要容器数据卷 角度:遇到问题,尝试以朴素的道理解决问题.问题复杂化,解决的方式也变得复杂 问题的提出:docker将应用和环境打包成一个镜像,但是对于容器内的数据,如果不进行外部的保存,那么当 ...
- Ubuntu20安装nodejs和npm并切换阿里源
参考 阿里巴巴开源镜像站 Ubuntu20安装npm并切换阿里源 安装直接在终端执行 sudo apt-get install nodejs npm没有安装上就执行 sudo apt-get inst ...
- toppo-1
靶机准备 由于是.vmdk文件,新建一个虚拟机把硬盘移除,在将此vmdk文件添加为新硬盘即. 开机发现给出了ip地址:192.168.164.184,且当前网络模式为NAT 将kali也设置为NAT模 ...
- 洛谷 P1020 [NOIP1999 普及组] 导弹拦截
Coidng #include <iostream> #include <algorithm> #include <cstring> #include <ve ...
- sqlserver下载地址及密匙
SqlServer 2017 下载地址及密钥 下载地址: ed2k://|file|cn_sql_server_2017_developer_x64_dvd_11296175.iso|17697771 ...
- Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?
启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解: @SpringBootConfiguration:组合了 ...
- promethues常用的函数
prometheus函数常用 时数据 (Instant vector): 包含一组时序,每个时序只有一个点,例如:http_requests_total区间数据 (Range vector): 包含一 ...
