关于java序列化
序列化
序列化:将 Java 对象转换成字节流的过程。
反序列化:将字节流转换成 Java 对象的过程。
序列化是干啥用的?
序列化的原本意图是希望对一个Java对象作一下“变换”,变成字节序列,这样一来方便持久化存储到磁盘,避免程序运行结束后对象就从内存里消失,另外变换成字节序列也更便于网络运输和传播,所以概念上很好理解:
- 序列化:把Java对象转换为字节序列。
- 反序列化:把字节序列恢复为原先的Java对象。
而且序列化机制从某种意义上来说也弥补了平台化的一些差异,毕竟转换后的字节流可以在其他平台上进行反序列化来恢复对象。
对象序列化的方式?
在Java中,如果一个对象要想实现序列化,必须要实现下面两个接口之一:
- Serializable 接口
- Externalizable 接口 (子接口)
序列化的版本问题
序列化的版本 ID,我们一直都有提到它,但是始终没有说明这个版本 ID 到底有什么用。用得好的可以拿来实现权限管理机制,用不好也可能导致你反序列化失败。
JAVA 建议每个继承 Serializable 接口的类都应当定义一个序列化版本字段。
private static final long serialVersionUID = xxxxL;
这个值可以理解为是当前类型的一个唯一标识,每个对象在序列化时都会写入外部类型的这个版本号,反序列化时首先就会检查二进制文件中的版本号与目标类型中的版本号是否一样,如果不一样将拒绝反序列化。
这个值不是必须的,如果你不提供,那么编译器将根据当前类的基本信息以某种算法生成一个唯一的序列号,可是如果你的类发生了一点点的改动,这个值就变了,已经序列化好的文件将无法反序列化了,因为你也不知道这个值变成什么了。
所以,JAVA 建议我们都自己来定义这么一个版本号,这样你可以控制已经序列化的对象能否反序列化成功。
自定义序列化规则
- 声明为 static 和 transient 的成员变量,不能被序列化。static 成员变量是描述类级别的属性,transient 表示临时数据
- 在序列化的实体类中可以重写 写入 和 读取的两个方法,也就是序列化 和 反序列化的规则,可以指定可以被序列化的属性。(当然,还有其他的方式)
@Data
@TableName("sys_role")
public class Role implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.AUTO)
private String id;
/**
*角色名称
*/
@TableField("role_name")
private String role_name;
/**
* 角色描述
*/
@TableField("role_content")
private String role_content;
private void writeObject(ObjectOutputStream os) throws IOException {
os.defaultWriteObject();//java对象序列化默认操作
os.writeUTF(role_name);
}
private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException {
is.defaultReadObject();//java对象反序列化默认操作
this.role_name = is.readUTF();
}
}
序列化与继承
例子:
1,父类实现了Serializable,子类没有,父类有int a = 1;int b = 2;int c = 3,子类有int d = 4;int e = 5
*序列化子类的时候,d和e会不会被序列化?*(答案:会)
2,反过来父类未实现Serializable,子类实现了,序列化子类实例的时候,父类的属性是直接被跳过不保存,还是能保存但不能还原?(答案:值不保存)
小结:
父类实现接口后,所有派生类的属性都会被序列化。子类实现接口的话,父类的属性值丢失。
实体类中引用到的对象也会被序列化