2008年9月30日星期二

深邃的初等Bug

这是一个初等的Bug,我却De了很久才De出来。暴露了我这个知识点没有很好地掌握:int和byte的类型转换。
我在重写inputstream的int
read()抽象方法。按照定义,它返回[-1,255]的整数,如果流到末端了,返回-1,否则返回流的下一个byte,向上转换成int,取值范围[0,255]。
有一个方法byte f(byte),我希望用它转换所有读到的字节,于是我这样写了:
int x=in.read();
if(x==-1)return -1;
else return f((byte)x);
结果,这个read方法给我找了很多麻烦,后来才发现,它返回值经常是比-1还小的负数,导致上层readUnsignedShort调用它的时候抛出EOF,因为它是这样判断的:
int ch1 = in.read();
int ch2 = in.read();
if ((ch1 | ch2) < 0)
throw new EOFException();
return (ch1 << 8) + (ch2 << 0);
如果in按照inputstream定义实现,这没问题,因为在这个假设前提下(ch1 | ch2) <
0当且仅当ch1和ch2至少有一个是-1,当且仅当in到了流末端。

原来,f返回byte,但所在的read方法却要求返回int,所以它自动完成了类型转换,这里非常深邃了,在java中,byte类型值作为整数取值范围在[-128,127],所以自动类型转换成int后就出比-1还小的负数了!要把byte转换成[0,255]之间的整数,(不能做线性变换!),应该将前3个高字节位补成0。
学计算机的人不难想出解决办法:做&0xFF就行了。我起初对此很困惑,写java程序很少用位运算的。。。不过现在能理解了^_^
改成
int x=in.read();
if(x==-1)return -1;
else return f((byte)x) &0xFF;
就都好了:)

P.S.
把一个int强制转换成byte时肯定不是单射,事实上,它并非直接取最低字节,而是带符号位的转换。