前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >详细聊聊 Java序列化和反序列化的作用

详细聊聊 Java序列化和反序列化的作用

作者头像
HaC
发布2021-03-09 11:09:27
1.3K0
发布2021-03-09 11:09:27
举报
文章被收录于专栏:HaC的技术专栏

1、序列化和反序列化是什么?

如果你看过某些类的源码或者公司的项目,有一些类是实现 Serializable 接口,同时还要显示指定 serialVersionUID 的值。

例如String类:

代码语言:javascript
复制
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -6849794470754667710L;

先来解释一下这两个概念:

  • 序列化:把对象转换为字节序列的过程称为对象的序列化.
  • 反序列化:把字节序列恢复为对象的过程称为对象的反序列化.

太复杂了?

再简单点说:

序列化是指把一个Java对象变成二进制内容,本质上就是一个byte[]数组。

为什么要把Java对象序列化呢?

因为序列化后可以把byte[]保存到文件中,或者把byte[]通过网络传输到远程,这样,就相当于把Java对象存储到文件或者通过网络传输出去了。

有序列化,就有反序列化,即把一个二进制内容(也就是byte[]数组)变回Java对象。有了反序列化,保存到文件中的byte[]数组又可以“变回”Java对象,或者从网络上读取byte[]并把它“变回”Java对象。


2、序列化和反序列化的例子

代码语言:javascript
复制
public class SerializableTest {
      public static void main(String[] args) throws IOException, ClassNotFoundException {
        serializeStudent();
        deserializeStudent();
    }

    //序列化
    static void serializeStudent() throws IOException, ClassNotFoundException {
        FileOutputStream fos = new FileOutputStream("F:\\HaC.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        Student student1 = new Student("HaC", "HelloCoder", 30);
        oos.writeObject(student1);
        oos.flush();
        System.out.println("Student 对象序列化成功!");
        oos.close();
    }

    static void deserializeStudent() throws IOException, ClassNotFoundException {
        //反序列化
        FileInputStream fis = new FileInputStream("F:\\HaC.txt");
        ObjectInputStream ois = new ObjectInputStream(fis);
        Student student2 = (Student) ois.readObject();
        System.out.println(student2.getUserName() + " " +
                student2.getPassword() + " " + student2.getYear());
        System.out.println("Student 对象反序列化成功!");
    }
}
@Data
@AllArgsConstructor
class Student implements Serializable{
    private static final long serialVersionUID = 3608451818006447637L;
    private String userName;
    private String password;
    private String year;
    //省略get、set
}

可以看到生成了一个打开是乱码的二进制文件:

其实这个例子就是序列化和反序列化的一个小过程,JVM通过序列化把对象写到文件,再通过反序列化从文件中读取数据,把数据转成一个对象。

看到控制台输出也是正常的:

代码语言:javascript
复制
Student 对象序列化成功!
HaC HelloCoder 30
Student 对象反序列化成功!

IDEA可以设置生成 serialVersionUID:

然后双击选中你的类,按下 Alt + Enter

作用

所以序列化和反序列化的作用是:

1、把对象转成JSON、xml 的时候,往往这些接口、方法 都实现了序列化,因为网络传输也是一个二进制的过程,需要进行转换

所以只要我们对内存中的对象进行持久化或网络传输, 这个时候都需要序列化和反序列化.

2、还有一个作用就是把对象的字节序列永久地保存到硬盘上 ,比如通过mybatis可持久化到MySQL,也是实现了序列化的。

比如:

代码语言:javascript
复制
<insert id="insertUser" parameterType="org.yudianxx.bean.User">
    INSERT INTO user(name, age) VALUES (#{name}, #{age})
</insert>

实际上我们并不是将整个对象持久化到数据库中, 而是将对象中的属性持久化到数据库中, 而这些属性都是实现了 Serializable 接口的基本属性。

3、为什么要实现 Serializable 接口还要指定serialVersionUID的值?

在 Java 中实现了 Serializable 接口后, JVM 会在底层帮我们实现序列化和反序列化,如果你实现该接口,你也可以自己自定义一个,就是有点复杂,这里不展开。

如果不显示指定 serialVersionUID, JVM 在序列化时会根据属性自动生成一个 serialVersionUID, 然后与属性一起序列化,再进行持久化或网络传输。

在反序列化时,JVM 会再根据属性自动生成一个新版 serialVersionUID,然后将这个新版 serialVersionUID 与序列化时生成的旧版 serialVersionUID 进行比较,如果相同则反序列化成功, 否则报错.

如果显示指定了 serialVersionUID, JVM 在序列化和反序列化时仍然都会生成一个 serialVersionUID, 但值为我们显示指定的值,这样在反序列化时新旧版本的 serialVersionUID 就一致了.

接上面的例子,我不生成serialVersionUID,反序列化也是没有问题的,但如果我指定了不一致的serialVersionUID ,或者加了新的属性:

代码语言:javascript
复制
class Student implements Serializable {
   // private static final long serialVersionUID = 1L;
    private String userName;
    private  String password;
    private  int year ;
    private  int age;

再调用deserializeStudent() 反序列化方法时就会报错:

代码语言:javascript
复制
Exception in thread "main" java.io.InvalidClassException: com.yudianxx.basic.序列化.Student; local class incompatible: stream classdesc serialVersionUID = -2548470143096162701, local class serialVersionUID = -9192788448472055372

可以看到有一个默认的serialVersionUID

还有一种情况就是,假如你不实现Serializable接口,在反序列化的时候也是会报错的:

代码语言:javascript
复制
class Student {
    private String userName;
    private  String password;
    private  int year ;
}

报错NotSerializableException

代码语言:javascript
复制
Exception in thread "main" java.io.NotSerializableException: com.yudianxx.basic.序列化.Student

通过这个例子你就大概知道,反序列化是和类和属性有关,就像秘钥和公钥一样,只有正确的serialVersionUID和类匹配,才能反序列化。

3、序列化的其他特性

1、static 属性不会被序列化

2、transient 修饰的属性,也不会被序列化

当某个字段被声明为transient后,默认序列化机制就会忽略该字段

这里我举个例子:

代码语言:javascript
复制
class Student implements Serializable {
    private static final long serialVersionUID = 3608451818006447637L;
    private String userName;
    private static String password = "123456";
    private transient int year = 24;
    //省略get set
}

我先调用serializeStudent() 进行序列化。

代码语言:javascript
复制
        serializeStudent();
//        deserializeStudent();

然后再调用deserializeStudent()

代码语言:javascript
复制
//        serializeStudent();
        deserializeStudent();

可以看到输出:

代码语言:javascript
复制
HaC 123456 0
Student 对象反序列化成功!

在序列化写入文件的时候是 Student student1 = new Student("HaC", "HelloCoder", 30);

反序列化就有问题。在序列化时,因为它不会把序列化,所以反序列化只能拿到默认定义的值。

4、结论

1、网络传输、对象转换一定要使用序列化

2、实现这个Serializable 接口的时候,一定要给这个 serialVersionUID 赋值

3、static 、transient 修饰的属性不会反序列化。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/03/08 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、序列化和反序列化是什么?
  • 2、序列化和反序列化的例子
    • 作用
    • 3、为什么要实现 Serializable 接口还要指定serialVersionUID的值?
    • 3、序列化的其他特性
      • 1、static 属性不会被序列化
        • 2、transient 修饰的属性,也不会被序列化
        • 4、结论
        相关产品与服务
        文件存储
        文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档