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

建议19:断言绝对不是鸡肋
 

Java对assert默认是不开启的,使用时,编译时需要用javac -source;执行时需要使用java -ea。
 

由于带有assert语句的程序运行时,使用了新的ClassLoader和Class类,因此,这种程序必须在JDK1.4(或者更高版本)的JRE下运行,而不能在老版本的JRE下运行。
 

由于我们可以选择开启assertion功能,或者不开启,另外我们还可以开启一部分类或包的assertion功能,所以运行选项变得有些复杂。通过这些选项,我们可以过滤所有我们不
 

关心的类,只选择我们关心的类或包来观察。下面介绍两类参数:
 

参数 -esa 和 -dsa:
 它们含义为开启(关闭)系统类的assertion功能。由于新版本的Java的系统类中,也使了assertion语句,因此如果用户需要观察它们的运行情况,就需要打开系统类的assertion功
 

能 ,我们可使用-esa参数打开,使用 -dsa参数关闭。
 -esa和-dsa的全名为-enablesystemassertions和-disenablesystemassertions,全名和缩写名有同样的功能。
 
 参数 -ea和-ea:
 它们含义为开启(关闭)用户类的assertion功能:通过这个参数,用户可以打开某些类或包的assertion功能,同样用户也可以关闭某些类和包的assertion功能。打开assertion功
 

能参数为-ea;如果不带任何参数,表示打开所有用户类;如果带有包名称或者类名称,表示打开这些类或包;如果包名称后面跟有三个点,代表这个包及其子包;如果只有三个点
 

,代表无名包。关闭assertion功能参数为-da,使用方法与-ea类似。
 -ea和-da的全名为-enableassertions和-disenableassertions,全名和缩写名有同样的功能。
 下面表格表示了参数及其含义,并有例子说明如何使用。
  
 参数  例子  说明
 -ea  java -ea  打开所有用户类的assertion
 -da  java -da  关闭所有用户类的assertion
 -ea:<classname>  java -ea:MyClass1打开MyClass1的assertion
 -da:<classname>  java -da: MyClass1关闭MyClass1的assertion
 -ea:<packagename>  java -ea:pkg1打开pkg1包的assertion
 -da:<packagename>  java -da:pkg1关闭pkg1包的assertion
 -ea:...  java -ea:...打开缺省包(无名包)的assertion
 -da:...  java -da:...关闭缺省包(无名包)的assertion
 -ea:<packagename>...  java -ea:pkg1...打开pkg1包和其子包的assertion
 -da:<packagename>...  java -da:pkg1...关闭pkg1包和其子包的assertion
 -esa  java -esa打开系统类的assertion
 -dsa  java -dsa关闭系统类的assertion
 综合使用  java -dsa:MyClass1:pkg1关闭MyClass1和pkg1包的assertion
 

java编程语言中断言的关键字 assert,其基本的使用方法
 

assert expression1
 或者
 assert expression1 : expression2
 

assert两个特性,1:assert默认是不启用的,2:assert抛出的异常AssertError是继承自Error
 assert不要使用的情况,1:在对外公开的方法中,不要使用 2:在使用assert功能时不要惨呀业务逻辑,因为java对assert的支持是可选的。
 

assert可以使用的情况,1:在私有方法中放置assert作为输入参数的校验,2:流程控制中不可能到达的区域,因为当程序执行到这里的时候就是错误的。
 3:建立程序探针
 例如,在一个财会系统中,公司的支出和收入必须保持一定的平衡关系,因此我们可以编写一个表达式检查这种平衡关系,如下表示。
  
          private boolean isBalance() {
    ……
    }
   
 在这个系统中,在一些可能影响这种平衡关系的方法的前后,我们都可以加上assert验证:assert isBalance():"balance is destoried";
 
挂在一个连接,一个写的关于java assert不错的文章
 

建议20:不要只替换一个类
 

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

final 修饰的类(即非基本类型),编译器认为它是不稳定态,在编译时建立的则是引用关系。
 

