i++和++i的联系与区别!超详细原理分析!

1. i++和++i的基本概念

在几乎所有的命令式编程语言中,必然都会有 i++ 和 ++i 这种语法。有些语言中 i++ 和 ++i 既可以作为左值又可以作为右值,笔者专门测试了一下,在 Java 语言中,这两条语句都只能作为右值,而不能作为左值。同时,它们都可以作为独立的一条指令执行。

关于 i++ 和 ++i 的区别,稍微有经验的程序员都或多或少都是了解的,为了文章的完整性,本文也通过实例来简单地解释一下。

案例 1:

/** * @author pcwl * @description i++ 和 ++i 详解 */public class Test {
public static void main(String[] args) { int i= 1; int j = i++; System.out.println("j = " + j); System.out.println("i = " + i); }}
运行结果:

案例 2:
public class Test {
public static void main(String[] args) { int i= 1; int j = ++i; System.out.println("j = " + j); System.out.println("i = " + i); }}
运行结果:

上面的例子中可以看到,无论是 i++ 和 ++i 指令,对于 i 变量本身来说是没有任何区别的,指令执行的结果都是 i 变量的值加 1。而对于 j 来说前 ++ 和后 ++ 结果却不一样了。
int i = 1;int j = i++; // 先将i的原始值(1)赋值给变量j(1),然后i变量的值加1int j = ++i; // 先将i变量的值加1,然后将i的当前值(2)赋值给变量j(2)

2. i++ 和 ++i 的实现原理

接下来让我们深入到编译后的字节码层面上来了解 i++ 和 ++i 的实现原理,为了方便对比,将这两个指令分别放在 2 个不同的方法中执行,源代码如下:
/** * @author pcwl * @description i++ 和 ++i 详解 */public class Test {
public void testIPlus() { int i = 0; int j = i++; }
public void testPlusI() { int i = 0; int j = ++i; }}
将上面的源代码编译之后,使用 javap 命令查看编译生成的代码(忽略次要代码)如下:
...{  ...
public void testIPlus(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=3, args_size=1 0: iconst_0 // 生成整数0 1: istore_1 // 将整数0赋值给1号存储单元(即变量i) 2: iload_1 // 将1号存储单元的值加载到数据栈(此时 i=0,栈顶值为0) 3: iinc 1, 1 // 1号存储单元的值+1(此时 i=1) 6: istore_2 // 将数据栈顶的值(0)取出来赋值给2号存储单元(即变量j,此时i=1,j=0) 7: return // 返回时:i=1,j=0 LineNumberTable: line 4: 0 line 5: 2 line 6: 7
public void testPlusI(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=3, args_size=1 0: iconst_0 // 生成整数0 1: istore_1 // 将整数0赋值给1号存储单元(即变量i) 2: iinc 1, 1 // 1号存储单元的值+1(此时 i=1) 5: iload_1 // 将1号存储单元的值加载到数据栈(此时 i=1,栈顶值为1) 6: istore_2 // 将数据栈顶的值(1)取出来赋值给2号存储单元(即变量j,此时i=1,j=1) 7: return // 返回时:i=1,j=1 LineNumberTable: line 9: 0 line 10: 2 line 11: 7}...
可以从上面的字节码文件看出,造成结果不同的原因就是:“1 号存储单元的值加 1 的操作”和“将 1 号存储单元的值加载到数据栈”的先后顺序造成的。如果前者在后者之前,则结果就是 1,反之则为 0。

3. i++ 和 ++i 使用的一些坑

i++ 和 ++i 在一些特殊场景下可能会产生意想不到的结果,本节介绍两种会导致结果混乱的使用场景,并剖析其原因。

01

案例1:i=i++导致的结果异常  

public class Test {    public static void main(String[] args) {        int i = 0;        i = i++;        System.out.println("i = " + i);   // 0    }}
运行结果:

正常来讲,执行的结果应该是:i = 1,实际结果却是:i = 0,这多少会让人有些诧异。为什么会出现这种情况呢?我们来从编码后的代码中找答案。上面的代码编译后的核心代码如下:

0: iconst_0                   // 生成整数01: istore_1                   // 将整数0赋值给1号存储单元(即变量i,i=0)2: iload_1                    // 将1号存储单元的值加载到数据栈(此时 i=0,栈顶值为0)3: iinc          1, 1         // 1号存储单元的值+1(此时 i=1)6: istore_1                   // 将数据栈顶的值(0)取出来赋值给1号存储单元(即变量i,此时i=0)7: getstatic      #16         // 下面是打印到控制台指令10: new           #22              13: dup14: ldc           #24                16: invokespecial #26                19: iload_120: invokevirtual #29               23: invokevirtual #33                26: invokevirtual #37                29: return
从编码指令可以看出,i 被栈顶值所覆盖,导致最终 i 的值仍然是 i 的初始值。 无论重复多少次 i = i++ 操作,最终 i 的值都是其初始值。
实际上: i++ 有中间缓存变量,,i = i++ 等价于:
temp = i;i = i + 1;i = temp;

所以 i 不变, 依然是0。

02

案例2 

public class Test {    public static void main(String[] args) {        int i = 0;        i++;        System.out.println("i = " + i);   // 1    }}
运行结果:

和上面上面的两端代码中唯一的差别就是 i++ 的结果有没有赋值给 i ,但是输出的 i 的结果一个加了1,而1个没有加。 这是为什么呢?我们看下编译的字节码文件:
0: iconst_0              // 生成整数0                1: istore_1              // 将整数0赋值给1号存储单元(即变量i,i=0)2: iinc          1, 1    // 1号存储单元的值+1(此时 i=1)5: getstatic     #2                  8: new           #3                  11: dup12: invokespecial #4                  15: ldc           #5                  17: invokevirtual #6                  20: iload_121: invokevirtual #7                  24: invokevirtual #8                  27: invokevirtual #9                  30: return

    
    

--------  END  ---------

最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。关注公众号并回复 888 领取,更多内容陆续奉上   


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值