改善Java程序的151个建议 读后感(3)

建议12:避免用序列化类在构造函数中为不变量(final关键字修饰的变量)赋值
 

代码1:
 public class Persion implements Serializable{
 

private static final long serialVersionUID = 1L;
 

public final String name;
 

public Persion () {
 name = "liangpeng";
 }
 
}
 

将 代码1 通过 ObjectOutputStream 的 writeObject(Object obj) 方法写入到一个文件中。然后将 Persion 的 name 的只修改为 liangpengGood
 

这时 通过 ObjectInputStream 的 readObject() 方法读取一个对象,这时打印Psersion的name值,您觉得是不是应该输出 liangpengGood,但是输出
 

结果是 liangpeng
 

至于为什么下面会进行解释
 

反序列化时构造函数不会执行
 

反序列化的执行过程:JVM从数据流中获取一个object对象,然后根据数据流中的类文件描述信息(在序列化时,磁盘的对象文件中包含了类的描述信息,注意是类的描述信息,不
 

是类)查看,发现是final变量,需要重新计算。于是引用Persion类中的name值,而此时JVM又发现name竟然没有赋值,不用引用,于是它很聪明的不再初始化,保持原值状态,所
 

以结果就是 liangpeng 了。
 

下面在说一下即使类实现了 serializable 接口后仍然不能序列化的情况
 a:static修饰的字段
 b:transient关键字修饰的字段
 c:父类没有实现serializable接口,序列化时只会序列化子类不会序列化父类。
 (欢迎大家积极补充,一起学习成长)
 

建议,在定义final变量时,就尽量给定它的初始值。
 

建议13:避免为final变量复杂赋值
 

建议12所有的final会被重新赋值,其中的“值”指的是简单的对象。简单的对象包括:8个基本数据类型,以及数组,字符串(字符串情况很复杂,不通过new关键字生成String对
 

象的情况下,final变量的赋值与基本类型相同),但是不能方法赋值。
 其中的原理是这样的,保存到磁盘上(或者网络传输)的对象文件包括两部分:
 a:类的描述信息
 但是不会保存方法,构造函数,static变量等的具体实现。其他的信息,例如,包路径、继承关系、访问权限,等都会保存。
 b:没有使用transient关键字修饰和static关键字修饰的实例变量值(如果是实例变量值,是复杂对象,那么连该对象和关联类信息一起保存,并且递归下去,关联类也必须实现
 
Serializable接口,否则会出现序列化异常)
 

总结一下,反序列化时final变量在以下情况下不会被重新赋值:
 
通过构造函数为final变量赋值
 
通过方法返回值为final变量赋值
 
final修饰的属性不是基本类型
 

建议14:使用序列化类的私有方法巧妙解决部分属性持久化的问题
 

实现Serializable接口的类,可以在类里定义一个readObject方法
 private void readObject(Java.io.ObjectInputStream stream)
 throws IOException, ClassNotFoundException{
 stream.defaultReadObject();
 stream.readInt();
 

}
 和一个writeObject方法
 private void writeObject(java.io.ObjectOutputStream stream)
 throws IOException {
 

stream.defaultWriteObject();
 stream.writeInt(12);
 

}
 

方法签名必须一个字母都不能差。这样,当你调用ObjectOutputStream类把一个对象转换成数据流时,会通过反射检查
 被序列化的类是否有writeObject方法,若没有,则有ObjectOutputStream按照默认的规则继续序列化。同样,在从流
 数据恢复成实例对象时,也会检查是否有一个私有的readObject方法,如果有,则会通过该方法读取属性值。
 

注意:stream.writeXX 和 stream.writeXX,分别写入和读取相应的值,类似一个队列,先进先出,如果此处有复杂的数据逻辑,
 建议封装Collection对象处理。
 

建议15:break 万万不可忘
 

public class SuggestTwo {
 

public static void main(String[] args) {
 String str = "";
 int n = 0;
 switch(n) {
 case 0 : str = "a";
 case 1 : str = "b";
 default : str = "liangpeng";
 }
 System.out.println("a="+str);
 }
 

}
 

这段代码的执行结果是,a=liangpeng。
 

原因是,每个case语句后面少加了break关键字。程序从 case 0 后面的语句开始执行,知道找到最近的break语句结束,但可惜的是我们程序中
 没有break语句,于是程序执行的过程中,str被多次赋值,switch语句执行结束了,于是结果也就如此了。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/6136f69fee4e0099db90bd28964854b3.html