Serializable接口的作用

Serializable接口的作用

 

 

 

1、什么是序列化与反序列化?
    序列化: 1、序列化就是将对象属性转变为二进制数据。2、在网络上进行传输。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。把对象转换为字节序列的过程称为对象的序列化,把字节序列恢复为对象的过程称为对象的反序列化。

 

2、Serializable 简介

     实现java.io.Serializable 接口的类是可序列化的,这个序列化接口没有任何方法和域,仅用于标识序列化的语意。 没有实现此接口的类将不能使它们的任意状态被序列化或逆序列化。这个接口其实是个空接口,那么这个序列化操作,到底是由谁去实现了呢?其实,看一下接口的注释说明就知道,当我们让实体类实现Serializable接口时,其实是在告诉JVM此类可被序列化,可被默认的序列化机制序列化。

public interface Serializable { }

 

3、Serializable 序列化测试

3.1、新建序列化测试类

/**
 * @Author dw
 * @ClassName SerializableTest
 * @Description
 * @Date 2023/1/2 15:08
 * @Version 1.0
 */
public class SerializableTest {

    public static void main(String[] args) {
        // 初始化
        User user = new User();
        user.setName("王二");
        user.setAge(18);
        System.out.println(user);

        // 把对象写到文件中
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.txt"))){
            oos.writeObject(user);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 从文件中读出对象
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("user.txt")))){
            User userRead = (User) ois.readObject();
            System.out.println(userRead);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static class User {
        private String name;

        private Integer age;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Integer getAge() {
            return age;
        }

        public void setAge(Integer age) {
            this.age = age;
        }
    }
}

  由于 User没有实现 Serializable 接口,所以系统会报错。运行后报错如下:

 

 顺着堆栈信息,我们来看一下 ObjectOutputStream 的 writeObject0 () 方法。其部分源码如下:

   这段代码的意思是,ObjectOutPutStream 在序列化的时候,会判断对象的类型,如果不是字符串、数组、枚举、Serializable 的实例,会抛出 NotSerializableException。但是,如果 SClass 实现了 Serializable 接口的话,就可以被序列化和反序列化了。

 

3.2、修改User类实现Serialiable接口。

 public static class User implements Serializable{
        private static final long serialVersionUID = -9085952353374185653L;
        private String name;

        private Integer age;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Integer getAge() {
            return age;
        }

        public void setAge(Integer age) {
            this.age = age;
        }
    }

再次测试, 序列化成功。

 

3.3、反序列化测试:修改User类中的序列化id, 测试能否序列化成功

public static class User implements Serializable{
        private static final long serialVersionUID = -9085952353374185654L;
          ...
}

提示如下错误:

com.dw.study.test.SerializableTest$User@5f184fc6
java.io.InvalidClassException: com.dw.study.test.SerializableTest$User; 
local class incompatible: stream classdesc serialVersionUID = -9085952353374185654, local class serialVersionUID = -9085952353374185655 at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699) at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1885) at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1751) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2042) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431) at com.dw.study.test.SerializableTest.main(SerializableTest.java:31)
serialVersionUID 被称为序列化 ID,它是决定 Java 对象能否反序列化成功的重要因子。在反序列化时,Java 虚拟机会把字节流中的 serialVersionUID 与被序列化类中的 serialVersionUID 进行比较,如果相同则可以进行反序列化,否则就会抛出序列化版本不一致的异常。
 
3.4、反序列化测试序列化前后的对象名称不一致。
// 序列化前的对象名称
 public static class User implements Serializable{}

// 序列化后的对象名称
 public static class MyUser implements Serializable{}

反序列化结果:

 提示User类未找到。 

 

4、serialversionuid 默认1L和64位的哈希字段有什么区别?

2.有两种生成方式:

一个是默认的1L,比如:private static final long serialVersionUID = 1L;

一个是根据类名等来生成一个64位的哈希字段

由此可以证明,Java 虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,还有一个非常重要的因素就是序列化 ID 是否一致。

也就是说,如果没有特殊需求,采用默认的序列化 ID(1L)就可以,这样可以确保代码一致时反序列化成功。

 

 

6、总结

1. static 和 transient 修饰的字段是不会被序列化的。因为序列化保存的是对象的状态,而 static 修饰的字段属于类的状态,因此可以证明序列化并不保存 static 修饰的字段。

transient 的中文字义为 “临时的”(论英语的重要性),它可以阻止字段被序列化到文件中,在被反序列化后,transient 字段的值被设为初始值,比如 int 型的初始值为 0,对象型的初始值为 null。

2. serialVersionUID 被称为序列化 ID,它是决定 Java 对象能否反序列化成功的重要因子。在反序列化时,Java 虚拟机会把字节流中的 serialVersionUID 与被序列化类中的 serialVersionUID 进行比较,如果相同则可以进行反序列化,否则就会抛出序列化版本不一致的异常。

 Java大部分的数据类型都已经实现了可序列化接口。

 

posted @ 2023-01-02 16:13  邓维-java  阅读(4434)  评论(0编辑  收藏  举报