i++ 与 ++i 的从字节码层面看二者的区别

/**
* javap命令可以对class反汇编得到其字节码文件(此命令并不是jdk8开始的,只不过jdk8中对工具进行加强,增加了一些参数,可通过 javap -help了解)
*
* 注意:
* 字节码文件在虚拟机中是通过栈帧来保存指令的,也称为操作栈,是一个后入先出的栈。并不是针对寄存器的直接操作。
* 字节码执行引擎通过操作站获取指令。
*
* 这个测试同时验证了 ++i 和 i++的区别。
* ++i,在字节码层面,会先进行iinc,也就是执行自增,然后load变量
* i++,则是,先load变量,后自增(因为已经load,所以本次自增,并不会影响已经load的变量值)。
*
* @author zhuotao
*
*/
public class JavapTest {

/**
* 最简单的求和操作,javap反汇编得到的字节码如下:
Code:
0: iload_1 // 第一个int类型变量
1: iload_2 // 第二个int类型变量
2: iadd // 执行int类型的add操作
3: ireturn // 返回int类型
*
*/
public int add(int a, int b) {
  return a+b;
}

/**
* 字节码如下:
* Code:
0: iload_1 // int类型变量
1: iconst_1 // int类型常量1
2: iadd // int类型add
3: ireturn // 返回
* @param a
* @return
*/
public int incr(int a) {
  return a+1;
}

/**
* 字节码如下:
* Code:
0: iload_1 // int类型变量
1: iinc 1, 1 // iinc 局部变量自增 第一个“1” 表示int类型变量索引, 第二个“1”表示+1
4: ireturn // 返回
* @param a
* @return
*/
public int incr1(int a) {
  return a++;
}

/**
* 用两个变量,来验证iinc的参数分别表示什么
* 字节码如下:
* Code:
0: iinc 2, 1 // “2”表示参数列表的第二个参数, "1"表示步长
3: iload_2 // load第二个int类型参数
4: ireturn // 返回load变量
*/
public int incr2(int a, int b) {
  return ++b;
}

/**
* 这个方法字节码同incr2
* 字节码如下:
* Code:
0: iinc 1, 1
3: iload_1
4: ireturn
*/
public int incr3(int a) {
  a++;
  return a;
}

public static void main(String[] args) {
  System.out.println(new JavapTest().incr1(2)); // return a++; 返回的是2,也就是先使用后加。
  System.out.println(new JavapTest().incr2(0,2)); // return ++a; 返回的是3,也就是先加后使用。
  System.out.println(new JavapTest().incr3(2));
}
}

时间: 2024-10-05 19:11:31

i++ 与 ++i 的从字节码层面看二者的区别的相关文章

从字节码层面看“HelloWorld”

一.HelloWorld 字节码生成 众所周知,Java 程序是在 JVM 上运行的,不过 JVM 运行的其实不是 Java 语言本身,而是 Java 程序编译成的字节码文件.可能一开始 JVM 是为 Java 语言服务的,不过随着编译技术和 JVM 自身的不断发展和成熟,JVM 已经不仅仅只运行 Java 程序.任何能编译成为符合 JVM 字节码规范的语言都可以在 JVM 上运行,比较常见的 Scala.Groove.JRuby等.今天,我就从大家最熟悉的程序“HelloWorld”程序入手,

从字节码层面,解析 Java 布尔型的实现原理

