2.6 表达式
表达式是由操作数和运算符按一定的语法形式组成的符号序列。每个表达式结果运算后都会产生一个确定的值,称为表达式的值。表达式的值是有类型的,该类型称为表达式类型。表达式类型由运算符和参与运算的数据的类型决定。可以是简单类型,也可以是复合类型。
一个常量或一个变量是最简单的表达式,表达式的值即该常量或变量的值。用运算符连接几个表达式构成的式子仍是表达式。
可以按表达式值的类型将表达式分类。
2.6.1 类型转换
在一个表达式中可能有不同类型的数据进行混合运算,这是允许的,但在运算时,Java将不同类型的数据转换成相同类型,再进行运算。
1.自动类型转换
整型、实型和字符型数据可以进行混合运算。在运算中,不同类型的数据先转换成相同类型,然后再进行运算。转换从低级到高级。可混合运算数据类型从低到高排列如下:
(低)->byte,short,char,int,long,float,double ->(高)
不同类型数据之间的转换规则如表2.14。
表2.14 不同类型数据之间的转换规则
2.强制类型转换
高级数据要转换为低级数据时,需进行强制类型转换。例如:
int a; char b; b=(char)a;
加括号的char告诉编译器想把整型的a转换为字符类型并把它赋值给b。
由于整型和字符型变量位长是不同的,整型是32 位长,字符型是16 位长,所以从整型转换到字符型可能会丢失信息。同样,当把64 位的长整型数转换为整型时,由于长整型可能有比32 位更多的信息,也很可能会丢失信息。即使两个量具有相同的位数,比如整型和单精度实型数据(都是32位),在转换小数时也会丢失信息。当进行类型转换要注意使目标类型能够容纳原始类型的所有信息,不会丢失信息的类型转换见表2.15。
表2.15 不会丢失信息的类型转换
需要说明的是,当执行一个这里并未列出的类型转换时可能并不总会丢失信息,不过进行这样一个理论上并不安全的转换总是很危险的。
3.表达式求值中的自动类型提升
在表达式的求值过程中,运算中间值的精度有时会超出操作数的取值范围。例如:
byte x = 30,y = 50,z = 100; int a = x * y / z;
在运算x*y项时,结果1500 已经超出了操作数byte类型的范围。为解决这类问题,Java语言在对表达式求值时,自动提升byte或short类型的数据为int类型的。Java语言对表达式求值的自动类型提升规则为:
(1)所有byte和short类型提升为int类型。
(2)若一个操作数是long类型,则整个表达式提升为long类型。
(3)若一个操作数是float类型,则整个表达式提升为float类型。
(4)若有double类型,则表达式值为double类型。
自动类型提升对数据的运算带来了方便,但也容易引起编译错误。例如:
byte x = 30; x = - x; // 编译错误!不能向byte变量赋已提升的int类型值
【例2.6】不同类型数据的混合运算。
class Promote { public static void main(String args[]) { byte b = 42; char c = 'a'; short s = 1024; int i = 50000; float f = 5.67f; double d = .1234; double result = (f * b) + (i / c) - (d * s);
System.out.println((f * b) + " + " + (i / c) + " - " + (d * s)); System.out.println("result = " + result); } }
程序运行结果如下:
238.14 + 515-126.3616 result = 626.7784146484375
2.6.2 优先级
在一个表达式中可能有各种运算符,Java语言规定了表达式中出现各种运算符的时候,哪种运算符先进行运算,哪种运算符后进行运算的运算符的运算顺序,称为运算符的优先级,它指明了同一表达式中多个运算符被执行的次序,同一级里的操作符具有相同的优先级。在表达式中,优先级高的运算符先进行运算。例如对于表达式:
a = b + c * d / (c ^ d)
Java处理时将按照表2.16 所列从最高优先级到最低优先级的次序进行。在上例中,因为括号优先级最高,所以先计算c^d,接着是c*d,然后除以c^d,最后,把上述结果与b的和存储到变量a中。
不论任何时候,若一时无法确定某种计算的执行次序时,可以使用加括号的方法明确为编译器指定运算顺序,这也是提高程序可读性的一个重要方法。例如,对表达式:
a | 4 + c >> b & 7 || b > a % 3
运算次序的理解就不如下面的表达式清晰,因为在下面的表达式中用括号()显式地表明了运算次序。
(a | (((4 + c) >> b) & 7)) || (b > (a % 3))
表2.16按从高到低的优先级列出了运算符。同一行中的运算符优先级相同。
表2.16 运算符优先级
2.6.3 结合性
在表达式中出现多个相同优先级的运算符时,就需要考虑结合性。结合性确定同级运算符的运算顺序。运算符有左结合性和右结合性两种。左结合性指的是从左向右使用运算符。例如二元算术运算符具有左结合性,计算a + b - c时,操作数b的左、右运算符 +、-是同级运算符,计算时,b先与左边的 + 结合,计算a + b,其和再与c相减;而右结合性是从右向左使用运算符。例如,赋值运算符具有右结合性,计算a = b = c时,操作数b的左、右运算符都是同级的赋值运算符,所以,先执行b = c,再执行a = b。