java开发150个建议( 八 )


public class Person implements Serializable {private static final long serialVersionUID = 1867341609628930239L;public final String perName;public Person() {perName = "程咬金";}}
这也是我们常用的一种赋值方式,可以把类定义为版本V1.0,然后进行序列化,看看序列化后有什么问题,序列化代码如下:
public class Serialize {public static void main(String[] args) {//序列化以持久保持SerializationUtils.writeObject(new Person());}}
的实习对象保存到了磁盘上,它时一个贫血对象(承载业务属性定义,但不包含其行为定义),我们做一个简单的模拟,修改一下值代表变更,要注意的是不变,修改后的代码如下:
public class Person implements Serializable {private static final long serialVersionUID = 1867341609628930239L;public final String perName;public Person() {perName = "秦叔宝";}}
此时类的版本时V2.0但没有改变,仍然可以反序列化,代码如下:
public class Deserialize {public static void main(String[] args) {Person p = (Person) SerializationUtils.readObject();System.out.println(p.perName);}}
现在问题出来了,打印出来的结果是"程咬金" 还是"秦叔宝"?答案是:"程咬金" 。final类型的变量不是会重新计算嘛,打印出来的应该是秦叔宝才对呀 。为什么会是程咬金?这是因为这里触及到了反序列化的两一个原则:反序列化时构造函数不会执行.
反序列化的执行过程是这样的:JVM从数据流中获取一个对象,然后根据数据流中的类文件描述信息(在序列化时,保存到磁盘的对象文件中包含了类描述信息,注意是描述信息,不是类)查看,发现是final变量,需要重新计算,于是引用类中的值,而此时JVM又发现竟没有赋值,不能引用,于是它很聪明的不再初始化,保持原值状态,所以结果就是"程咬金"了 。
注意:在序列化类中不使用构造函数为final变量赋值.
回到顶部
建议13:避免为final变量复杂赋值
为final变量赋值还有另外一种方式:通过方法赋值,及直接在声明时通过方法的返回值赋值,还是以类为例来说明,代码如下:
public class Person implements Serializable {private static final long serialVersionUID = 1867341609628930239L;//通过方法返回值为final变量赋值public final String pName = initName();public String initName() {return "程咬金";}}
pName属性是通过方法的返回值赋值的,这在复杂的类中经常用到,这比使用构造函数赋值更简洁,易修改,那么如此用法在序列化时会不会有问题呢?我们一起看看 。类写好了(定义为V1.0版本),先把它序列化,存储到本地文件,其代码与之前相同,不在赘述 。现在类的代码需要修改,的返回值改为"秦叔宝".那么我们之前存储在磁盘上的的实例加载上来,pName的会是什么呢?
现在,类的代码需要修改,的返回值也改变了,代码如下:
public class Person implements Serializable {private static final long serialVersionUID = 1867341609628930239L;//通过方法返回值为final变量赋值public final String pName = initName();public String initName() {return "秦叔宝";}}
上段代码仅仅修改了的返回值(类为V2.0版本)也就是通过new生成的对象的final变量的值都是"秦叔宝",那么我们把之前存储在磁盘上的实例加载上来,pName的值会是什么呢?
结果是"程咬金",很诧异,上一建议说过final变量会被重新赋值,但是这个例子又没有重新赋值,为什么?
上个建议说的重新赋值,其中的"值"指的是简单对象 。简单对象包括:8个基本类型,以及数组、字符串(字符串情况复杂,不通过new关键字生成的对象的情况下,final变量的赋值与基本类型相同),但是不能方法赋值 。