三个角度带你认识 i++ 与 ++i 姐妹花

我们对与 i++ , ++i 的理解,其实很多人在对这块并不是很理解。甚至在遇到这个问题时会把两者搞混,那么这两者之间存在着怎样的联系呢?现在从三个角度带你认识 i++和++i这对姐妹花

引入

我们先看一个例子:

int i = 0 ;
int j = i ++ ;
System.out.println( "i = " + i + ","+ "j = " + j);

int x = 0 ;
int y = ++ x ;
System.out.println( "x = " + x + ","+ "y = " + y);

这个例子,输出的是什么呢? 有的人会觉得很简单,也有的人会觉得,有点蒙的样子。我们之前一般喜欢说:

  • i++ , 先赋值在做 ++ 运算;
  • ++i ,先做 ++ 运算,再进行赋值。

那根据这个来,我们这里的 j 的值应该是 0 , i 的值应该是 1 。x 的值应该是 1 , y 的值是 1 。我们的程序跑一下就知道了:

i = 1,j = 0
x = 1,y = 1

方式一:口诀

我之所以说这个第一个个方式是口诀,是因为它的确只需要记住这句话就好了:

  • i++ , 先赋值在做 ++ 运算;
  • ++i ,先做 ++ 运算,再进行赋值。

这种方式应该是一种很常见,也很高效的做法。我们通过前面的例子,也可以看到一个结论就是: i 最后一定做了 ++ 运算。这也就是为什么我们在 for 循环里面写 ++i , i++ 为什么问题不大的原因。

刚刚也说了,这种方法很常见,也很高效, 但是,它就是最准确的吗? 这么来说,它可以解决目前我们见到的 99% 的场景,那剩下的 1% 的场景怎么办呢?我们举个例子:

int a= 0 ;
a = a ++ ;
System.out.println( "a = " + a );

根据我们之前的口诀, a 会先赋值给 a , 在做 ++ 运算。也就是说, a 一定做了 ++ 运算。那么问题来了,输出的 a 会是 1 吗?我们看程序的执行结果:

三个角度带你认识 i++ 与 ++i 姐妹花

大家可以看到,结果并不是 1 , 难道是因为没有做 ++ 操作吗?还是怎样呢?

方式二: .class

.class 文件前面我又提到过,是 Java 程序的可执行文件,会通过 JVM 中的 ClassLoad 加载到我们的JVM中,换句话说,这个 .class 会决定了我们这个输出。我们现在去看一下, 答案和我们预想的不一样的,在.class 中能不能找到答案?

我现在贴出整个 .class 文件:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.java.learn;

public class CompareI {
    public CompareI() {
    }

    public static void main(String[] args) {
        int a = 0;
        byte var10000 = a;
        int var2 = a + 1;
        a = var10000;
        System.out.println("a = " + a);
    }
}

现在我们是不是看到了? 我们一步一步来:

首先我们定义一个变量 a ,

int a = 0;

将 a 的值赋值给一个 叫 var10000 的临时变量:

byte var10000 = a;

执行 +1 操作:

int var2 = a + 1;

再将我们的 临时变量的值 还给 a:

a = var10000;

最后输出 a 的值。

现在该明白为什么输出的结果是 0 , 不是 1 了吧? 1 在变量 var2 里面,被它截胡了,能咋办呢?我们现在再来看看之前的那个 .class 文件:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.java.learn;

public class CompareI {
    public CompareI() {
    }

    public static void main(String[] args) {
        int i = 0;
        byte var10000 = i;
        int i = i + 1;
        int j = var10000;
        System.out.println("i = " + i + ",j = " + j);
        int x = 0;
        int x = x + 1;
        System.out.println("x = " + x + ",y = " + x);
    }
}

这里我们看一下,这里的 ++x 的操作,是不是就直接是 x=x+1 了?这里也很好理解是吧。到这里,我们的这个对于 ++i 和 i++ 的理解就很明确了。

方式三: 字节码

最后一种方式为 字节码 的方式,什么意思呢?我们从程序的基础出发,去理解这个 i++ 与 ++i 。我们先来看一下那个让我们判断错误的程序的字节码文件长啥样:

Compiled from "CompareI.java"
public class com.java.learn.CompareI {
  public com.java.learn.CompareI();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_0
       1: istore_1
       2: iload_1
       3: iinc          1, 1
       6: istore_1
       7: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      10: new           #3                  // class java/lang/StringBuilder
      13: dup
      14: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      17: ldc           #5                  // String a =
      19: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      22: iload_1
      23: invokevirtual #7                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      26: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      29: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      32: return
}

这里怎么去跟大家解释呢?一步一步来:

# 将一个常量加载到操作数栈
 0: iconst_0
# 将一个数值从操作数栈存储到局部变量表
 1: istore_1
# 将一个本地变量加载到操作数栈
 2: iload_1
# 将我们的局部变量1 做 + 1 操作,但是栈中的变量不变
 3: iinc          1, 1

到这里就知道了,栈里面没有变。这也就是为什么输出是0 ,不是 1 的原因了。

小结

好了,以上的三个方式是对 i++ 和 ++i 的理解。总的来说,第一种方式是比较简单快捷的,可以帮助我们在 99% 的场景下去解决问题,但是遇到了 1% 的场景也不要怕,我们直接上 .class ,也可以帮我们解决这剩下的 1% 。

如果说大家对此有什么不同意见的,特别是后面的字节码文件的解读,可以私信或者评论里面指出,我们大家共同努力,一起进步。

原文 

https://www.blog.sun-iot.xyz/2020/03/16/Java/core-technology/compare-for-i/

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » 三个角度带你认识 i++ 与 ++i 姐妹花

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址