从JVM层面来解释i++和++i的真正区别

肉眼品世界

共 13597字,需浏览 28分钟

 ·

2021-07-07 23:52

前言

如果只用普通的知识解释i++和++i的话

  • i++ 先将i赋值再++
  • ++i 先++再赋值

但是这简单的回答并不能入吸引面试官的眼球,如果用java字节码指令分析则效果完全不同。

代码实现

public class OperandStackTest {
/**
    程序员面试过程中, 常见的i++和++i 的区别
     */

    public static void add(){
        //第1类问题:
        int i1 = 10;
        i1++;
        System.out.println(i1);//11

        int i2 = 10;
        ++i2;
        System.out.println(i2);//11

        //第2类问题:
        int i3 = 10;
        int i4 = i3++;
        System.out.println(i3);//11
        System.out.println(i4);//10

        int i5 = 10;
        int i6 = ++i5;
        System.out.println(i5);//11
        System.out.println(i6);//11

        //第3类问题:
        int i7 = 10;
        i7 = i7++;
        System.out.println(i7);//10

        int i8 = 10;
        i8 = ++i8;
        System.out.println(i8);//11

        //第4类问题:
        int i9 = 10;
        int i10 = i9++ + ++i9;//10+12
        System.out.println(i9);//12
        System.out.println(i10);//22
    }

    public static void main(String[] args) {
        add();
    }
}

运行结果

字节码指令

通过javap -v out目录下的class文件名 在终端运行得到如下结果

 public static void add();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=10, args_size=0
         0: bipush        10
         2: istore_0
         3: iinc          01
         6: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
         9: iload_0
        10: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        13: bipush        10
        15: istore_1
        16: iinc          11
        19: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        22: iload_1
        23: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        26: bipush        10
        28: istore_2
        29: iload_2
        30: iinc          21
        33: istore_3
        34: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        37: iload_2
        38: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        41: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        44: iload_3
        45: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        48: bipush        10
        50: istore        4
        52: iinc          41
        55: iload         4
        57: istore        5
        59: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        62: iload         4
        64: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        67: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        70: iload         5
        72: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        75: bipush        10
        77: istore        6
        79: iload         6
        81: iinc          61
        84: istore        6
        86: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        89: iload         6
        91: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        94: bipush        10
        96: istore        7
        98: iinc          71
       101: iload         7
       103: istore        7
       105: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
       108: iload         7
       110: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
       113: bipush        10
       115: istore        8
       117: iload         8
       119: iinc          81
       122: iinc          81
       125: iload         8
       127: iadd
       128: istore        9
       130: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
       133: iload         8
       135: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
       138: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
       141: iload         9
       143: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
       146return

解释以上运行结果

第一类问题

对应的指令为

先将i1的值为10入栈(bipush),然后将int类型的值从栈中存到局部变量表0的位置,然后执行iinc将0位置的值+1,然后将局部变量表0位置的数入栈执行输出操作

所以i1的值为11

先将i2的值为10入栈(bipush),然后将int类型的值从栈中存到局部变量表1的位置,然后执行iinc将1位置的值+1,然后将局部变量表1位置的数入栈执行输出操作

所以i2的值为11

总结

由于没有赋值操作,区别不大。

第二类问题

先将i3入栈存储到局部变量表2的位置,然后将它入栈,执行iinc将2位置的值加一,i4存储到局部表量表3的位置

所以i3是11,i4还是10

将i5入栈存储到局部变量表4的位置,由于是++i所以先iinc将4位置的值加一,然后将局部变量表4的值入栈,执行赋值操作,所以都是11

第三类问题

先将i7入栈,然后存到局部变量表6的位置,先把i6入栈,然后把6处的值加一,由于又将这个值存储到局部变量表6处,所以产生覆盖又把值变为10。

而++i不会产生覆盖先执行加一然后再把值入栈,在赋值给局部变量表中,所以i8为11。另外,欢迎关注公众号Java笔记虾,后台回复“后端面试”,送你一份面试题宝典!

第四类问题

先将i9=10入栈,然后存在局部变量表8的位置

int i10 = i9++ + ++i9;

先iload将8位置的i9入栈然后执行iinc将8处的i9加一,然后执行++i9,在将8处的i9加一

此时i9=10+1+1为12,然后将8位置的i9入栈,执行add将栈中的两i9相加,得到的值存储到局部变量表9的位置

所以i10=10+12(i9++后还是10,++i9后是12,因为执行了两次iinc操作)

然后调用虚方法和静态方法,在将9处的值入栈执行输出语句

(感谢阅读,希望对你所有帮助)
来源:https://blog.csdn.net/weixin_45706762/

推荐阅读:

世界的真实格局分析,地球人类社会底层运行原理

企业IT技术架构规划方案

论数字化转型——转什么,如何转?

企业10大管理流程图,数字化转型从业者必备!

【中台实践】华为大数据中台架构分享.pdf

华为的数字化转型方法论

华为如何实施数字化转型(附PPT)

超详细280页Docker实战文档!开放下载

华为大数据解决方案(PPT)

浏览 31
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报