近日在J2ME里做汉字编解码运算时,遇到一诡异问题,起初让我百思不得其解,并直叫见鬼了。
将问题简单描述如下:
汉字“国”的Unicode 编码值为22269,高字节为0x56 ,低字节为0xFD,于是我按照算法int ch=0x56<<8+0xfd 将ch理所当然地视为“国”的Unicode编码值ch==22269,但 char letter=(char)ch后,我发现letter=‘ી’是一乱码,而不是我所期待的“国”字。经验告诉我,debug的时候到了,通过debug发现,int ch=0x56<<8+0xfd计算得到的ch值是2752。
我百思不得其解,开始心烦意乱地胡乱猜疑,怀疑0x56<<8与0x56*256两个表达式的数值不相等,但大学里的知识告诉我将一个整数右移N位肯定等于该数乘以2的N次幂,Sytem.out.println((0x56<<8)==(0x56*256))运行的实际结果也否定了这次猜疑。于是我写了如下测试语句:
int highByte=0x56;
int lowByte=0xfd;
int chValue1=highByte<<8+lowByte;//2752
int chValue2=highByte*256+lowByte;//22269
char ch1=(char)chValue1;//乱码
char ch2=(char)chValue2;//'国'
通过debug发现,chValue1值为2752,转为char后是一个乱字符。chValue2值为22269,转为char后是我梦寐以求的“国”字,绝望地看着屏幕半小时后,我小心翼翼地分别给highByte<<8和highByte*256加了括号,发现得到的chValue1和chValue2都是我所期望的22269,于是我想到了运算符优先级的问题,猜测在java中<<的优先级低于+,在表达式highByte<<8+lowByte中,现计算8+lowByte,然后将得到的数值右移8位,实际程序的运行结果验证了我的想法, highByte<<8+lowByte和highByte<<(8+lowByte)结果相同都是2752。通过查找java运算符优先级顺序表(见下图),发现<<运算符确实低于+,是我没有注意正确的运算顺序导致了该问题的出现。
Java操作符的优先级和结合性
| 优先级 | 结合性 |
1 | [ ] . ( ) (函数呼叫) | 从左到右 |
2 | ! ~ ++ -- +(单操作数) –(单操作数) ( ) (类型转化) new | 从右到左 |
3 | * / % | 从左到右 |
4 | + - | 从左到右 |
5 | << >> >>> | 从左到右 |
6 | < <= > >= instanceof | 从左到右 |
7 | == != | 从左到右 |
8 | & | 从左到右 |
9 | ^ | 从左到右 |
10 | | | 从左到右 |
11 | && | 从左到右 |
12 | || | 从左到右 |
13 | ? : | 从右到左 |
14 | = += -= *= /= %= ^= <<= >>= >>>= | 从右到左 |
结论:Java优先级顺序是很基本的常识,一个看起来让人悲伤欲绝的bug,查找出原因来很可能是因为一些很基本的常识没有注意到。在解决bug时,尽量保持平静的心态和,客观理性地查找原因。