一 序列化 & 反序列化
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修饰的属性,所以不会被序列化;