Java编程中遇到的陷阱、圈套
谜题1:奇数性
下面的方法意图确定它那唯一的参数是否是一个奇数。这个方法能够正确运转吗?
public static boolean isOdd(int i){
return i % 2 == 1;
}
奇数可以被定义为被2整除余数为1的整数。表达式i % 2 计算的是i 整除2 时所产生的余数,因此看起来这个程序应该能够正确运转。遗憾的是,它不能;它在四分之一的时间里返回的都是错误的答案。
为什么是四分之一?因为在所有的int 数值中,有一半都是负数,而isOdd 方法对于对所有负奇数的判断都会失败。在任何负整数上调用该方法都回返回false ,不管该整数是偶数还是奇数。
这是Java 对取余操作符(%)的定义所产生的后果。该操作符被定义为对于所有的int 数值 a 和所有的非零int 数值b,都满足下面的恒等式:
(a / b) * b + (a % b) == a
换句话说,如果你用b整除a,将商乘以b,然后加上余数,那么你就得到了最初的值 a 。该恒等式具有正确的含义,但是当与Java 的截尾整数整除操作符相结合时,它就意味着:当取余操作返回一个非零的结果时,它与左操作数具有相同的正负符号。
当i 是一个负奇数时,i % 2 等于-1而不是1,因此isOdd 方法将错误地返回false。为了防止这种意外,请测试你的方法在为每一
个数值型参数传递负数、零和正数数值时,其行为是否正确。
这个问题很容易订正。只需将i % 2 与0而不是与1比较,并且反转比较的含义即可:
public static boolean isOdd(int i){
return i % 2 != 0;
}
如果你正在在一个性能临界(performance-critical)环境中使用isOdd方法,那么用位操作符AND(&)来替代取余操作符会显得更好:public static boolean isOdd(int i){
return (i & 1) != 0;
}
总之,无论你何时使用到了取余操作符,都要考虑到操作数和结果的符号。该操作符的行为在其操作数非负时是一目了然的,但是当
一个或两个操作数都是负数时,它的行为就不那么显而易见了。
谜题2:最后的笑声
下面的程序将打印出什么呢?
public class LastLaugh{
public static void main(String[] args){
System.out.print("H"+"a");
System.out.print('H'+'a');
}
}
你可能会认为这个程序将打印HaHa。该程序看起来好像是用两种方式连接了H和a,但是你所见为虚。如果你运行这个程序,就会发现它打印的是Ha169。那么,为什么它会产生这样的行为呢?
正如我们所期望的,第一个对System.out.print的调用打印的是Ha:它的参数是表达式"H"+"a",显然它执行的是一个字符串连接。而第二个对System.out.print的调用就是另外一回事了。问题在于'H'和'a'是字符型字面常量,因为这两个操作数都不是字符串类型的,所以+ 操作符执行的是加法而不是字符串连接。
编译器在计算常量表达式'H'+'a'时,是通过我们熟知的拓宽原始类型转换将两个具有字符型数值的操作数('H'和'a')提升为int数值而实现的。从char到int的拓宽原始类型转换是将16位的char数值零扩展到32位的int。对于'H',char数值是72,而对于'a',char数值是97,因此表达式'H'+'a'等价于int常量72 + 97,或169。
站在语言的立场上,若干个char和字符串的相似之处是虚幻的。语言所关心的是,char是一个无符号16位原始类型整数——仅此而已。对类库来说就不尽如此了,类库包含了许多可以接受char参数,并将其作为Unicode字符处理的方法。
那么你应该怎样将字符连接在一起呢?你可以使用这些类库。例如,你可以使用一个字符串缓冲区:
StringBuffer sb = new StringBuffer();
sb.append('H');
字符串常量是字符常量吗sb.append('a');
System.out.println(sb);
这么做可以正常运行,但是显得很丑陋。其实我们还是有办法去避免这种方式所产生的拖沓冗长的代码。你可以通过确保至少有一个操作数为字符串类型,来强制+ 操作符去执行一个字符串连接操
作,而不是一个加法操作。这种常见的惯用法用一个空字符串("")作为一个连接序列的开始,如下所示:
System.out.println("" + 'H' + 'a');
这种惯用法可以确保子表达式都被转型为字符串。尽管这很有用,但是多少有一点难看,而且它自身可能会引发某些混淆。你能猜到下面的语句将会打印出什么吗?如果你不能确定,那么就试一下:System.out.print("2 + 2 = " + 2+2);
如果使用的是JDK 5.0,你还可以使用
System.out.printf("%c%c", 'H', 'a');
总之,使用字符串连接操作符使用格外小心。+ 操作符当且仅当它的操作数中至少有一个是String类型时,才会执行字符串连接操作;否则,它执行的就是加法。如果要连接的没有一个数值是字符串类型的,那么你可以有几种选择:
预置一个空字符串;
将第一个数值用String.valueOf显式地转换成一个字符串;
使用一个字符串缓冲区;
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论