Java常见疑难问题-数值表达式1
5. 窄数字类型提升至宽类型时使用符号位扩展还是零扩展
System.out.println((int)(char)(byte)-1);// 65535
结果为什么是65535而不是-1?
窄的整型转换成较宽的整型时符号扩展规则:如果最初的数值类型是有符号的,那么就执行
符号扩展(即如果符号位为1,则扩展为1,如果为零,则扩展为0);如果它是char,那么
不管它将要被提升成什么类型,都执行零扩展。
了解上面的规则后,我们再来看看迷题:因为byte是有符号的类型,所以在将byte数值-1
(二进制为:11111111)提升到char时,会发生符号位扩展,又符号位为1,所以就补8个
1,最后为16个1;然后从char到int的提升时,由于是char型提升到其他类型,所以采用
零扩展而不是符号扩展,结果int数值就成了65535。
如果将一个char数值c转型为一个宽度更宽的类型时,只是以零来扩展,但如果清晰表达
以零扩展的意图,则可以考虑使用一个位掩码:
int i = c & 0xffff;//实质上等同于:int i = c ;
如果将一个char数值c转型为一个宽度更宽的整型,并且希望有符号扩展,那么就先将char
转型为一个short,它与char上个具有同样的宽度,但是它是有符号的:
int i = (short)c;
如果将一个byte数值b转型为一个char,并且不希望有符号扩展,那么必须使用一个位掩
码来限制它:
char c = (char)(b & 0xff);// char c = (char) b;为有符号扩展
6. ((byte)0x90 == 0x90)?
答案是不等的,尽管外表看起来是成立的,但是它却等于false。为了比较byte数值(byte)0x90
和int数值0x90,Java通过拓宽原生类型将byte提升为int,然后比较这两个int数值。因为
byte是一个有符号类型,所以这个转换执行的是符号扩展,将负的byte数值提升为了在数
字上相等的int值(10010000–>111111111111111111111111 10010000)。在本例中,该转换将
(byte)0x90提升为int数值-112,它不等于int数值的0x90,即+144。
解决办法:使用一个屏蔽码来消除符号扩展的影响,从而将byte转型为int。
((byte)0x90 & 0xff)== 0x90
7. 三元表达式(?:)
char x = ‘X’;
int i = 0;
System.out.println(true ? x : 0);// X
System.out.println(false ? i : x);// 88
条件表达式结果类型的规则:
(1) 如果第二个和第三个操作数具有相同的类型,那么它就是条件表达式的类型。
(2) 如果一个操作的类型是T,T表示byte、short或char,而另一个操作数是一个int
类型的”字面常量”,并且它的值可以用类型T表示,那条件表达式的类型就是T。
(3) 否则,将对操作数类型进行提升,而条件表达式的类型就是第二个和第三个操作被
提升之后的类型。
现来使用以上规则解上面的迷题,第一个表达式符合第二条规则:一个操作数的类型是char,
另一个的类型是字面常量为0的int型,但0可以表示成char,所以最终返回类型以char类
型为准;第二个表达式符合第三条规则:因为i为int型变量,而x又为char型变量,所以
会先将x提升至int型,所以最后的结果类型为int型,但如果将i定义成final时,则返回
结果类型为char,则此时符合第二条规则,因为final类型的变量在编译时就使用”字面常
量0″来替换三元表达式了:
final int i = 0;
System.out.println(false ? i : x);// X
在JDK1.4版本或之前,条件操作符 ?: 中,当第二个和延续三个操作数是引用类型时,条
件操作符要求它们其中一个必须是另一个的子类型,那怕它们有同一个父类也不行:
public class T {
public static void main(String[] args) {
System.out.println(f());
}
public static T f() {
// !!1.4不能编译,但1.5可以
// !!return true?new T1():new T2();
return true ? (T) new T1() : new T2();// T1
}
}
class T1 extends T {
public String toString() {
return “T1”;
}
}
class T2 extends T {
public String toString() {
return “T2”;
}
}
在5.0或以上版本中,条件操作符在延续二个和第三个操作数是引用类型时总是合法的。其
结果类型是这两种类型的最小公共超类。公共超类总是存在的,因为Object是每一个对象
类型的超类型,上面的最小公共超类是T,所以能编译。
8. +=复合赋值问题
x+=i与x=x+i等效吗,许多程序员都会认为第一个表达式x+=i只是第二个表达式x=x+i的
简写方式,但这并不准确。
Java语言规范中提到:复合赋值 E1 op= E2等价于简单赋值 E1 = (T)((E1) op (E2)),其中T
是E1的类型。
复合赋值表达式自动地将所执行计算的结果转型为其左侧变量的类型。如果结果的类型与该
变量的类型相同,那么这个转型不会造成任何影响,然而,如果结果的类型比该变量的类型
要宽,那么复合赋值操作符将悄悄地执行一个窄化原生类型转换,这样就会导致结果不正确:
short x=0;
int i = 123456;
x +=i;
System.out.println(x);//-7616
使用简单的赋值方式就不会有这样的问题了,因为宽类型不能自动转换成窄的类型,编译器
会报错,这时我们就会注意到错误:x = x + i;//编译通不过
请不要将复合赋值操作符作用于byte、short或char类型的变量;在将复合赋值操作符作用
于int类型的变量时,要确保表达式右侧不是long、float或double类型;在将复合赋值操作
符作用于float类型的变量时,要确保表达式右侧不是double类型。其实一句:不要将让左
侧的类型窄于右侧的数字类型。
总之,不要在short、byte或char类型的变量之上使用复合赋值操作符,因为这一过程会伴
随着计算前类型的提升与计算后结果的截断,导致最后的计算结果不正确。
9. i =++i;与i=i++;的区别
int i = 0;
i = i++;
System.out.println(i);
上面的程序会输出什么?大部分会说是 1,是也,非也。运行时正确结果为0。
i=++i;相当于以下二个语句(编译时出现警告,与i=i;警告相同):
i=i+1;
i=i;
i = i++;相当于以下三个语句:
int tmp = i;
i = i + 1;
i = tmp;
下面看看下面程序片段:
int i = 0, j = 0, y = 0;
i++;//相当于:i=i+1;
System.out.println(“i=” + i);// i=1
++i;//相当于:i=i+1;
System.out.println(“i=” + i);// i=2
i = i++;//相当于:int tmp=i;i=i+1;i=tmp;
System.out.println(“i=” + i);// i=2
i = ++i;//编译时出现警告,与i=i;警告相同。相当于:i=i+1;i=i;
System.out.println(“i=” + i);// i=3
j = i++;//相当于:int tmp=i;i=i+1;j=tmp;
System.out.println(“j=” + j);// j=3
System.out.println(“i=” + i);// i=4
y = ++i;//相当于:i=i+1;y=i;
System.out.println(“y=” + y);// y=5
System.out.println(“i=” + i);// i=5
10. Integer.MAX_VALUE + 1=?
System.out.println(Integer.MAX_VALUE + 1);
上面的程序输出多少?2147483647+1=2147483648?答案为-2147483648。
查看源码Integer.MAX_VALUE 为MAX_VALUE = 0x7fffffff;所以加1后为0x80000000,又
0x80000000为整型字面常量,满了32位,且最位为1,所以字面上等于 -0,但又由于 -0
就是等于0,所以-0这个编码就规定为最小的负数,32位的最小负数就是-2147483648。
11. -1<<32=?、-1<<65=?
如果左操作数是int(如果是byte、short、char型时会提升至int型再进行位操作)型,移位
操作符只使用其右操作数的低5位作为移位长度(也就是将右操作数除以32取余);如果左
操作数是long型,移位操作符只使用其右操作数的低6位作为移位长度(也就是将右操作
数除以64取余);
再看看下面程序片段就会知道结果:
System.out.println(-1 << 31);// -2147483648 向左移31%32=31位
System.out.println(-1 << 32);// -1 向左移32%32=0位
System.out.println(-1 << 33);// -2 向左移33%32=1位
System.out.println(-1 << 1);// -2 向左移1%32=1位
System.out.println(-1L << 63);// -9223372036854775808 向左移63%64=63位
System.out.println(-1L << 64);// -1 向左移64%64=0位
System.out.println(-1L << 65);// -2 向左移65%64=1位
System.out.println(-1L << 1);// -2 向左移1%64=1位
byte b = -1;// byte型在位操作前类型提升至int
System.out.println(b << 31);// -2147483648 向左移31%32=31位
System.out.println(b << 63);// -2147483648 向左移63%32=31位
short s = -1;// short型在位操作前类型提升至int
System.out.println(s << 31);// -2147483648 向左移31%32=31位
System.out.println(s << 63);// -2147483648 向左移63%32=31位
char c = 1;// char型在位操作前类型提升至int
System.out.println(c << 31);// -2147483648 向左移31%32=31位
System.out.println(c << 63);// -2147483648 向左移63%32=31位
声明: 除非转自他站(如有侵权,请联系处理)外,本文采用 BY-NC-SA 协议进行授权 | 嗅谱网
转载请注明:转自《Java常见疑难问题-数值表达式1》
本文地址:http://www.xiupu.net/archives-234.html
关注公众号:
微信赞赏
支付宝赞赏