- 论坛徽章:
- 0
|
java解惑学习笔记3:
谜题7:互换内容
public class CleverSwap{
public static void main(String[] args){
int x = 1984; // (0x7c0)
int y = 2001; // (0x7d1)
x^= y^= x^= y;
System.out.println("x= " + x + "; y= " + y);
}
}
它会打印出什么呢?
x = 0111 1100 0000 ,y = 0111 1101 0001 x^=y,后 x = 0000 0001 0001
y^=x 后y = 0111 1100 0000 x^=y 后 x = 0111 1101 0001
所以 x = 0x7d1 y = 0x7c0
很好嘛!不用中间变量的swap方法。但是打印的结果出乎意料:x = 0; y = 1984
郁闷哪!
Java语言规范描述到:操作符的操作数是从左向右求值的。为了求表达式 x ^= expr的值,
x的值是在计算expr之前被提取的,并且这两个值的异或结果被赋给变量x。在CleverSwap程序中,
变量x的值被提取了两次——每次在表达式中出现时都提取一次——但是两次提取都发生在所有的
赋值操作之前。
按编译时引入中间变量的方法来分解x^= y^= x^= y,
x = x ^(y^x^y) : x与y两次xor, 结果还是x ,x xor x ,结果是0 ,故最终得0;
int tmp1 = x ; // x在表达式中第一次出现
int tmp2 = y ; // y的第一次出现
int tmp3 = x ^ y ; // 计算x ^ y
x = tmp3 ; // 最后一个赋值:存储x ^ y 到 x
y = tmp2 ^ tmp3 ; // 第二个赋值:存储最初的x值到y中
x = tmp1 ^ y ; // 第一个赋值:存储0到x中
solution:
丑陋而又无用的解法:
y = (x^= (y^= x))^ y
最好的方法是不要这么干!不想使用中间变量我们可以这样:{a = a + b; b = a - b;a = a - b};
谜题8:Dos Equis
public class DosEquis{
public static void main(String[] args){
char x = 'X';
int i = 0;
System.out.println(true ? x : 0);
System.out.println(false ? i : x);
}
}
和我一样性子急的人,看到这个问题就忍不住想说:
结果是 XX
很不幸,我们又被骗了,当表达式是复合类型的时候,事情往往没那么简单,
表达式结果的类型有时候让我觉得自己被玩了,让我们找出躲在阴暗的角落表达式规范,来增加对自己
代码的掌控能力。
确定条件表达式结果类型的规则的核心:
1.如果第二个和第三个操作数具有相同的类型,那么它就是条件表达式的类型。换句话说,
你可以通过绕过混合类型的计算来避免大麻烦。
2.如果一个操作数的类型是T(byte,short,char),而另一个操作数是一个int类型的常量表达式,
它的值是可以用类型T表示的,那么条件表达式的类型就是T。
3.否则,将对操作数类型运用二进制数字提升,而条件表达式的类型就是第二个和第三个操作数被提升之后的类型。
对于第一个输出,两个操作数是char和int型常量,采用规则2,输出是char型X,
对于第二个输出,采用规则3,其返回的类型是对int和char运用了二进制数字提升之后的类型,即int。
表达式的结果决定那一个重载print method 被调用,对第一个表达式来说,PrintStream.print(char)将被调用,而对第二个表达式来说,
PrintStream.print(int)将被调用。前一个重载方法将变量x的值作为Unicode字符(X)来打印,而后一个重载方法将其作为一个十进制整数
(88)来打印。
所以最终的结果是 X88
经验:总之,通常最好是在条件表达式中使用类型相同的第二和第三操作数。否则,你和你的程序的读者必须要彻底理解这些表达式行为的复杂规范。
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/28187/showart_242719.html |
|