改善java程序的151个建议 读后感三

改善java程序的151个建议 读后感三


建议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语句执行结束了,于是结果也就如此了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值