浅谈JVM 底层解析 i++和 ++i 区别

 更新时间:2021年09月09日 11:07:31   作者:默默无闻代码人  
这篇文章主要介绍了JVM 底层解析 i++和 ++i 区别,需要的朋友可以参考下面文章的具体内容

一、前言

如果只用普通的知识解释i++和++i的话
i++ 先将i赋值再++
++i 先++再赋值
但是这简单的回答并不能入吸引面试官的眼球,如果用java字节码指令分析则效果完全不同

二、代码实现

public class OperandStackTest {
/**
    程序员面试过程中, 常见的i++和++i 的区别
     */
    public static void add(){
        //第1类问题:
        int i1 = 10;
        i1++;
        System.out.println("i1 =" + i1);//11

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

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

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

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

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

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

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

运行结果

i1 = 11
i2 = 11
i3 = 11
i4 = 10
i5 = 11
i6 = 11
i7 = 10
i8 = 11
i9 = 12
i10 = 22

三、字节码指令

通过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          0, 1
         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          1, 1
        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          2, 1
        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          4, 1
        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          6, 1
        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          7, 1
       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          8, 1
       122: iinc          8, 1
       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
       146: return

四、字节码解析

1. 第一类问题

 //第1类问题:
 int i1 = 10;
 i1++;
 System.out.println("i1 =" + i1);//11

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

对应字节码指令为

   0: bipush        10
   2: istore_0
   3: iinc          0, 1
   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          1, 1
  19: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
  22: iload_1
  23: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V

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

所以i1的值为11

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

所以i2的值为11

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

2. 第二类问题

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

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

对应字节码为

  26: bipush        10
  28: istore_2
  29: iload_2
  30: iinc          2, 1
  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          4, 1
  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

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

所以i3是11,i4还是10

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

所以i5、i6都是11

3. 第三类问题

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

对应字节码

  75: bipush        10
  77: istore        6
  79: iload         6
  81: iinc          6, 1
  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          7, 1
 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

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

所以i7为10

而++i不会产生覆盖先执行加一然后再把值入栈,在赋值给局部变量表中

所以i8为11

4. 第四类问题

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

对应字节码为

 113: bipush        10
 115: istore        8
 117: iload         8
 119: iinc          8, 1
 122: iinc          8, 1
 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
 146: return

先将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处的值入栈执行输出语句

到此这篇关于浅谈JVM 底层解析 i++和 ++i 区别的文章就介绍到这了,更多相关JVM 底层解析 i++和 ++i 区别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 深入剖析springBoot中的@Scheduled执行原理

    深入剖析springBoot中的@Scheduled执行原理

    这篇文章主要介绍了springBoot中的@Scheduled执行原理,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • Android中几种图片特效的处理的实现方法

    Android中几种图片特效的处理的实现方法

    这篇文章主要介绍了 Android中几种图片特效的处理的实现方法的相关资料,这里有放大缩小图片,获得圆角图片,获得带倒影图片的几种方法,需要的朋友可以参考下
    2017-08-08
  • Java实现Excel百万级数据导入功能的示例代码

    Java实现Excel百万级数据导入功能的示例代码

    这篇文章主要为大家详细介绍了Java如何实现Excel百万级数据导入功能,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以参考下
    2024-04-04
  • SpringBoot 3.0 新特性内置声明式HTTP客户端实例详解

    SpringBoot 3.0 新特性内置声明式HTTP客户端实例详解

    声明式 http 客户端主旨是使得编写 java http 客户端更容易,为了贯彻这个理念,采用了通过处理注解来自动生成请求的方式,本文给大家详解介绍SpringBoot 声明式HTTP客户端相关知识,感兴趣的朋友跟随小编一起看看吧
    2022-12-12
  • Spring中的Bean对象初始化详解

    Spring中的Bean对象初始化详解

    这篇文章主要介绍了Spring中的Bean对象初始化详解,我们也可以根据Java语言的特性猜测到其很有可能是通过反射机制来完成Bean的初始化操作,接下来我们一步一步的剖析Spring对Bean的初始化操作,需要的朋友可以参考下
    2023-12-12
  • Maven一键部署Springboot到Docker仓库为自动化做准备(推荐)

    Maven一键部署Springboot到Docker仓库为自动化做准备(推荐)

    这篇文章主要介绍了Maven一键部署Springboot到Docker仓库,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-07-07
  • 浅谈Hibernate对象状态之间的神奇转换

    浅谈Hibernate对象状态之间的神奇转换

    这篇文章主要介绍了浅谈Hibernate对象状态之间的神奇转换,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-09-09
  • JAVA使用JDBC连接oracle数据库的详细过程

    JAVA使用JDBC连接oracle数据库的详细过程

    JDBC是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成,下面这篇文章主要给大家介绍了关于JAVA使用JDBC连接oracle数据库的详细过程,需要的朋友可以参考下
    2023-05-05
  • SpringBoot实现RabbitMQ监听消息的四种方式

    SpringBoot实现RabbitMQ监听消息的四种方式

    本文介绍了在Spring Boot中实现RabbitMQ监听消息的几种方式,包括使用@RabbitListener注解、MessageListenerAdapter、配置连接工厂和队列等方式,感兴趣的可以了解一下
    2024-07-07
  • javaweb实现app扫码登录功能

    javaweb实现app扫码登录功能

    这篇文章主要为大家详细介绍了javaweb实现app扫码登录功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-10-10

最新评论