建议12:避免用序列化类在构造函数中为不变量赋值
public class Client implements Serializable{
private static final long serialVersionUID = -5492180880303604959L;
public final String name="sea";
}
Client对象被序列化存储后,反序列化时name会被重新计算其值(跟static 变量不一样,
static 变量压根不会被序列化存储),比如类中的name改为了“sky”,那么之前序列化的对象反序列化之后name也是“sky“,保持新旧对象的final变量一致,有利于代码业务逻辑统一,这是序列化的基本原则之一。如果final变量通过构造函数赋值呢,如下:
public class Client implements Serializable{
private static final long serialVersionUID = -5492180880303604959L;
public final String name;
public Client(){
name="sea";
}
}
上边的的类 new Cliient()之后序列化存储,之后类被重写,name变为了”sky“, 并且保持serialVersionUID不变,改变后的类如下:
public class Client implements Serializable{
private static final long serialVersionUID = -5492180880303604959L;
public final String name;
public Client(){
name="sky";
}
}
那么反序列化之后,name的值是什么呢?
答案是”sea“。
这时候大家可能会有疑问,final变量(不是static的)不是会被重新计算吗,但是”反序列化时构造函数不会执行“,所以还是”sea“。
建议13:避免为final变量进行复杂赋值
通过方法为final变量赋值,如下:
public class Client implements Serializable{
private static final long serialVersionUID = -5492180880303604959L;
public final String name=iniName();
public String iniName(){
return "sea";
}
}
序列化存储后,iniName()返回值改为了”sky“,如下:
public class Client implements Serializable{
private static final long serialVersionUID = -5492180880303604959L;
public final String name=iniName();
public String iniName(){
return "sky";
}
}
反序列化后,name的值还是sea。
上边说过final变量会被重新计算赋值,但是这个值 是值简单对象赋值,包括8个基本类型,数组,字符串(不通过new String()生成的字符串 final 赋值与基本类型一样),不能通过方法。
原理:保存到磁盘上(或者网络传输)的对象文件包括两部分:
(1)类描述信息
包括 包路径、继承关系、访问权限、变量描述、变量访问权限、方法签名、返回值,以及变量的关联类信息。它并不是class文件的翻版,它不记录方法、构造函数、static变量等的具体实现。
(2)非瞬态(transient关键字)和非静态(static)的实例变量值
这里的值是基本类型,如果是复杂对象,连该对象和关联类信息一起保存,并且递归下去(关联类也必须实现Serializable接口,否则异常),其实还是基本数据类型的保存。
最后,总结一下,final变量在一下情况不会被重新计算赋值:
(1)通过构造函数赋值
(2)通过方法赋值
(3)final修饰的不是基本类型