🕓 序列化 & 反序列化
2022年10月10日
- Java
🕓 序列化 & 反序列化
1. 作用
1) 持久化到磁盘
- MyBatis 在持久化到数据库时,不是将对象类持久化到数据库,而是对象类的属性依次持久化到数据库中
2) 网络传输
- 会将对象类转换为 JSON 字符串,而 String 实现了 Serializable 接口
2. Serializable 接口
// 起 标识作用
public interface Serializable {
}
1) 序列化 & 反序列化 为什么需要实现 Serializable 接口
- 实现 Serializable 接口后,JVM 底层会帮我们将对象进行 序列化 & 反序列化
- 若不实现 Serializable 接口,我们需要自己对对象进行 序列化 & 反序列化
2) serialVersionUID 的作用
- 虚拟机是否允许反序列化, 不仅取决于 类路径和功能代码 是否⼀致, 还取决于 serialVersionUID 是否⼀致
- 若没有显示指定
serialVersionUID
,JVM 序列化时,会根据 属性 自动生成 serialVersionUID,然后与 属性 一起序列化- 在反序列化时,根据 属性 再次生成一个 serialVersionUID,然后根据原本的 serialVersionUID,和当前 serialVersionUID 比较,如果相同,表示序列化成功
- 只有 Class 类不变,那么每次生成的 serialVersionUID 一致,而只要类对象发生改变,serialVersionUID 就会发生改变
- 若显示指定 serialVersionUID,JVM 序列化 & 反序列化生成的都是我们指定的 serialVersionUID
- 在实际开发中,若我们修改了对象类,serialVersionUID 仍然不变,因此序列化 & 反序列化均不会抛出 InvalidCastException 异常
- 因此,我们在序列化类时,需要显式指定 serialVersionUID,它为
Long
类型数据结构
3) 简单实现序列化
4) 自定义序列化
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 8683452581122892189L;
transient Object[] elementData; // transient 修饰,不会被序列化,反序列化得到的是 null
// 1. 序列化
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{
int expectedModCount = modCount;
s.defaultWriteObject();
s.writeInt(size);
for (int i=0; i<size; i++) { // 只序列化有元素的数组部分
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
// 2. 反序列化
private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA;
s.defaultReadObject();
s.readInt(); // ignored
if (size > 0) {
int capacity = calculateCapacity(elementData, size); // 保证数组空间足够
SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
ensureCapacityInternal(size);
Object[] a = elementData;
for (int i=0; i<size; i++) { // 读取,序列化后的有元素部分的数组
a[i] = s.readObject();
}
}
}
}
5) 底层原理
writeObject()
readObject()
6) 补充
- 被
transient
修饰的成员变量,在序列化的时候其值会被忽略,在被反序列化后,transient
变量的值被设为初始值,- int == 0,对象 == null
static
属性不会被序列化- 序列化是针对 对象而言,而
static
属性变量是属于类的,优于实例对象的出现,随着类加载而存在,因此不会序列化
- 序列化是针对 对象而言,而
private static final long serialVersionUID = 8683452581122892189L;
也被static
修饰,为什么还会被序列化?- serialVersionUID 其实并未被序列化,而是在反序列化时,将我们指定的值赋值给 serialVersionUID,所以看起来像是被序列化了
- 类要想正确序列化,其字段属性也需要能被序列化,属性对象也需要实现 Serializable 接口