最近在系统回顾学习 Java 虚拟机方面的知识,其中想到一个很有意思的问题:布尔型在虚拟机中到底是什么类型? 要想解答这个问题,我们看 JDK 的源码是无法解决源码的,我们必须深入到 class 文件中才能解决问题.于是他给出了这么一道题: public class Foo{ static boolean flag; public static void main(String[] args){ flag = true; if(flag){ System.out.println("Hello,

从字节码指令看重写在JVM中的实现

Java是解释执行的,包括动态链接的特性,都给解析或运行期间提供了很多灵活扩展的空间.面向对象语言的继承.封装和多态的特性,在JVM中是怎样进行编译.解析,以及通过字节码指令如何确定方法调用的版本是本文如下要探讨的主要内容,全文围绕一个多态的简单举例来看在JVM中是如何实现的. 先简单介绍几个概念.对于字节码执行模型及字节码指令集的相关概念可以参考之前的一篇介绍http://blog.csdn.net/lijingyao8206/article/details/46562933. 一.方法调用的

Java协程框架-Kilim字节码剖析

前面几篇文章从代码层面介绍了Kilim的基本原理,但是对于其中的一些细节,比如Task的执行状态如何管理等问题从代码上依然得不到答案,本文即再深入到字节码层面来解答. 1.  Kilim字节码改写前后的代码有什么区别? 这里还是先上Kilim官方文档中的一张图,这张图清晰的展现出原始的代码与经Kilim改写后的协程代码. 可以看出左边的原始代码,与我们常见的函数相比有所不同,这里显示声明抛出Pausable异常.实际上这个异常在运行期间不会抛出,它的实际作用类似于注解,使得Kilim能够识别哪些

从Java源码到Java字节码

Java最主流的源码编译器,javac,基本上不对代码做优化,只会做少量由Java语言规范要求或推荐的优化:也不做任何混淆,包括名字混淆或控制流混淆这些都不做.这使得javac生成的代码能很好的维持与原本的源码/AST之间的对应关系.换句话说就是javac生成的代码容易反编译. Java Class文件含有丰富的符号信息.而且javac默认的编译参数会让编译器生成行号表,这些都有助于了解对应关系. 关于Java语法结构如何对应到Java字节码,在JVM规范里有相当好的例子:Chapter 3.

Lua1.1 打印字节码

如何打印出字节码:代码里做如下修改,把打印字节码的宏开关打开.y.tab.c18 行#define LISTING 0改为#define LISTING 1 因为 PrintCode 的定义在调用之后,所以加个前置声明:做出下修改:y.tab.c329 行添加static void PrintCode (Byte *code, Byte *end);保证在 lua_parse 调用它的时候,是已经声明了的. 把打印字节码打开之后,执行脚本的时候就会先打字节码打印出来.看看字节码打印出来是什么样,

java中i=i++字节码分析

原文出处: Ticmy 1 2 int i = 0; i = i++; 结果还是0为什么? 程序的执行顺序是这样的:因为++在后面,所以先使用i,"使用"的含义就是i++这个表达式的值是0,但是并没有做赋值操作,它在整个语句的最后才做赋值,也就是说在做了++操作后再赋值的,所以最终结果还是0 让我们看的更清晰点: 1 2 int i = 0;//这个没什么说的 i = i++;//等效于下面的语句: 1 2 3 int temp = i;//这个temp就是i++这个表达式的值 i++

通过字节码分析JDK8中Lambda表达式编译及执行机制

关于Lambda字节码相关的文章,很早之前就想写了,[蜂潮运动]APP 产品的后端技术,能快速迭代,除了得益于整体微服架构之外,语言层面上,也是通过Java8的lambda表达式的运用以及rxJava响应式编程框架,使代码更加简洁易维护,调用方式更加便捷.本文将介绍JVM中的方法调用相关的字节码指令,重点解析JDK7(JSR-292)之后新增的invokedynamic指令给lambda表达式的动态调用特性提供的实现机制,最后再探讨一下lambda性能方面的话题. 方法调用的字节码指令 在介绍i

字节码增强技术探索

1.字节码 1.1 什么是字节码? Java之所以可以“一次编译,到处运行”,一是因为JVM针对各种操作系统.平台都进行了定制,二是因为无论在什么平台,都可以编译生成固定格式的字节码(.class文件)供JVM使用.因此,也可以看出字节码对于Java生态的重要性.之所以被称之为字节码,是因为字节码文件由十六进制值组成,而JVM以两个十六进制值为一组,即以字节为单位进行读取.在Java中一般是用javac命令编译源代码为字节码文件,一个.java文件从编译到运行的示例如图1所示. 图1 Java运