前言

最近在阅读别人的源码时,发现很多实体类都实现了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. Python之简单的用户名密码验证

    题目要求: 输入用户名密码 认证成功后显示欢迎信息 输错三次后锁定   #要求使用文件存储用户名和密码,每次从文件读入用户名和密码用来验证,如果输错三次密码该账户会被锁定,并讲锁定的用户名写入文件 # ...

  2. 给Windows系统免疫不再受恶意代码骚扰

    所谓恶意代码,是指网页中使用了利用WSH漏洞来修改系统的一段代码(但是由于它并不具备传染性和自我复制这两个病毒的基本特征,因此不能称作病毒).WSH是"Windows Scripting H ...

  3. python监控cpu 内存实现邮件微信报警

    # qianxiao996精心制作 #博客地址:https://blog.csdn.net/qq_36374896 import psutil, time,smtplib,socket import ...

  4. 线程池提交任务时submit()和execute()的区别

    因为之前一直是用的execute方法,最近有个情况需要用到submit方法,所以研究了下. 他们的区别: 1.execut()可以添加一个Runable任务,submit()不仅可以添加Runable ...

  5. 摆烂期的Android学习笔记一

    Android大致分为四层架构1.Linux内核层:提供各种硬件驱动,如显示驱动,音频驱动,相机驱 动,蓝牙驱动.... 2.系统运行库层:通过C/c++库为android地图提供支持 3.应用框架层 ...

  6. z-index原理及适用范围

    z-index原理及适用范围 原理 z-index这个属性控制着元素在z轴上的表现形式,堆叠顺序是当前元素位于z轴上的值,数值越大说明元素的堆叠1顺序越高,越靠近屏幕. 适用范围 <div cl ...

  7. Kafka 分区的目的?

    分区对于 Kafka 集群的好处是:实现负载均衡.分区对于消费者来说,可以提高并发度,提高效率.

  8. 什么是可重入锁ReentrantLock?

    举例来说明锁的可重入性 public class UnReentrant{ Lock lock = new Lock(); public void outer(){ lock.lock(); inne ...

  9. JS字符串格式化

    //字符串格式化String.prototype.format = function () { var values = arguments; return this.replace(/\{(\d+) ...

  10. Django用mongoengine操作mongodb

    最近在写Django项目,使用的是mongodb,虽然Django不支持NoSQL,但是mongoengine可以帮助我们像ORM一样在Django项目中去操作mongodb 推荐一个可视化工具  R ...