i++与++i 详解 真正区别 一看就懂 深层理解

问题描述
记得刚开始学编程的时候还是从c语言开始的,还是看的谭浩强写的那本书,上面对介绍i++和++i的区别如下:
i++是先赋值,然后再自增;++i是先自增,后赋值。
用代码表示就是:
若 a = i++; 则等价于 a=i;i=i+1;
而 a = ++i; 则等价于 i=i+1;a=i;

那么事实真是这样么,只是曾经我深信不疑,但是直到我看到下面这段代码:

 @Test
  public void test(){
      int i = 0;
      i=i++;
      System.out.println(i);
  }

如果按原先定义,就应该是i=i;i=i+1; 那么结果就应该是1;但是很遗憾结果是0;所以得知原先定义有误,至少是不准确的。

模拟++的机制
那么真实的机制是怎么样的呢?我简单用代码模拟一下它的效果:

int i;
 
@Test
public void testAddI() {
    i = 0;
    i = lastAdd();
    System.out.println(i);
    i = 0;
    i = firstAdd();
    System.out.println(i);
}


//模拟i++的机制
public int lastAdd() {
    //操作i前对其值保留副本
    int temp = i;
    i = i + 1;
    //返回副本
    return temp;
}

//模拟++i的机制
public int firstAdd() {
    i = i + 1;
    return i;
}

输出结果为0和1,和i=i++以及i=++i的结果一致。

通过以上代码模拟,似乎在java的执行过程中,i++和++i都直接对i进行了i=i+1的操作,但是不同的是i++得到的是i未进行加法操作的前的值的副本,而++i直接得到计算后的值。那么,事实真的是这样吗,我们再去刨析一下源码,看看在汇编指令中,它到底是怎么做的。

源码解析
再写一个类,源码如下:

public class PlusI {

    public void iPlusPlus(){
    	int i = 0;
        i++;
    }

public void plusPlusI(){
	int i = 0;
    ++i;
}

对其class文件进行反汇编后,代码如下:

public class com.aliencat.javabase.bit.PlusI {

  public com.aliencat.javabase.bit.PlusI();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void iPlusPlus();
    Code:
		0 iconst_0
		1 istore_1
		2 iinc 1 by 1
		5 return

  public void plusPlusI();
    Code:
		0 iconst_0
		1 istore_1
		2 iinc 1 by 1
		5 return

}

先不谈这些汇编指令的意义,乍一看,两个方法的执行指令完全一样。
我们再把代码改下,看看为什么i=i++和i=++i会产生不一样的结果:

public class PlusI {

    public void iPlusPlus(){
        int i = 0;
        i = i++;
    }

    public void plusPlusI(){
        int i = 0;
        i = ++i;
    }
}

对其进行反汇编后,代码如下:

public class com.aliencat.javabase.bit.PlusI {

  public com.aliencat.javabase.bit.PlusI();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void iPlusPlus();
    Code:
		0 iconst_0
		1 istore_1
		2 iload_1
		3 iinc 1 by 1
		6 istore_1
		7 return

  public void plusPlusI();
    Code:
		0 iconst_0
		1 istore_1
		2 iinc 1 by 1
		5 iload_1
		6 istore_1
		7 return

}

通过比较我们发现,除了iload_1 和iinc 1 by 1这两条指令顺序有区别外,其它都是一致的。
下面我们来分析一下每条汇编指令的意义:

iconst_0:将int类型的0值压入操作数栈

istore_1: 弹出操作数栈顶的值赋给局部变量表下标为1的变量

iload_1: 将局部变量表下标为1的位置存储的值压入操作数栈

iinc 1 by 1:取局部变量表下标为1的位置存储的值加上1

istore_1:弹出操作数栈顶的值赋给局部变量表下标为1的变量

结论
在使用i=i++的过程中,它会先把i的原始值0复制到操作数栈中,然后再对局部变量表中的0进行+1操作使得i变为了1,此时操作数栈顶的值为0,然后执行赋值操作时候使用的是弹出的操作数栈顶的值,所以最后i又被修改为了0;
而i=++i的过程则是先对局部变量表中i的原始值进行加1的操作,即使得i由0变为1,然后将i的值复制到操作数栈,最后赋值即弹出操作数栈顶的值。
i++;和++i;的执行过程和结果是一样的。
在使用i++和++i赋值的过程中,他们区别在于前者先复制当前数据,再进行原始值加1的操作,后者则先进行了原始值加1的操作,再对计算后的结果进行了复制,最后返回的其实都是放入操作数栈的拷贝。
看懂了上面的原理,你应该能明白为什么int i = 0;i=i++ + ++i;等于2了吧。如果按原来的定义取理解,也许会得出结果为1。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值