本博文为《编写高质量代码—改善Java程序的151个建议》一书的阅读笔记。该书从很多方面给予了编写高质量代码的宝贵经验。而且该书应该是那种开发经验越丰富,体会越深的书籍。在阅读过程中,从该书中收获良多,这里主要作下书籍笔记,有体会的地方加点自己的想法。受限于知识水平,部分内容还没能深刻体会,所以更多更好的内容和具体实例还需要从书中去找寻。
在Java开发过程中有很多通用的准则,遵守这些准则能够避免很多不必要的错误发生,让代码的质量更高,下面的内容为书籍第一章《Java开发中通用的方法和准则》的阅读笔记。
一、不要在常量和变量中出现易混淆的字母
例如数字1和小写字母l容易混淆,数字0和字母o容易混淆,因此在变量或常量命名时需要避免两个同时出现。
另外命名最好遵守Java编码规范:包名全小写,类名首字母全大写,常量全部大写并用下划线分隔,变量采用驼峰命名法等等。
二、不要让常量蜕变成变量
常量应该保证在编译期就确定其值不变,而不是在运行期设置其值。书中列举了一个定义常量值等于随机数的例子,这样编译不会有问题,但是是不可取的作法。
三、三元操作符的类型勿必一致
三元操作符经常作if-else的简写使用,但是需要注意保证三元操作符的类型都是一致的,不然可能出现意想不到的问题。
例如书中所举示例:String.valueOf(80<90?90:100)返回结果是90,而String.valueOf(80<90:100.0)的返回结果却是90.0,就因为类型不一致导致。具体的原因与三元操作符的转换规则有关系,对于该示例若两个操作数都是数字,则返回值类型为范围较大者。所以造成返回值不同。
四、避免带有变长参数的方法重载
变长参数能够提高方法的灵活度和可复用性,不过在使用过程中需要注意变长参数必须是方法参数的最后一个参数,同一个方法不能够定义多个变长参数,另外需要特别注意带有变长参数的方法不要进行重载,因为这样如果参数类型相同时会引起“不知道调用的是哪个方法”的疑惑,代码可读性变差,而且可能导致潜在的问题。
五、别让null值和空值威胁到变长方法
在调用变长参数的方法时,不要将参数直接传null值,可能引起不必要的错误,应该传指定参数类型的数据(明确类型的数据)。
六、覆盖变长方法也要循规蹈矩
子类覆盖父类的变长方法时一定要注意子类的方法参数列表保证与被重写的方法参数列表相同。(这里书中有一个很好的例子进行说明,限于篇幅不列举了)
七、警惕自增的陷阱
书中举了一个很有意思的例子, int count = 0; for( int i = 0; i < 10; i++ ){ count = count++; } System.out.println(count);
最后输出的结果是0。因为每次循环都会把count赋值为0。所以在编写代码时要特别警惕自增的陷阱。
八、少用静态导入
对于静态导入,需要遵循两个规则:
1. 不使用*(星号)通配符,除非是导入常量类或接口;
2. 方法名必须是具有明确、清晰表象意义的工具类,才可以使用静态导入。
这样主要是为了增强代码的可读性。
九、不要在本类中覆盖静态导入的变量和方法
在本类覆盖静态导入的变量和方法时,方法执行时会遵循“最短路径”原则,本类的方法和变量会优先,不过这样容易引起理解上的困难,导致代码可读性变差。
十、养成好习惯,显式声明UID
在实现Serializable接口时,常常需要增加一个serialVersionUID,这个值是用来定义类的版本的,显示声明UID可以避免对象不一致。
十一、避免用序列化类在构造函数中为不变量赋值
在序列化类中,不要使用构造函数为final变量赋值,如果赋值的话可能出现书中示例所给出的类版本变化时,序列化与反序列化的值不一致的问题。
十二、避免为final变量复杂赋值
final变量可以通过方法返回值进行赋值,不过要避免这种赋值方式。因为这样可能在类版本变化时,序列化与反序列化的值不一致的问题。
根本原因是final变量在以下三种情况下不会被重新赋值:
1. 通过构造函数为final变量赋值
2. 通过方法返回值为final变量赋值
3. final修饰的属性不是基本类型
十三、使用序列化类的私有方法巧秒解决部分属性持久化问题
在进行序列化时,有时部分属性不想序列化,此时会选择加上关键字transient,这在大多数情况下都可以,有时却无法满足需要,此时可以自实现序列化的readObject和writeObject方法,实现自定义的序列化逻辑。具体实例参考书籍建议14。
十四、break万万不可忘
在switch语句中,case后面执行的语句常常容易忽略break,造成不必要的错误,这个要注意。
十五、易变业务使用脚本语言编写
脚本语言灵活,便捷、简单,引入脚本语言会让Java更加强大。
十六、慎用动态编译
使用动态编译需要注意以下几点:
1. 在框架中谨慎使用。
2. 不要在要求高性能的项目中使用。
3. 动态编译需要考虑安全问题。
4. 记录动态编译过程。
十七、避免instanceof非预期结果
instanceof用来判断一个对象是否是一个类实例,其中要求左右操作数必需有继承关系或实现关系,不然编译会失败。
十八、断言绝对不是鸡肋
在防御式编程中经常会使用断言对环境和参数做出判断,避免程序因不当的输入或错误的环境而产生逻辑异常。
断言在下面两种情况下不可以使用: 1. 在对外公开的方法中:因为外部环境是不可控的,对外公开的方法应该是多做校验,而不是断言。 2. 在执行逻辑代码的情况下:因为assert的支持是可选的,所以不能在assert表达式中执行代码,这样可能因为环境的不同而是不同的逻辑。
十九、不要只替换一个类
在系统中经常会有常量类定义,这样在类编译时就会把常量值进行替换,如果常量类进行改变时,只编译常量类,而使用常量的类不重新编码,这样改动实际上算没有生效,从而可能引发一些问题。
例如在项目发布的时候发现某个常量类需要修改,这个时候重新改过后,为了省事,直接编译这个类,替换项目包中的class文件,再重新启动项目。可能就会出现上述其它引用到的常量值其实没有改动的问题。
所以在项目类文件有改动时,不要替换类文件,要重新编译整个项目再发布。