两个有趣的关于java里基本类型转换的问题
先看一小段代码,考虑下打印出什么。
public class Typecast{
public static void main(String[] args){
System.out.println((int)(char)(byte)-1);
}
}
我们考虑下一个整数-1历经3次波折会以哪种样子示人呢?一步步来分析吧。-1是个int型字面量,如果是-1L的话就是个long型字面量,要注意没有short型或byte型字面量。这也是尽量少用这俩类型的一个原因,因为变量存的值哪里来的,别的变量或者一个字面量赋值的,而且归根结底是要被一个字面量赋值的。没有字面量,变量就是无源之水了。那么这个源头却没有short和byte两种类型,这就说明如果使用这俩变量,在数据传导的过程中必然有一步要进行类型转换的,而类型转换涉及到得隐晦问题还是很多的。
我们把-1写成二进制码吧,应该是0xffffffff,第一步转成byte很简单,术语叫做窄化原生类型转换,砍掉高位的bit,转成11111111。第二步就有疑问了,char是个16位的,肯定要补成16个bit了,但是前面补8个1还是8个0呢?这个小代码的精髓也就是这里要转成char型,假如要转成short型,很简单了,执行符号扩展,转成16个1,但是char和short虽都是16位,但是char无符号,short有符号,所以这俩不能一视同仁的。如果补8个1会怎样,16个1被解释成char型应该就是65535了,如果补8个0呢?就是255。最后一步char转int是执行零扩展的,也就是高位补16个0,这算常识吧,应该都知道。运行下程序结果输出65535,这说明了第二步是执行符号扩展的,也就是高位补了8个1。这种行为遵守语言规范的这条规则:如果最初的数值类型是有符号的,那么就执行符号扩展;如果它是char,那么不管它将要被转成什么类型,都执行零扩展。
再看个有趣的小问题,请提供一个对i的声明,将下面的循环转变为一个无限循环:
while(i!=0){
i>>>=1;
}
由于执行移位操作,i的类型有要求的,排除掉float,double,boolean,>>>叫做无符号右移操作符,在最左边补0就是了,初看起来好像移动一定的位数,32或64,循环就结束了。这里的关键在于这个表达式等价于i=i>>>1;有一个赋值的过程,赋值的双方类型不一定一致,类型不一致时就可能出问题。可以对i进行声明,short i=-1;在执行移位操作时,第一步是将i提升为int类型,因为所有的算术操作都会对short,byte,char类型的操作数执行这样的提升。这么做拓宽了原生类型,没有信息的损失,执行的是符号扩展,i的值提升后是0xffffffff,i>>>1之后得到的是0x7fffffff。将这个值再存入i中的时候执行了可怕地窄化原生类型,把高16位截掉,剩下(short)0xffff,又回到了起点。这提示我们不要short,byte,char类型上使用复合赋值操作符,很容易出问题。
posted on 2010-04-23 12:05 monkey.D.luffy 阅读(695) 评论(0) 编辑 收藏 举报