Java 表达式运算符全解析
1. 加法运算符(+ 和 -)
在 Java 中,加法运算符(+)和减法运算符(-)可用于数值类型的操作。
-
加法运算
:当对两个数值类型的操作数应用二元 + 运算符时,它会执行加法操作,得出操作数的和。
-
减法运算
:二元 - 运算符执行减法操作,得到两个数值操作数的差。
在进行运算时,会对操作数执行二元数值提升,这其中包括值集转换和可能的拆箱转换。加法表达式的类型是操作数的提升类型。若提升类型为 int 或 long,则执行整数运算;若为 float 或 double,则执行浮点运算。
加法运算在操作数表达式没有副作用时是可交换的。整数加法在操作数类型相同时是可结合的,但浮点加法不具有结合性。
整数加法若发生溢出,结果是数学和在足够大的二进制补码格式下的低位。而浮点加法的结果依据 IEEE 754 算术规则确定:
| 操作数情况 | 加法结果 |
| — | — |
| 任一操作数为 NaN | NaN |
| 两个相反符号的无穷大相加 | NaN |
| 两个相同符号的无穷大相加 | 该符号的无穷大 |
| 无穷大与有限值相加 | 无穷大操作数 |
| 两个相反符号的零相加 | 正零 |
| 两个相同符号的零相加 | 该符号的零 |
| 零与非零有限值相加 | 非零操作数 |
| 两个非零有限值,大小相同、符号相反 | 正零 |
若加法表达式是 FP - strict 的,当类型为 float 时,必须选择 float 值集;为 double 时,必须选择 double 值集。若不是 FP - strict 的,float 类型可选择 float 值集或 float - extended - exponent 值集;double 类型可选择 double 值集或 double - extended - exponent 值集。
减法运算中,对于整数和浮点运算,a - b 与 a + (-b) 的结果相同。不过对于浮点操作数,从零减去操作数与取反不同,例如当 x 为 +0.0 时,0.0 - x 是 +0.0,而 -x 是 -0.0。并且,数值加法运算符的计算不会抛出运行时异常。
2. 移位运算符(<<、>> 和 >>>)
移位运算符包括左移(<<)、有符号右移(>>)和无符号右移(>>>)。左操作数是要移位的值,右操作数指定移位距离。
移位运算符在语法上是左结合的,会对每个操作数分别执行一元数值提升。操作数在一元数值提升后必须是基本整数类型,否则会出现编译时错误。移位表达式的类型是左操作数的提升类型。
若左操作数的提升类型为 int,仅使用右操作数的最低 5 位作为移位距离;若为 long,则仅使用最低 6 位。
运行时,移位操作在左操作数的二进制补码整数表示上进行。n << s 相当于 n 左移 s 位,等同于乘以 2 的 s 次幂;n >> s 是 n 右移 s 位并进行符号扩展,结果为 floor(n / 2^s);n >>> s 是 n 右移 s 位并进行零扩展,具体规则如下:
- 若 n 为正数,结果与 n >> s 相同。
- 若 n 为负数且左操作数类型为 int,结果等于 (n >> s) + (2 << ~s)。
- 若 n 为负数且左操作数类型为 long,结果等于 (n >> s) + (2L << ~s)。
下面是移位运算的流程 mermaid 图:
graph TD;
A[开始] --> B[判断左操作数提升类型];
B -->|int| C[取右操作数低 5 位为移位距离];
B -->|long| D[取右操作数低 6 位为移位距离];
C --> E[执行移位操作];
D --> E;
E --> F[输出结果];
F --> G[结束];
3. 关系运算符
关系运算符包含数值比较运算符(<、>、<=、>=)和 instanceof 运算符。关系表达式的类型始终为 boolean。
3.1 数值比较运算符
操作数类型必须能转换为基本数值类型,会对操作数执行二元数值提升。若提升类型为 int 或 long,执行有符号整数比较;若为 float 或 double,执行浮点比较。
浮点比较依据 IEEE 754 标准:
| 操作数情况 | 比较结果 |
| — | — |
| 任一操作数为 NaN | false |
| 非 NaN 值 | 负无穷 < 所有有限值 < 正无穷 |
| 正零和负零 | 相等 |
对于整数操作数或非 NaN 的浮点操作数,各运算符规则如下:
- < 运算符:左操作数小于右操作数时为 true,否则为 false。
- <= 运算符:左操作数小于或等于右操作数时为 true,否则为 false。
- > 运算符:左操作数大于右操作数时为 true,否则为 false。
- >= 运算符:左操作数大于或等于右操作数时为 true,否则为 false。
3.2 instanceof 运算符
instanceof 运算符的左操作数必须是引用类型或 null 类型,右操作数必须是可具体化的引用类型。若将左操作数强制转换为右操作数类型会出现编译时错误,那么 instanceof 表达式也会产生编译时错误。
运行时,若左操作数不为 null 且可强制转换为右操作数类型而不抛出 ClassCastException,则结果为 true,否则为 false。例如:
class Point { int x, y; }
class Element { int atomicNumber; }
class Test {
public static void main(String[] args) {
Point p = new Point();
Element e = new Element();
if (e instanceof Point) { // compile-time error
System.out.println("I get your point!");
p = (Point)e; // compile-time error
}
}
}
此程序会出现两个编译时错误,因为 Element 及其子类的实例不可能是 Point 及其子类的实例。若 Point 是 Element 的子类,那么强制转换和 instanceof 表达式就会有效。
4. 相等运算符(== 和 !=)
相等运算符包括 ==(等于)和 !=(不等于),表达式类型始终为 boolean。
4.1 数值相等运算符
若操作数均为数值类型,或一个为数值类型,另一个可转换为数值类型,则会对操作数执行二元数值提升。若提升类型为 int 或 long,执行整数相等性测试;若为 float 或 double,执行浮点相等性测试。
浮点相等性测试遵循 IEEE 754 标准:
| 操作数情况 | == 结果 | != 结果 |
| — | — | — |
| 任一操作数为 NaN | false | true |
| 正零和负零 | true | false |
| 其他情况 | 两值相等为 true,不等为 false | 两值相等为 false,不等为 true |
例如,
-0.0 == 0.0
为 true,
Float.isNaN
和
Double.isNaN
方法可用于测试值是否为 NaN。
4.2 布尔相等运算符
若操作数均为 boolean 类型或一个为 boolean 类型,另一个为 Boolean 类型,会对可能需要的操作数进行拆箱转换。若操作数均为 true 或均为 false,
==
结果为 true,否则为 false;
!=
结果与之相反。
!=
应用于布尔操作数时与
^
运算符行为相同。
4.3 引用相等运算符
若操作数均为引用类型或 null 类型,进行对象相等性测试。若无法通过强制类型转换将一个操作数的类型转换为另一个操作数的类型,则会出现编译时错误。运行时,若操作数均为 null 或引用同一个对象或数组,
==
结果为 true,否则为 false;
!=
结果与之相反。使用
==
比较 String 引用时,比较的是是否引用同一个 String 对象,若要比较内容,需使用
s.equals(t)
方法。
5. 按位和逻辑运算符
按位和逻辑运算符包括按位与(&)、异或(^)和按位或(|),各运算符优先级不同,
&
最高,
|
最低,且在语法上是左结合的,若操作数表达式无副作用则是可交换和可结合的。
5.1 整数按位运算符
当操作数可转换为基本整数类型时,先对操作数执行二元数值提升,表达式类型为操作数的提升类型。
-
&
:结果值是操作数的按位与。
-
^
:结果值是操作数的按位异或。
-
|
:结果值是操作数的按位或。
例如:
int result1 = 0xff00 & 0xf0f0; // 结果为 0x0f00
int result2 = 0xff00 ^ 0xf0f0; // 结果为 0x0ff0
int result3 = 0xff00 | 0xf0f0; // 结果为 0xfff0
5.2 布尔逻辑运算符
当操作数均为 boolean 或 Boolean 类型时,表达式类型为 boolean,操作数按需进行拆箱转换。
-
&
:两个操作数均为 true 时结果为 true,否则为 false。
-
^
:操作数不同时结果为 true,否则为 false。
-
|
:两个操作数均为 false 时结果为 false,否则为 true。
6. 条件与运算符(&&)
条件与运算符
&&
与
&
类似,但仅当左操作数为 true 时才计算右操作数。
条件与运算符在语法上是左结合的,且在副作用和结果值方面是完全可结合的。每个操作数必须为 boolean 或 Boolean 类型,表达式类型始终为 boolean。
下面是条件与运算符的执行流程 mermaid 图:
graph TD;
A[开始] --> B[计算左操作数];
B -->|false| C[结果为 false,结束];
B -->|true| D[计算右操作数];
D --> E[根据右操作数结果确定最终结果];
E --> F[结束];
综上所述,Java 中的各种运算符在不同场景下有着不同的使用规则和特性。理解这些运算符的行为对于编写正确、高效的 Java 代码至关重要。在实际编程中,需要根据具体需求选择合适的运算符,并注意运算符的优先级、结合性以及操作数类型等问题,以避免出现意外的结果。
超级会员免费看
2726

被折叠的 条评论
为什么被折叠?



