改善Java程序的151个建议总结(一)

The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself.

明白事理的人使自己适应世界;不明事理的人想让世界适应自己。

                                                                                                                                                                                         ——萧伯纳


字母“l”作为长整型标志时务必大写,字母“O”则增加注释。

务必让常量的值在运行期保持不变。

三元操作符类型的转换规则:

1、若两个操作数不可转换,则不做转换,返回值为Object类型。

2、若两个操作数是明确类型的表达式(比如变量),则按照正常的二进制数字来转换,int类型转换为long类型,long类型转换为float类型等。

3、若两个操作数中有一个是数字S,另外一个是表达式,且其类型标示为T,那么,若数字S在T的范围内,则转换为T类型;若S超出了T类型的范围,则T转换为S类型。

4、若两个操作数都是直接量数字,则返回值类型为范围较大者。

编译器想要“偷懒”,它会从最简单的开始“猜想”,只要符合编译条件的即可通过。

覆写(重写)必须满足的条件,子类重写父类的方法:

1、重写方法不能缩小访问权限。

2、参数列表必须与被重写方法相同(参数数量相同、类型相同、顺序相同)。

3、返回类型必须与被重写方法的相同或是其子类。

4、重写方法不能抛出新的异常,或者超出父类范围的异常,但是可以抛出更少、更有限的异常,或者不抛出异常。

覆写的方法参数与父类相同,不仅仅是类型、数量,还包括显示形式。

count=count++中count++是一个表达式,是有返回值的,其处理步骤是:

步骤1 JVM把count初始值拷贝到临时变量区。

步骤2 count值加1,这时候count的值是加1之后的值。

步骤3返回临时变量区的值,注意这个只是初始值,没有修改过。

步骤4返回值赋值给count,此时count值被重置成0。

静态导入要遵循的两个原则:

1、不使用*(星号)通配符,除非是导入静态常量类(只包含常量的类或接口)。

2、方法名是具有明确、清晰表象意义的工具类。

编译器“最短原则”:如果能够在本类中查找到的变量、常量、方法,就不会到其他包或父类、接口中查找,以确保本类中的属性、方法优先。因此,如果要变更一个被静态导入的方法,最好的办法是在原始类中重构,而不是在本类中覆盖。

JVM在反序列化时,会比较数据流中的serialVersionUID与类的serialVersionUID是否相同,如果相同,则认为类没有发生变化,可以把数据流load为实例对象;如果不相同,抛出异常InvalidClassException。

显示声明serialVersionUID可以避免对象不一致,但尽量不要以这种方式向JVM“撒谎”。

反序列化的执行过程:JVM从数据流中获取一个Object对象,然后根据数据流中的类文件描述信息(在序列化时,保存到磁盘的对象文件中包含了类描述信息,注意是类描述信息,不是类)查看,发现是final变量,需要重新计算,于是引用Person类中的name值,而此时JVM又发现name竟然没有赋值,不能引用,于是它很“聪明”地不再初始化,保持原值状态,所以结果就是“混世魔王”了。

在序列化类中,不使用构造函数为final变量赋值。

序列化时,保存到磁盘上(或网络传输)的对象文件包括两部分:

1、类描述信息

包括包路径、继承关系、访问权限、变量描述、变量访问权限、方法签名、返回值,以及变量的关联类信息。不记录方法、构造函数、static变量等的具体实现。

2、非瞬态(transient关键字)和非静态(static关键字)的实例变量值

变量值如果是一个基本类型,就是一个简单值保存下来;如果是复杂对象,连该对象和关联类信息一起保存,并且持续递归下去(关联类也必须事先Serializable接口,否则会出现序列化异常),递归到最后,还是基础数据类型的保存。

反序列化时final变量在以下情况下不会被重新赋值:

通过构造函数为final变量赋值。

通过方法返回值为final变量赋值。

final修饰的属性不是基本类型。

在序列化某个类后,想要反序列化这个类时,但是该类中的某些成员变量不可以在反序列化后显示,实现有五个方案:

1、在不需要现实的成员变量前面加上transient关键字

加上transient关键字的类成员变量会失去分布式部署的功能。分布式部署不可能。

2、新增业务对象

重新设计一个类。增加工作量,不是最优方法。

3、请求端过滤

请求端系统获取类对象后,过滤掉不要的成员变量。设计严重失职。

4、变更传输契约

改用XML传输,或者重建一个Web Service服务。成本高。

5、序列化的类中增加私有方法

在序列化的类中增加writeObject和readObject两个方法,并且访问权限都是私有级别。

序列化独有的机制:序列化回调。Java调用ObjectOutputStream类把一个对象转换成流数据时,会通过反射(Reflection)检查被序列化的类是否有writeObject方法,并且检查其是否符合私有、无返回值的特性,若有,则会委托该方法进行对象序列化,若没有,则有ObjectOutputStream按照默认规则继续序列化。同样,在从流数据恢复成实例对象时,也会检查是否有一个私有的readObject方法,如果有,则会通过该方法读取属性值。此处有几个关键点要说明:

1、out.defaultWriteObject()

告知JVM按照默认的规则写入对象,惯例的写法是写在第一句话里。

2、in.defaultReadObject()

告知JVM按照默认的规则读入对象,惯例的写法也是写在第一句话里。

3、out.writeXX和in.readXX

分别是写入和读出相应的值,类似一个队列,先进先出,如果此处又复杂的数据逻辑,建议按封装Collection对象处理。

按照正常执行逻辑不可能到达的代码区域可以放置assert。具体分三种情况:

1、在私有方法中防止assert作为输入参数的校验

2、流程控制中不可能到达的区域

3、建立程序探针

对于final修饰的基本类型和String类型,编译器会认为它是稳定态(Immutable Status),所以在编译时就直接把值编译到字节码中了,避免了在运行期引用(Run-time Reference),以提高代码的执行效率。

当我们引用一个常量类里面的基本类型或String类型的数据时候,第一次引用之后,修改常量类中的该值重新编译该常量类,而不重新编译引用类,此时该类中引用的数据还是修改之前的数据。

对于final修饰的类(即非基本类型),编译器认为它是不稳定态(Mutable Status),在编译时建立的则是引用关系(该类型也叫做Soft Final),如果Client类引入的常量是一个类或实例,即使不重新编译也会输出最新值。

发布应用系统时禁止使用类文件替换方式,整体WAR包发布再是万全之策。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值