建议21:用偶判断,不用奇判断
 

public void test() {
 int i = -1;
 System.out.println(i % 2 == 1 ? "奇数" : "偶数");
 }
 

打印结果是,偶数。我们先来了解一下java中的取余算法,模拟代码如下:
 被除数        除数
 public static int remainder(int dividend, int divisor) {
 return dividend - dividend / divisor * divisor;
 }
 

这回大家明白了吧,
 

修改为 System.out.println(i % 2 == 0 ? "偶数" : "奇数");  就好了。
 


建议22:使用整数类型处理货币
 

执行代码
 System.out.println(10 - 9.4); 运行结果是 0.5999999999999996,至于为什么给我就不说了,大家可以上网查阅一下相关资料(嘻嘻,其实我也不是很明白)。
 这个时候,可以把参与运算的值扩大到整数,计算后的结果在相应的再缩小倍数。如果有更好的办法,欢迎大家评论。
 

建议23:不要让类型默默的转换
 

int i = 30 * 10000 * 1000;
 System.out.println(i);
 long L = i * 60 * 8;
 System.out.println(L);
 

执行的结果是:
 300000000
 -2028888064
 

为什么第二个数是负数呢,那是因为java是先运算然后再进行类型转换的,具体的说就是因为 L 的三个运算参数都是int类型的,三者相乘的结果虽然也是int类型的,
 但是已经超过了int的最大值(基本类型:int 二进制位数:32(即占用四个字节)包装类:java.lang.Integer 最小值:-2147483648最大值:2147483647),所以
 其值就是负值了(为什么是负值?应为过界了就会从头开始,意思是 2147483647 加上 1 结果是 2147483648,以此类推),在转换成long型,结果还是负值。
 将代码修改为 long L = i * 60L * 8; 60L是一个长整形,乘出来的结果也是一个长整形(此乃java的基本转换规则,向数据范围大的方向转换,也就是加宽类型)。
 在实际开发中,更通用的做法是主动的声明式类型转化(注意不是强制的类型转换),代码如下:long L = 1L * i * 60 * 8;(注意,基本类型转换时,使用主动声明式
 减少不必要的bug)。
 

建议25:不要让四舍五入亏了一方
 

具体的内容就不说了,下面就列举一下java支持的一下七种舍入方式(想采用什么舍入模式使用RoundingMode设置即可):
 ROUND_UP:远离零方向舍入
 ROUND_DOWN:趋向零方向舍入
 ROUND_CEILING:向正无穷方向舍入
 ROUND_FLOOR:向负无穷方向舍入
 HALF_UP:最近数字舍入(5进),这就是我们最最经典的四舍五入模式
 HALF_DOWN:最近数字舍入(5舍),在四舍五入中,5是进位的,而在HALF_DOWN中却是舍弃不进位的。
 HALF_EVEN:银行家算法。
 关于以上具体的舍入计算方法就不说了,请大家查看java的JDK文档
 

在普通的项目中舍入模式不会有太多影响,可以直接使用Math.round()方法,但在大量与货币数字交互的项目中,一定要选择好近似的计算模式,尽量减少因算法
 不同而造成的损失。
 

建议26:提防包装类型的null
 

执行以下方法
 public static void main(String[] args) {
 List<Integer> l = new ArrayList<Integer>();
 l.add(1);
 l.add(2);
 l.add(null);
 
int count = 0;
 for(int i : l) {
 count += i;
 }
 System.out.println(count);
 
}
 代码的运行结果是,报错 Exception in thread "main" java.lang.NullPointerException,报错的原因是在程序的for循环中,隐含了一个拆箱过程,
 在此过程中包装类型转换为了基本类型。拆箱的过程调用的是对象的 intValue() 方法来实现的,由于包装类型是 null 值,访问intValue() 方法报空
 指针异常也就在所难免了。
 

将count ++ i 修改为 count += (i != null) ? i : 0 就ok了
 

对于此类问题,我们要谨记一点:包装类型参与运算时,要做null值校验。

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

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