2. 复合赋值运算符概述
在本节中,我们将介绍 Java 中的复合赋值运算符(Compound Assignment Operators),包括它们的类型以及 Java 是如何对这些表达式进行求值的。
我们还会解释隐式类型转换(implicit casting)是如何发生的。
3. 复合赋值运算符
赋值运算符是一种二元运算符,它将右侧表达式的值赋给左侧的变量。最简单的赋值运算符是 =
:
int x = 5;
这行代码声明了一个新的变量 x
,并将其赋值为 5
,同时整个表达式也返回 5
。
✅ 复合赋值运算符提供了一种更简洁的方式,将算术或位运算操作与赋值操作合并执行。
例如,下面这两行乘法操作是等价的,变量 a
和 b
最终值相同:
int a = 3, b = 3, c = -2;
a = a * c; // 简单赋值运算符
b *= c; // 复合赋值运算符
⚠️ 注意:复合赋值运算符左侧的变量必须已经声明,不能用于声明新变量。
像 =
运算符一样,复合赋值运算符也会返回赋值后的结果:
long x = 1;
long y = (x += 2);
最终,x
和 y
的值都是 3
。
表达式 (x += 2)
实际上做了两件事:
- 将
x
的值加 2,变成3
- 返回这个结果
3
并赋值给y
4. 复合赋值运算符的类型
Java 提供了 11 种复合赋值运算符,可以分为两类:算术运算符 和 位运算符。
算术类复合运算符:
- 加法赋值:
+=
- 减法赋值:
-=
- 乘法赋值:
*=
- 除法赋值:
/=
- 取模赋值:
%=
位运算类复合运算符:
- 按位与赋值:
&=
- 按位异或赋值:
^=
- 按位或赋值:
|=
- 左移赋值:
<<=
- 右移赋值:
>>=
- 无符号右移赋值:
>>>=
来看几个实际例子:
// 简单赋值
int x = 5; // x = 5
// 加法赋值
x += 5; // x = 10
// 减法赋值
x -= 2; // x = 8
// 乘法赋值
x *= 2; // x = 16
// 取模赋值
x %= 3; // x = 1
// 按位与赋值
x &= 4; // x = 0
// 按位异或赋值
x ^= 4; // x = 4
// 按位或赋值
x |= 8; // x = 12
可以看到,这些运算符的语法风格非常统一,使用起来简单粗暴。
5. 复合运算符的求值过程
Java 在对复合赋值运算符求值时,会根据左操作数是否是数组元素,采用不同的处理流程。
当左操作数不是数组元素时:
- 验证左操作数是否为已声明的变量
- 保存左操作数的当前值
- 计算右操作数的值
- 执行对应的二元运算
- 将运算结果隐式转换为左操作数的类型
- 将转换后的结果赋值给左操作数
当左操作数是数组元素时:
- 验证数组表达式是否合法,若非法则抛出
NullPointerException
或ArrayIndexOutOfBoundsException
- 保存数组中指定索引位置的值
- 计算右操作数
- 判断数组元素是基本类型还是引用类型,后续处理流程与非数组情况一致
如果在任意一步出错,Java 会立即终止后续流程。
来看几个数组操作的例子:
int[] numbers = null;
// 尝试对 null 数组进行复合赋值
numbers[2] += 5;
不出所料,这会抛出 NullPointerException
。
但如果给数组赋初值:
int[] numbers = {0, 1};
// 索引越界
numbers[2] += 5;
虽然不会空指针了,但会抛出 ArrayIndexOutOfBoundsException
。
修正索引后:
int[] numbers = {0, 1};
// 正常操作
numbers[1] += 5; // numbers[1] = 6
✅ 最终 numbers[1]
的值变为 6
。
6. 隐式类型转换
复合赋值运算符的一大优势是它能自动进行类型转换(implicit casting)。
形式上,一个形如 E1 op= E2
的复合赋值表达式等价于:
E1 = (T)(E1 op E2)
其中 T
是 E1
的类型。
来看一个经典例子:
long number = 10;
int i = number;
i = i * number; // ❌ 编译错误
为什么会编译失败?
Java 会在混合运算中自动将较小类型提升为较大类型,但当试图将大类型赋值给小类型时就会报错。
在这个例子中,i
会被提升为 long
,然后与 number
相乘得到 10L
,再试图将 long
赋值给 int
类型的 i
,导致编译错误。
可以通过显式类型转换修复:
i = (int)(i * number); // ✅ 显式转换
而复合赋值运算符则会自动处理这个转换:
i *= number; // ✅ 隐式转换,自动 cast
这行代码可以正常运行,自动将乘法结果转换为 int
并赋值给 i
。
7. 总结
本文介绍了 Java 中的复合赋值运算符,包括它们的类型和求值机制。我们也详细说明了隐式类型转换的工作原理,这是复合运算符非常实用的一个特性。
📌 所有示例代码都可以在我们的 GitHub 仓库 中找到。