
代码 int i = 6; i += i - 1;,我们来逐步分析:
初始赋值:int i = 6;,即变量 i 的值初始化为 6。
表达式解析:i += i - 1; 这一行等价于 i = i + (i - 1);。
i - 1:当前 i 的值是 6,因此 i - 1 计算结果为 6 - 1 = 5。赋值:最终,i 被赋值为 11。
因此,在这段代码执行完毕后,i 等于 11。
要理解为什么 i 的值在表达式中不会中途变化,需要深入了解表达式求值的顺序和变量赋值的机制,尤其是在 Java 中是如何处理变量的。
在表达式 i += i - 1; 中,Java 遵循一个确定的求值顺序。这是基于 Java 语言的运算符优先级和求值顺序规则。Java 是一种严格从左到右求值的语言,这意味着:
i += i - 1; 时,右侧表达式 i - 1 首先被计算,然后将结果赋值给 i。i - 1 计算过程中,i 的值并不会在中途发生变化。所有在表达式中引用的 i,都是指向它在表达式开始时的值。具体来说,i - 1 的值是基于 i 的当前值来计算的,而这个计算过程不会影响当前 i 的值。
i 的初始值为 6。i - 1。此时,i 还是 6,因此 i - 1 = 6 - 1 = 5。i + 5 进行计算,此时 i 仍然是 6,故 i + 5 = 6 + 5 = 11。11 赋值给 i,所以 i = 11。整个右侧表达式的计算是在独立的内存空间中完成的,并不影响变量 i 在此时的值,直到计算结果最终赋值给 i。
i += i - 1; 中,表达式右侧 i - 1 的计算先于赋值进行。也就是说,右侧的表达式 i - 1 使用的 i 是表达式开始时的值,并且直到右侧计算完成后,新的值才会覆盖原来的 i。+= 操作符表示的是先计算再赋值。整个右侧表达式(i - 1)必须完全计算完毕,Java 才会将结果写回给左侧的变量 i。这是因为计算右侧表达式时,Java 会先保存当前的 i 值,并基于这个值计算整个表达式。然后,它再执行最终的赋值操作。即:
i - 1 时,i 的值仍然是 6。i + (i - 1) 时,i 的值仍然是 6。11 赋值给 i。在 Java 的内存模型(JMM)中,局部变量存储在每个线程的栈内存中。由于这段代码是在单线程中运行的,并且没有跨线程的可见性问题,局部变量的值始终是对该线程可见且一致的。在整个求值过程中,i 的值不会因为计算的进展而改变。
i。i 的值在内存中是稳定的,不会中途发生变化。编译器在处理表达式时,也会进行一定的优化,但这些优化不会影响程序的语义。在这种情况下,编译器可能会将变量 i 的值暂时保存在寄存器中,直到计算完成之后才将最终结果写入内存中的 i。这同样保证了 i 的值在表达式求值过程中不会改变。
虽然在单线程的情况下不涉及 Java 内存模型(JMM)的复杂性,但在多线程场景下,如果变量 i 是共享的,且没有正确的同步机制(如 volatile 关键字或 synchronized 块),那么不同线程对 i 的操作可能会产生可见性问题,导致线程间的 i 值不一致。但是在当前的单线程环境下,JMM 不是重点。

bipush 6 (0: bipush 6)
6 压入操作数栈中。此时,操作数栈内容为:[6]istore_1 (2: istore_1)
6 保存到局部变量表的索引 1 处,即 i。同时,栈被清空:[i=6][]iload_1 (3: iload_1)
i 的值(6)到操作数栈中。此时,操作数栈内容为:[6]iload_1 (4: iload_1)
i 的值(6)到操作数栈中。此时,操作数栈内容为:[6, 6]iconst_1 (5: iconst_1)
1 压入操作数栈。此时,操作数栈内容为:[6, 6, 1]isub (6: isub)
isub 是整数减法操作,从操作数栈中弹出两个值,执行减法运算,将结果压回栈顶:6 和 1。6 - 1 = 5。5 压回栈顶。此时,操作数栈内容为:[6, 5]iadd (7: iadd)
iadd 是整数加法操作,同样从操作数栈中弹出两个值,执行加法运算,将结果压回栈顶:
6 和 5。6 + 5 = 11。11 压回栈顶。此时,操作数栈内容为:[11]istore_1 (8: istore_1)
istore_1 将栈顶的值 11 存回局部变量表的索引 1 处,更新 i 的值为 11:[i=11][]在表达式 i += i - 1 的计算过程中,Java 虚拟机(JVM)按照以下原则来执行指令:
JVM 使用操作数栈来执行大部分指令,并且局部变量表和操作数栈是相互独立的。每次从局部变量表加载 i 的值到操作数栈时,栈中的操作仅影响栈,而不会影响局部变量表中 i 的值。局部变量表中的 i 只有在 istore_1 这一指令执行后才更新。这就是为什么即使执行了 i - 1 的计算,第一个 i 的值仍然保持为 6,直到所有运算结束。
i 的值在被加载到操作数栈后,在局部变量表中的值不会改变,直到最终计算完成后通过 istore 进行赋值。i - 1 的减法运算,再执行 i + (i - 1) 的加法,整个计算过程使用的是最初从局部变量表中加载的 i 值。11 才会通过 istore_1 更新 i 的值。因此,i 在中途不会变成 5,而是一直保持为 6,直到计算结果 11 最后被存储到局部变量表中。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。