i++ 和 ++i
计算原则
i++ 和 ++i 都是先自增后返回,只不过返回值有不同。后文有代码指令解释。
对此有个简单的记忆方法,变量在 ++ 前返回的就是自增前的值,变量在 ++ 后返回的就是自增后的值。
另外,java 的表达式是从左到右执行的,即使只是一个变量,它也是一个表达式。
记住,用逻辑运算符(+、-、*、/)分割的就是一个表达式。
但表达式的执行也要遵循计算式的优先级。
计算分析
// 4: iload_1 // 本地局部变量表索引为 1 的变量槽值 0 入栈( i = 0 中的 0 )到栈顶,此时操作数栈模型为:顶部 0 <- 尾部
// 5: iinc 1, 1 // 本地局部变量表索引为 1 的变量槽值 0 自增 1(i 自增 1),此时 i = 1
// 8: iinc 2, 1 // 本地局部变量表索引为 2 的变量槽值 0 自增 1(j 自增 1),此时 j = 1
// 11: iload_2 // 本地局部变量表索引为 2 的变量槽值 1 ( j = 1 中的值 1 )压入栈顶,此时操作数栈模型为:顶部 1 <- 0 尾部
// 12: iload_1 // 本地局部变量表索引为 1 的变量槽值 1 (i = 1 中的值 1 )压入栈顶,此时操作数栈模型为:顶部 1 <- 1 <- 0 尾部
// 13: imul // 操作数栈中栈顶的两个操作数 1,1 出栈运行乘法,计算结果压入栈顶,此时操作数栈模型为:顶部 1 <- 0 尾部
// 14: iadd // 操作数栈中栈顶的两个操作数 1,0 出栈运行加法,计算结果压入栈顶,此时操作数栈模型为:顶部 1 <- 尾部
// 15: istore_3 // 操作数栈中栈顶的操作数 1 出栈到本地变量 k,k = 1,此时操作数栈模型为:顶部 <- 尾部
int i = 0;
int j = 0;
int k = i++ + ++j * i;
根据 i++ 和 ++i 的计算原则,和计算式优先级
先算乘除、后算加减
此时表达式可以拆分为两块 i++ 表达式的值加上 ++j * i 的值
即 (i++) + (++j * i)
计算第一步:i++ 返回自增前的 i,表达式为 0 + (++j * 1)
为什么是这样呢?因为 i++ 执行后返回了自增前的 i,即 i++ 这个表达式返回值为 0
而实际上 i++ 这个表达式已经改变了 i 的值,执行 i++ 表达式之后,i 已经为 1 了
计算第二步:++j 表达式返回自增后的 j,即 j++ 这个表达式的返回值为 1,此时表达式为 0 + (1 * 1)
计算第三步:返回结果 1
图解分析
指令: 0: iconst_0
读取 “int i = 0” 的值 “0” 压入操作数栈栈顶
指令:1: istore_1
操作数栈栈顶的操作数 “0” 写入索引为 1 的本地局部变量变量槽 Sloat,
执行 “int i = 0” 赋值操作,把值 0 赋给变量 i

指令:2: iconst_0
读取 “int j = 0” 的值 “0” 压入操作数栈栈顶

指令:3: istore_2
操作数栈栈顶的操作数 “0” 写入索引为 2 的本地局部变量变量槽 Sloat,
执行 “int j = 0” 赋值操作,把值 0 赋给变量 j

指令:4: iload_1
从这条指令开始执行 “int k = i++ + ++j * i;”
本地局部变量索引为 1 的变量槽值 0 入栈,
即 “=” 右侧的表达式从左向右执行,这条指令就是把 “i++” 中的 i 的入栈。

指令:5: iinc 1, 1
本地局部变量索引为 1 的变量槽值 0 自增加 1,即执行 “i++” 中的 “++” 进行自增。
此时局部变量表索引为 1 的变量槽中的值为 1,即此时实际上是 “i = 1” ,
但是操作数栈中依旧保留自增前的值 0。此时表达式可以写成 “0 + (++j * 1)”

指令:8: iinc 2, 1
本地局部变量索引为 2 的变量槽值 0 自增加 1,即执行 “++j” 中的 “++” 进行自增。
此时局部变量表索引为 2 的变量槽中的值为 1,即此时实际上是 “j = 1” 。
此时表达式可以写成 “0 + (1 * 1)”

指令:11: iload_2
本地局部变量索引为 2 的变量槽值 1 入操作数栈栈顶,此时操作数栈有两个值 1,0

指令:12: iload_1
本地局部变量索引为 1 的变量槽值 1 入操作数栈栈顶,此时操作数栈有三个值 1,1,0

指令:13: imul
操作数栈栈顶的前两个操作数做乘积运算(执行 1 * 1)并出栈,计算结果 1 入操作数栈栈顶,此时操作数栈有两个值 1,0

指令:14: iadd
操作数栈栈顶的前两个操作数做累和运算(执行 0 + 1)并出栈,
计算结果 1 入操作数栈栈顶,此时操作数栈有一个值 1

指令:15: istore_3
操作数栈栈顶的操作数 1 写入本地局部变量表索引为 3 的变量槽,即 k = 1

参考指令
iload_1 本地局部变量表索引为 1 的槽位的 int 值入栈顶
iinc 1, 1 本地局部变量表索引为 1 的槽位的 int 值自增 1
istore_1 栈顶的值出栈写入本地局部变量表索引为 1 的槽位
imul 操作数栈前两个运行乘法,计算结果压入栈顶
iadd 操作数栈前两个运行加法,计算结果压入栈顶
口诀
变量在前返回前;
变量在后返回后;
从左到右算有序;
计算优先不可忘;
演示代码
public static void main (String[] args) { // 0: iconst_0 // 1: istore_1 // 2: iconst_0 // 3: istore_2 // 4: iload_1 // 入栈 i = 0 中的 0 到栈顶 // 5: iinc 1, 1 // 本地变量表中的 i 自增 1,此时 i = 1 // 8: istore_1 // 栈顶的 0 出栈到本地变量表 i,这个操作导致 i 重新变成 0 // 这个 i++ 的操作结束,总结就是入栈、自增、回写(返回) // 9: iinc 2, 1 // 本地变量表中的 j 自增 1,此时 j = 1 // 12: iload_2 // 入栈 j = 1 中的 1 到栈顶 // 13: istore_2 // 栈顶的 1 出栈到本地变量表 j,这个操作导致 j 依旧为 1 // 这个 ++j 的操作结束,总结就是自增、入栈、回写(返回) // 14: return // 所以说,无论是 i++ 还是 ++i,都是先自增最后返回 // 只不过二者入栈的时机不同,导致最终返回值不同 // i++ 是先入栈,最终返回的就是自增之前入栈的值 0 // ++i 是后入栈,最终返回的就是自增之后入栈的值 1 int i = 0; int j = 0; i = i++; j = ++j; }
模型
Java 代码执行模型
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)