2、表达式类型的陷阱
Java是一门强类型语言,不仅每个变量具有指定的数据类型,它的表达式也具有指定的数据类型。因此,使用表达式一定要注意它的数据类型。
提示:强类型语言,通常具有以下两个基本特征。
- 所有的变量必须先声明,然后才能使用,声明变量时必须指定该变量的数据类型。
- 一旦某个变量的数据类型确定下来,那么这个变量将永远只能接受该类型的值,不能接受其他类型的值。
2、1 表达式类型的自动升级
Java语言规定:当一个算术表达式中包含多个基本类型的值是,整个算术表达式的数据类型将自动提升。Java语言中的自动提升规则如下所示。
- 所有的byte类型、short类型和char类型将被提升到int类型。
- 整个算术表达式的数据类型自动提升到与表达式中最高等级操作数同样的类型。
public class AutoPromote {
public static void main(String[] args) {
short sValue = 5;
//sValue = sValue - 2; ① 报错:Type mismatch: cannot convert from int to short
byte b = 20;
char c = 'a';
int i = 30;
double d = .315;
double result = b + c + i + d; //②
System.out.println("result = " + result); //该表达式最高等级操作数为d,则表达式类型为double类型
int val = 3;
int result1 = 19 / val; //③
System.out.println(result1); //表达式中两个操作数类型都是int,故表达式类型为int
System.out.println("hello!" + 'a' + 6); //④
System.out.println('a' + 6 + "hello!"); //⑤
}
}
输出结果为:
result = 147.315
6
hello!a6
103hello!
6
hello!a6
103hello!
上面程序中①行代码无法通过编译,因为sValue是一个short类型变量,但sValue-2表达式的类型是int类型(与2的类型保持一致)。②行代码完全正确,右边算术表达式中等级最高的是d,它是double类型,因此表达式的类型是double类型。③行代码完全正确,虽然19/val不能整除,但由于val是int类型,因此19/val表达式也是int类型。④、⑤行代码是表达式自动转换为字符串,当基本类型的值和String进行连接运算时,系统会将基本类型的值自动转换为String类型,这样才可让连接运算正常进行。
2、2 复合赋值运算符的陷阱
在2、1节中下面语句将会引起编译错误。
short sValue = 5;
sValue = sValue - 2;
因为sValue-2表达式的类型将自动提升为int类型,所以程序将一个int类型的值赋给short类型的变量时聚会导致了编译错误。但是如果将上面代码改为如下的代码则可以通过编译。
short sValue = 5;
sValue -= 2;
Java语言几乎允许所有的双目运算符和=一起结合成复合赋值运算符,如+=、-=、*=、/=、%=、<<=、>>=、>>>=、&=、^=、|=等。
根据Java语言规范,复合赋值运算符包含了一个隐式类型转换。
a = a + 5;
a += 5;
实际上,a+=5等价于a=(a的类型)(a+5);,这就是复合赋值运算符中包含的隐式类型转换。换句话来说,复合赋值运算符会自动将它计算的结果值强制类型转换为其左侧变量的类型。如果结果的类型与该变量的类型相同,那么这个转换不会造成任何影响。如果结果值的类型比该变量的类型大,那么复合赋值运算符将会执行一次强制类型转换。
public class CompositeAssign {
public static void main(String[] args) {
short s = 5;
s += 10; //①
System.out.println(s);
s += 100000; //②
System.out.println(s);
}
}
输出结果为:
15
-31057
-31057
对于①行代码等价于s = (short)(s + 10);执行完上面语句们可以看到s依然是一个short类型变量,值为15。对于②行代码等价于s = (short)(s + 100000);,问题在于:short类型的变量只能接受-32768~32767之间的整数,因此上面的程序最后输出的会是-31057。
由此可见,复合赋值运算发简单、方便,而且具有性能上的优势,但是复合赋值运算符可能有一定的危险。为了避免潜在的危险,应该注意一下几种情况。
- 将符合赋值运算符运用于byte、short或char等类型的变量。
- 将符合赋值运算符运用于int类型的变量,而表达式右侧是long、float或double类型的值。
- 将符合赋值运算符运用于float类型的变量,而表达式右侧是double类型的值。
2、3 Java7新增的二进制整数
从Java7开始,Java增加了二进制整数支持,但这种二进制整数也引入了新的陷阱。
public class BinaryTest {
public static void main(String[] args) {
int i = 0b1010_1010;
byte b = (byte)0b1010_1010;
System.out.println(i == b);
}
}
输出结果为:
false
造成这种问题的原因是如下两条规则。
- 直接使用整数直接量时,系统会将它当成int类型处理。
- byte类型的整数虽然可以包含8位,但最高位时符号位。