神奇的i++

神奇的i++

i++,++i,多简单啊,不需要深入研究吧!!!
我是这样想的。
直到我做了一道Java基础检测题,才发现,哦,原来是这样啊!!!

题是这样的

public class Demo {
     public static void main(String args[]) {
         int num = 50 ;
         num = num ++ * 2 ;
         System.out.println(num) ;
     }
 }
 最终的执行结果是什么?

结果是什么?100?102?101?
正确输出是:100
对了就不必往下看了.

Javap介绍

先来看个有用的指令:javap
javap:
javap是jdk自带的反解析工具,通过反编译生成的汇编代码,我们可以深入的了解java代码的工作机制
javap的用法格式:
javap
classes就是你要反编译的class文件

D:\notepadd++cache>javap
用法: javap <options> <classes>
其中, 可能的选项包括:
  -help  --help  -?        输出此用法消息
  -version                 版本信息
  -v  -verbose             输出附加信息
  -l                       输出行号和本地变量表
  -public                  仅显示公共类和成员
  -protected               显示受保护的/公共类和成员
  -package                 显示程序包/受保护的/公共类
                           和成员 (默认)
  -p  -private             显示所有类和成员
  -c                       对代码进行反汇编
  -s                       输出内部类型签名
  -sysinfo                 显示正在处理的类的
                           系统信息 (路径, 大小, 日期, MD5 散列)
  -constants               显示最终常量
  -classpath <path>        指定查找用户类文件的位置
  -cp <path>               指定查找用户类文件的位置
  -bootclasspath <path>    覆盖引导类文件的位置

Jvm指令介绍

这是接下来要用到的Jvm指令的含义,推荐一篇JVM指令博客JVM指令介绍博客
//指令含义
bipush : valuebyte值带符号扩展成int值入栈
istore : 将栈顶int类型值保存到局部变量indexbyte中
iinc : 将指定int型变量增加指定值(i++, i--, i+=2)
iload : 从局部变量indexbyte中装载int类型值入栈
iconst_2: 2(int)值入栈
imul : 将栈顶两int类型数相乘,结果入栈。

分析

通过javap来分析下程序的运行机制
把这段代码放到StrTest.java中

 public class StrTest {
     public static void main(String args[]) {
		 int num1 = 50 ;
         num1 = num1++  * 2 ;
         System.out.println(num1) ;
     }
 }

通过cmd执行javac StrTest.java ->javap -c StrTest.class得到如下反汇编代码

public class StrTest {
  public StrTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
  public static void main(java.lang.String[]);
    Code:
       0: bipush        50
       2: istore_1
       3: iload_1
       4: iinc          1, 1
       7: iconst_2
       8: imul
       9: istore_1
      10: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      13: iload_1
      14: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
      17: return
}

把上边的内容添加上注释,瞬间就能明白,为啥结果是100了。

public static void main(java.lang.String[]);
    Code:
       0: bipush        50					//把50推入栈顶top
       2: istore_1							//将50保存到第二个局部变量中->num1=50
       3: iload_1							//把局部变量num1的值推入栈顶,此时栈顶值top是50
       4: iinc          1, 1				//将50执行加1->50+1 局部变量num1值是51
       7: iconst_2							//把2推入栈顶
       8: imul								//把50和2相乘,并把结果100推入栈顶,此时栈顶值top是100
       9: istore_1							//把100保存到第二个局部变量中->num1=100
      10: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      13: iload_1
      14: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
      17: return

通过Jvm指令解析后,可知程序经过的步骤是这样的:

  1. 把50推入栈顶,栈顶值是50
  2. num1的值是50
  3. num1执行自增->num1=51
  4. 把2推入栈顶,栈顶值是2
  5. 执行50*2并保存到栈顶,栈顶值是100
  6. 把栈顶值赋给num1->num1=100

同理,++i也好理解了,把上边程序的num1++改为++num1
结果102,再分析下执行步骤

 public static void main(java.lang.String[]);
    Code:
       0: bipush        50					//把50推入栈顶top
       2: istore_1							//将50保存到第二个局部变量中->num1=50
       3: iinc          1, 1				//将50执行加1->50+1 局部变量num1值是51
       6: iload_1							//把局部变量num1的值推入栈顶,此时栈顶值top是51
       7: iconst_2							//把2推入栈顶
       8: imul								//把51和2相乘,并把结果102推入栈顶,此时栈顶值top是102
       9: istore_1							//把102保存到第二个局部变量中->num1=102
      10: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      13: iload_1
      14: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
      17: return

通过Jvm指令解析后,可知程序经过的步骤是这样的:

  1. 把50推入栈顶,栈顶值是50
  2. num1的值是50
  3. num1执行自增->num1=51
  4. 把num1的值推入栈顶,栈顶的值是51
  5. 把2推入栈顶,栈顶值是2
  6. 执行51*2并保存到栈顶,栈顶值是102
  7. 把栈顶值赋给num1->num1=102

结束

ok,分析结束,又温习了一遍指令,记录一下

posted @ 2019-12-26 11:11  往霄龙  阅读(177)  评论(0编辑  收藏  举报
TOP