Java - 序列化、反序列化及Serializable接口

tech2022-07-13  163

一 序列化 & 反序列化

    1、序列化:把对象转换为字节序列的过程称为对象的序列化

    2、反序列化:把字节序列恢复为对象的过程称为对象的反序列化.

二 什么场景会涉及序列化 & 反序列化

    1、持久化内存空间中的数据至 物理磁盘 或 数据库 以便长期保存时;

    2、前后端数据交互;

    3、两个进程间的远程通信调用 eg:RPC框架;

    补充说明第二点:服务器与浏览器间数据交互时也会存在序列化的过程,Json实际上就是将一个对象转换为字符串String再与前端交互,而 String 源码中也实现了 Serializable 接口

三 Serializable接口

Java通过  java.io.Serializable 接口实现序列化功能, java.io.Serializable 接口中不提供任何方法或字段,仅用来标识序列化的语义,JVM会在底层帮我们实现序列化和反序列化。未实现此接口的类则无法将其进行序列化或反序列化,序列化功能支持扩展至子类,即只需父类实现了序列化接口,所有子类就可以进行序列化或反序列化。

当对一个未实现序列化接口的类进行序列化时会抛出 java.io.NotSerializableException;

四  serialVersionUID

序列化版本号,JVM在对类进行序列化时会根据类名、接口名、方法名、属性等自动生成 serialVersionUID,然后与序列化后的属性一起进行持久化或网络传输,在反序列化时JVM按照同样的规则再次生成一个 serialVersionUID 并与序列化时生成的serialVersionUID 做对比,如果两者一致则反序列化成功。

如果未显式指定 serialVersionUID,在对类序列化成功后如果新增或修改了类的属性,则在反序列化时JVM按照属性重新生成的 serialVersionUID 会与序列化时生成的不一致,处于安全机制考虑,程序会抛出 java.io.InvalidClassException stream classdesc serialVersionUID = xxxx, local class serialVersionUID = **** 不匹配异常;

如果显式指定 serialVersionUID,JVM在序列化和反序列化时仍会生成一个 serialVersionUID,但会拿显式指定的 serialVersionUID 覆盖自动生成的 serialVersionUID,这样在反序列化时新旧版本的 serialVersionUID 就一致了,此时就可以在序列化后仍可以修改类中的属性或者方法,而不会影响到后期的反序列化流程。

可以说 serialVersionUID 是序列化和反序列化之间彼此匹配的唯一口令;

测试demo: 实现序列化接口的Person实体类:

package com.pojo.model; import java.io.Serializable; public class Person implements Serializable{ private static long serialVersionUID = -234234238976l; private String code; private String name; public Person(String code, String name) { this.code = code; this.name = name; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Person{" + "code='" + code + '\'' + ", name='" + name + '\'' + '}'; } }

测试类:

package com.study.controller; import com.study.pojo.model.Person; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class TestController { public static void main(String[] args) { Person p = new Person("1", "张三"); // 序列化 serializable(p); // 反序列化 Person pp = deserializable(); System.out.println(pp); } public static void serializable(Person p) { try (ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream(new File("E:\\temp/p.txt")));) { output.writeObject(p); } catch(IOException e) { e.printStackTrace(); } } public static Person deserializable() { try (ObjectInputStream input = new ObjectInputStream(new FileInputStream(new File("E:\\temp/p.txt")));) { return (Person)input.readObject(); } catch(IOException | ClassNotFoundException e) { e.printStackTrace(); } return null; } }

五 序列化API

java.io.ObjectOutputStream:对象输出流,通过 writeObject(obj) 方法实现对象序列化;

java.io.ObjectInputStream:对象输入流,通过 readObject() 方法实现对象反序列化;

序列化 & 反序列化方法参见上述Demo;

六 例外

1、被 transient 关键字修饰的属性不会被序列化;

2、被 static 关键字修饰的属性不会被序列化;

源码参见:

被 static 关键字修饰的属性不会被序列化是因为被static修饰的属性属于类,随着类的加载被加载,而不属于对象,不需要创建对象去调用static修饰的属性,所以不会被序列化;

最新回复(0)