2. 复合赋值运算符概述

在本节中,我们将介绍 Java 中的复合赋值运算符(Compound Assignment Operators),包括它们的类型以及 Java 是如何对这些表达式进行求值的。

我们还会解释隐式类型转换(implicit casting)是如何发生的。

3. 复合赋值运算符

赋值运算符是一种二元运算符,它将右侧表达式的值赋给左侧的变量。最简单的赋值运算符是 =

int x = 5;

这行代码声明了一个新的变量 x,并将其赋值为 5,同时整个表达式也返回 5

复合赋值运算符提供了一种更简洁的方式,将算术或位运算操作与赋值操作合并执行。

例如,下面这两行乘法操作是等价的,变量 ab 最终值相同:

int a = 3, b = 3, c = -2;
a = a * c; // 简单赋值运算符
b *= c;    // 复合赋值运算符

⚠️ 注意:复合赋值运算符左侧的变量必须已经声明,不能用于声明新变量

= 运算符一样,复合赋值运算符也会返回赋值后的结果:

long x = 1;
long y = (x += 2);

最终,xy 的值都是 3

表达式 (x += 2) 实际上做了两件事:

  1. x 的值加 2,变成 3
  2. 返回这个结果 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 在对复合赋值运算符求值时,会根据左操作数是否是数组元素,采用不同的处理流程。

当左操作数不是数组元素时:

  1. 验证左操作数是否为已声明的变量
  2. 保存左操作数的当前值
  3. 计算右操作数的值
  4. 执行对应的二元运算
  5. 将运算结果隐式转换为左操作数的类型
  6. 将转换后的结果赋值给左操作数

当左操作数是数组元素时:

  1. 验证数组表达式是否合法,若非法则抛出 NullPointerExceptionArrayIndexOutOfBoundsException
  2. 保存数组中指定索引位置的值
  3. 计算右操作数
  4. 判断数组元素是基本类型还是引用类型,后续处理流程与非数组情况一致

如果在任意一步出错,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)

其中 TE1 的类型。

来看一个经典例子:

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 仓库 中找到。


原始标题:Java Compound Operators | Baeldung