• 从字节码文件理解 ++i 和 i++ 的区别_i++ 与 ++i 底层


    jvm内存模型

    jvm 内存模型:Java 代码是运行在 Java 虚拟机之上的,由 Java 虚拟机通过解释执行 (解释器)或编译执行 (即时编译器) 来完成,故 Java 内存模型,也就是指 Java 虚拟机的运行时内存模型。

    运行时内存模型,分为线程私有共享数据区两大类,

    • 线程私有的数据区包含程序计数器、虚拟机栈、本地方法区
    • 所有线程共享的数据区包含 Java 堆、方法区,在方法区内有一个常量池

    java 运行时的内存模型图,如下:

    在这里插入图片描述

    从图中,可知内存分为线程私有和共享两大类:

    • 线程私有区,包含以下 3 类:

      • 程序计数器,记录正在执行的虚拟机字节码的地址;

      • 虚拟机栈:方法执行的内存区,每个方法执行时会在虚拟机栈中创建栈帧;

      • 本地方法栈:虚拟机的 Native 方法执行的内存区;

    • 线程共享区,包含以下 2 类

      • Java 堆:对象分配内存的区域;
      • 方法区:存放类信息、常量、静态变量、编译器编译后的代码等数据;
      • 常量池:存放编译器生成的各种字面量和符号引用,是方法区的一部分。

    提到的 Java 栈,一般而言是指图中的虚拟机栈,在代码中的方法调用过程中,往往需要从一个方法跳转到另一个方法,执行完再返回,那么在跳转之前需要在当前方法的基本信息压入栈中保存再跳转。

    关于寄存器的问题对于 java 最常用的虚拟机,sun 公司提供的 hotspot 虚拟机,是基于栈的虚拟机; 而对于 android 的虚拟机,则采用 google 提供的 dalvik,art 两种虚拟机,在 android 5.0 以后便默认采用 art 虚拟机,这是基于寄存器的虚拟机。 楼主问的是 jvm(即 java vm),这是基于栈的虚拟机。那么关于虚拟机栈,这块内存的内容,我们再进一步详细分析,如下图:

    在这里插入图片描述

    可以看到,在虚拟机栈有一帧帧的 栈帧组成,而栈帧包含局部变量表操作栈等子项,那么线程在运行的时候,代码在运行时,是通过程序计数器不断执行下一条指令。真正指令运算等操作时通过控制操作栈的操作数入栈和出栈,将操作数在局部变量表和操作栈之间转移

    自加自减运算

    理解:++ 运算,表示自增1。同理,-- 运算,表示自减1,用法与++ 一致。

    1、单独使用

    • 变量在单独运算的时候,变量前++和变量后++,是没有区别的。
    • 变量前++ :例如 ++a
    • 变量后++ :例如 a++
    public class ArithmeticTest3 {
    	public static void main(String[] args) {
    		// 定义一个int类型的变量a
    		int a = 3;
    		//++a;
    		a++;
            // 无论是变量前++还是变量后++,结果都是4
    		System.out.println(a);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2、复合使用

    • 其他变量放在一起使用或者和输出语句放在一起使用前++后++就产生了不同。
    • 变量前++ :变量先自增1,然后再运算。
    • 变量后++ :变量先运算,然后再自增1。
    public class ArithmeticTest4 {
    	public static void main(String[] args) {
    		// 其他变量放在一起使用
    		int x = 3;
    		//int y = ++x; // y的值是4,x的值是4,
    		int y = x++; // y的值是3,x的值是4
    		
    		System.out.println(x);
    		System.out.println(y);
    		System.out.println("==========");
            
    		// 和输出语句一起
    		int z = 5;
    		//System.out.println(++z);// 输出结果是6,z的值也是6
    		System.out.println(z++);// 输出结果是5,z的值是6
    		System.out.println(z);
            
    	} 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3、++ 或 – 运算,不会改变变量的数据类型

    short s1 = 10;
    //方式1:
    //编译不通过
    //s1 = s1 + 1;//short 运算时转换为int,返回值需要int接受
    
    s1 = (short)(s1 + 1); 
    System.out.println(s1);
    
    //方式2:
    s1++;
    System.out.println(s1);
    
    //结论:++ 或 -- 运算,不会改变变量的数据类型!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    从字节码分析

    获取字节码方式:

    1. idea插件 jclasslib Bytecode Viewer

    2. 那么从反编译 javap -v xx.class 文件之后,可以看到一下指令

    常用指令:

    iconst_1	将 int 型 1 推送至栈顶
    istore_1	将栈顶 int 型数值存入第二个本地变量   将操作栈数弹出,到局部变量表
    iload_1		将第二个 int 型本地变量推送至栈顶    	局部变量表压入操作栈
    inc 1 by 1 这是一个双参数指令,主要的功能是将局部变量表中索引为1的值自增一个常量值
    iadd	将栈顶两 int 型数值相加并将结果压入栈顶
    
    • 1
    • 2
    • 3
    • 4
    • 5

    设计概念:

    局部变量表:实例方法局部变量表第一个位置(下标为0)总是保存一个this引用

    操作栈:进行表达式运算 如 + - * /

    字节码分析

    int i = 0;
    
    i = i++;
    i= ++i;
    
    • 1
    • 2
    • 3
    • 4

    字节码解读:

     0 iconst_0   //将 int 型 0 推送至栈顶
     1 istore_1   //将栈顶 int 型数值存入第二个本地变量
     /*  0-1行解释
      i = 1
      局部变量表[this,0]
     */
     
     2 iload_1  
     3 iinc 1 by 1
     6 istore_1
    /* 2-6解释 : i = i++;
    	
        iload_1		:从局部变量表中第二个值(0)到操作栈中
        iinc 1 by 1	:变量i直接在局部变量表的slot上进行运算,并不影响操作数栈已经存在的值,此时局部变量表中i=1
        istore		:将此时操作栈顶的值(0)弹出,存入局部变量表i中,并将刚刚iinc自增的值覆盖掉 i =0
    */	
     
     7 iinc 1 by 1
    10 iload_1
    11 istore_1
    /* 7-22行解释: i= ++i;
        innc:代表了局部变量表中的变量进行自增操作,此时局部变量表中i=1
        iload:从局部变量表中加载数字(1)到操作栈中
        istore:将此时操作栈顶的值(1)弹出,存入局部变量表中,并将局部变量表中值覆盖掉
    */
    
    
    12 return
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    ++i 是先在自己的局部变量表中自增了之后,再将局部变量的值入操作数栈并再返回。所以为 1。

    i++ 是,先入了操作数栈,栈中为 0,而局部变量表 i 自增为 1,接下里栈中 0 出栈赋值给了 i,所以为 0。

    案例分析

    第一题

    int i = 1;
    i = ++i + i++;
    System.out.println(i);
    
    • 1
    • 2
    • 3

    思考一下,再看分析过程

     0: iconst_1  //将常量数字1入栈
     1: istore_1 //出栈赋值给局部变量表i=1
     
     // ++i
     2: iinc 1 by 1 // 局部变量表i,自增加1 等于2
     5: iload_1 // 2入栈
     
     // i++
     6: iload_1 //2再次入栈
     7: iinc 1 by 1  // 局部变量表i自增1,等于3
     
     //将栈顶两 int 型数值相加并将结果压入栈顶
    10: iadd // 操作数栈中的2+2相加等于4
        
    // i =   2 + 2
    11: istore_1 //出栈,替换局部变量表i的3为4。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    所以结果为 4。

    第二题

    int i = 1;
    i = i++ + ++i + i++ + ++i;
    System.out.println(i);
    
    • 1
    • 2
    • 3
    0: iconst_1 	数值1入栈
    1: istore_1 	数值1出栈赋值给i
    2: iload_1 		i(1)入操作数栈
    3: iinc 1, 1 	i在局部变量表自增1,此时i=2
    6: iinc 1, 1 	i在局部变量表自增1,此时i=3
    9: iload_1 		i继续入栈,第二次为3
    10: iadd 		相加,与第一次栈中1和第二次的3相加为4,此时栈中为只有一个值且4
    11: iload_1 	又把局部变量表中的i=3入栈
    12: iinc 1, 1  	局部变量表中i为3自增1,变为了4
    15: iadd 		4+3等于7
    16: iinc 1, 1 	局部变量表中i为4自增1,变为了5
    19: iload_1 	i=5入操作数栈
    20: iadd 		与原本操作数栈中的7相加,等于12
    21: istore_1 	出栈返回给局部变量表中的i,即此时i从5变为了12
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    所以结果为 12

    总结

    以上就是 i++ 和 ++i 的底层分析,终极区别是,是否先入栈再自增,还是先自增再入栈。

    附录 字节码指令

    字节码助记符指令含义
    0x00nopNone
    0x01aconst_null将 null 推送至栈顶
    0x02iconst_m1将 int 型 - 1 推送至栈顶
    0x03iconst_0将 int 型 0 推送至栈顶
    0x04iconst_1将 int 型 1 推送至栈顶
    0x05iconst_2将 int 型 2 推送至栈顶
    0x06iconst_3将 int 型 3 推送至栈顶
    0x07iconst_4将 int 型 4 推送至栈顶
    0x08iconst_5将 int 型 5 推送至栈顶
    0x09lconst_0将 long 型 0 推送至栈顶
    0x0alconst_1将 long 型 1 推送至栈顶
    0x0bfconst_0将 float 型 0 推送至栈顶
    0x0cfconst_1将 float 型 1 推送至栈顶
    0x0dfconst_2将 float 型 2 推送至栈顶
    0x0edconst_0将 double 型 0 推送至栈顶
    0x0fdconst_1将 double 型 1 推送至栈顶
    0x10bipush将单字节的常量值 (-128~127) 推送至栈顶
    0x11sipush将一个短整型常量 (-32768~32767) 推送至栈顶
    0x12ldc将 int,float 或 String 型常量值从常量池中推送至栈顶
    0x13ldc_w将 int,float 或 String 型常量值从常量池中推送至栈顶 (宽索引)
    0x14ldc2_w将 long 或 double 型常量值从常量池中推送至栈顶 (宽索引)
    0x15iload将指定的 int 型本地变量推送至栈顶
    0x16lload将指定的 long 型本地变量推送至栈顶
    0x17fload将指定的 float 型本地变量推送至栈顶
    0x18dload将指定的 double 型本地变量推送至栈顶
    0x19aload将指定的引用类型本地变量推送至栈顶
    0x1aiload_0将第一个 int 型本地变量推送至栈顶
    0x1biload_1将第二个 int 型本地变量推送至栈顶
    0x1ciload_2将第三个 int 型本地变量推送至栈顶
    0x1diload_3将第四个 int 型本地变量推送至栈顶
    0x1elload_0将第一个 long 型本地变量推送至栈顶
    0x1flload_1将第二个 long 型本地变量推送至栈顶
    0x20lload_2将第三个 long 型本地变量推送至栈顶
    0x21lload_3将第四个 long 型本地变量推送至栈顶
    0x22fload_0将第一个 float 型本地变量推送至栈顶
    0x23fload_1将第二个 float 型本地变量推送至栈顶
    0x24fload_2将第三个 float 型本地变量推送至栈顶
    0x25fload_3将第四个 float 型本地变量推送至栈顶
    0x26dload_0将第一个 double 型本地变量推送至栈顶
    0x27dload_1将第二个 double 型本地变量推送至栈顶
    0x28dload_2将第三个 double 型本地变量推送至栈顶
    0x29dload_3将第四个 double 型本地变量推送至栈顶
    0x2aaload_0将第一个引用类型本地变量推送至栈顶
    0x2baload_1将第二个引用类型本地变量推送至栈顶
    0x2caload_2将第三个引用类型本地变量推送至栈顶
    0x2daload_3将第四个引用类型本地变量推送至栈顶
    0x2eiaload将 int 型数组指定索引的值推送至栈顶
    0x2flaload将 long 型数组指定索引的值推送至栈顶
    0x30faload将 float 型数组指定索引的值推送至栈顶
    0x31daload将 double 型数组指定索引的值推送至栈顶
    0x32aaload将引用类型数组指定索引的值推送至栈顶
    0x33baload将 boolean 或 byte 型数组指定索引的值推送至栈顶
    0x34caload将 char 型数组指定索引的值推送至栈顶
    0x35saload将 short 型数组指定索引的值推送至栈顶
    0x36istore将栈顶 int 型数值存入指定本地变量
    0x37lstore将栈顶 long 型数值存入指定本地变量
    0x38fstore将栈顶 float 型数值存入指定本地变量
    0x39dstore将栈顶 double 型数值存入指定本地变量
    0x3aastore将栈顶引用类型数值存入指定本地变量
    0x3bistore_0将栈顶 int 型数值存入第一个本地变量
    0x3cistore_1将栈顶 int 型数值存入第二个本地变量
    0x3distore_2将栈顶 int 型数值存入第三个本地变量
    0x3eistore_3将栈顶 int 型数值存入第四个本地变量
    0x3flstore_0将栈顶 long 型数值存入第一个本地变量
    0x40lstore_1将栈顶 long 型数值存入第二个本地变量
    0x41lstore_2将栈顶 long 型数值存入第三个本地变量
    0x42lstore_3将栈顶 long 型数值存入第四个本地变量
    0x43fstore_0将栈顶 float 型数值存入第一个本地变量
    0x44fstore_1将栈顶 float 型数值存入第二个本地变量
    0x45fstore_2将栈顶 float 型数值存入第三个本地变量
    0x46fstore_3将栈顶 float 型数值存入第四个本地变量
    0x47dstore_0将栈顶 double 型数值存入第一个本地变量
    0x48dstore_1将栈顶 double 型数值存入第二个本地变量
    0x49dstore_2将栈顶 double 型数值存入第三个本地变量
    0x4adstore_3将栈顶 double 型数值存入第四个本地变量
    0x4bastore_0将栈顶引用型数值存入第一个本地变量
    0x4castore_1将栈顶引用型数值存入第二个本地变量
    0x4dastore_2将栈顶引用型数值存入第三个本地变量
    0x4eastore_3将栈顶引用型数值存入第四个本地变量
    0x4fiastore将栈顶 int 型数值存入指定数组的指定索引位置
    0x50lastore将栈顶 long 型数值存入指定数组的指定索引位置
    0x51fastore将栈顶 float 型数值存入指定数组的指定索引位置
    0x52dastore将栈顶 double 型数值存入指定数组的指定索引位置
    0x53aastore将栈顶引用型数值存入指定数组的指定索引位置
    0x54bastore将栈顶 boolean 或 byte 型数值存入指定数组的指定索引位置
    0x55castore将栈顶 char 型数值存入指定数组的指定索引位置
    0x56sastore将栈顶 short 型数值存入指定数组的指定索引位置
    0x57pop将栈顶数值弹出 (数值不能是 long 或 double 类型的)
    0x58pop2将栈顶的一个 (对于非 long 或 double 类型) 或两个数值 (对于非 long 或 double 的其他类型) 弹出
    0x59dup复制栈顶数值并将复制值压入栈顶
    0x5adup_x1复制栈顶数值并将两个复制值压入栈顶
    0x5bdup_x2复制栈顶数值并将三个 (或两个) 复制值压入栈顶
    0x5cdup2复制栈顶一个 (对于 long 或 double 类型) 或两个 (对于非 long 或 double 的其他类型) 数值并将复制值压入栈顶
    0x5ddup2_x1dup_x1 指令的双倍版本
    0x5edup2_x2dup_x2 指令的双倍版本
    0x5fswap将栈顶最顶端的两个数值互换 (数值不能是 long 或 double 类型)
    0x60iadd将栈顶两 int 型数值相加并将结果压入栈顶
    0x61ladd将栈顶两 long 型数值相加并将结果压入栈顶
    0x62fadd将栈顶两 float 型数值相加并将结果压入栈顶
    0x63dadd将栈顶两 double 型数值相加并将结果压入栈顶
    0x64isub将栈顶两 int 型数值相减并将结果压入栈顶
    0x65lsub将栈顶两 long 型数值相减并将结果压入栈顶
    0x66fsub将栈顶两 float 型数值相减并将结果压入栈顶
    0x67dsub将栈顶两 double 型数值相减并将结果压入栈顶
    0x68imul将栈顶两 int 型数值相乘并将结果压入栈顶
    0x69lmul将栈顶两 long 型数值相乘并将结果压入栈顶
    0x6afmul将栈顶两 float 型数值相乘并将结果压入栈顶
    0x6bdmul将栈顶两 double 型数值相乘并将结果压入栈顶
    0x6cidiv将栈顶两 int 型数值相除并将结果压入栈顶
    0x6dldiv将栈顶两 long 型数值相除并将结果压入栈顶
    0x6efdiv将栈顶两 float 型数值相除并将结果压入栈顶
    0x6fddiv将栈顶两 double 型数值相除并将结果压入栈顶
    0x70irem将栈顶两 int 型数值作取模运算并将结果压入栈顶
    0x71lrem将栈顶两 long 型数值作取模运算并将结果压入栈顶
    0x72frem将栈顶两 float 型数值作取模运算并将结果压入栈顶
    0x73drem将栈顶两 double 型数值作取模运算并将结果压入栈顶
    0x74ineg将栈顶 int 型数值取负并将结果压入栈顶
    0x75lneg将栈顶 long 型数值取负并将结果压入栈顶
    0x76fneg将栈顶 float 型数值取负并将结果压入栈顶
    0x77dneg将栈顶 double 型数值取负并将结果压入栈顶
    0x78ishl将 int 型数值左移指定位数并将结果压入栈顶
    0x79lshl将 long 型数值左移指定位数并将结果压入栈顶
    0x7aishr将 int 型数值右 (带符号) 移指定位数并将结果压入栈顶
    0x7blshr将 long 型数值右 (带符号) 移指定位数并将结果压入栈顶
    0x7ciushr将 int 型数值右 (无符号) 移指定位数并将结果压入栈顶
    0x7dlushr将 long 型数值右 (无符号) 移指定位数并将结果压入栈顶
    0x7eiand将栈顶两 int 型数值 “按位与” 并将结果压入栈顶
    0x7fland将栈顶两 long 型数值 “按位与” 并将结果压入栈顶
    0x80ior将栈顶两 int 型数值 “按位或” 并将结果压入栈顶
    0x81lor将栈顶两 long 型数值 “按位或” 并将结果压入栈顶
    0x82ixor将栈顶两 int 型数值 “按位异或” 并将结果压入栈顶
    0x83lxor将栈顶两 long 型数值 “按位异或” 并将结果压入栈顶
    0x84iinc将指定 int 型变量增加指定值 (如 i++, i–, i+=2 等) 不是操作的操作栈,是局部变量表中
    0x85i2l将栈顶 int 型数值强制转换为 long 型数值并将结果压入栈顶
    0x86i2f将栈顶 int 型数值强制转换为 float 型数值并将结果压入栈顶
    0x87i2d将栈顶 int 型数值强制转换为 double 型数值并将结果压入栈顶
    0x88l2i将栈顶 long 型数值强制转换为 int 型数值并将结果压入栈顶
    0x89l2f将栈顶 long 型数值强制转换为 float 型数值并将结果压入栈顶
    0x8al2d将栈顶 long 型数值强制转换为 double 型数值并将结果压入栈顶
    0x8bf2i将栈顶 float 型数值强制转换为 int 型数值并将结果压入栈顶
    0x8cf2l将栈顶 float 型数值强制转换为 long 型数值并将结果压入栈顶
    0x8df2d将栈顶 float 型数值强制转换为 double 型数值并将结果压入栈顶
    0x8ed2i将栈顶 double 型数值强制转换为 int 型数值并将结果压入栈顶
    0x8fd2l将栈顶 double 型数值强制转换为 long 型数值并将结果压入栈顶
    0x90d2f将栈顶 double 型数值强制转换为 float 型数值并将结果压入栈顶
    0x91i2b将栈顶 int 型数值强制转换为 byte 型数值并将结果压入栈顶
    0x92i2c将栈顶 int 型数值强制转换为 char 型数值并将结果压入栈顶
    0x93i2s将栈顶 int 型数值强制转换为 short 型数值并将结果压入栈顶
    0x94lcmp比较栈顶两 long 型数值大小, 并将结果 (1, 0 或 - 1) 压入栈顶
    0x95fcmpl比较栈顶两 float 型数值大小, 并将结果 (1, 0 或 - 1) 压入栈顶; 当其中一个数值为NaN时, 将 - 1 压入栈顶
    0x96fcmpg比较栈顶两 float 型数值大小, 并将结果 (1, 0 或 - 1) 压入栈顶; 当其中一个数值为NaN时, 将 1 压入栈顶
    0x97dcmpl比较栈顶两 double 型数值大小, 并将结果 (1, 0 或 - 1) 压入栈顶; 当其中一个数值为NaN时, 将 - 1 压入栈顶
    0x98dcmpg比较栈顶两 double 型数值大小, 并将结果 (1, 0 或 - 1) 压入栈顶; 当其中一个数值为NaN时, 将 1 压入栈顶
    0x99ifeq当栈顶 int 型数值等于 0 时跳转
    0x9aifne当栈顶 int 型数值不等于 0 时跳转
    0x9biflt当栈顶 int 型数值小于 0 时跳转
    0x9cifge当栈顶 int 型数值大于等于 0 时跳转
    0x9difgt当栈顶 int 型数值大于 0 时跳转
    0x9eifle当栈顶 int 型数值小于等于 0 时跳转
    0x9fif_icmpeq比较栈顶两 int 型数值大小, 当结果等于 0 时跳转
    0xa0if_icmpne比较栈顶两 int 型数值大小, 当结果不等于 0 时跳转
    0xa1if_icmplt比较栈顶两 int 型数值大小, 当结果小于 0 时跳转
    0xa2if_icmpge比较栈顶两 int 型数值大小, 当结果大于等于 0 时跳转
    0xa3if_icmpgt比较栈顶两 int 型数值大小, 当结果大于 0 时跳转
    0xa4if_icmple比较栈顶两 int 型数值大小, 当结果小于等于 0 时跳转
    0xa5if_acmpeq比较栈顶两引用型数值, 当结果相等时跳转
    0xa6if_acmpne比较栈顶两引用型数值, 当结果不相等时跳转
    0xa7goto无条件跳转
    0xa8jsr跳转至指定的 16 位 offset 位置, 并将 jsr 的下一条指令地址压入栈顶
    0xa9ret返回至本地变量指定的 index 的指令位置 (一般与 jsr 或 jsr_w 联合使用)
    0xaatableswitch用于 switch 条件跳转, case 值连续 (可变长度指令)
    0xablookupswitch用于 switch 条件跳转, case 值不连续 (可变长度指令)
    0xacireturn从当前方法返回 int
    0xadlreturn从当前方法返回 long
    0xaefreturn从当前方法返回 float
    0xafdreturn从当前方法返回 double
    0xb0areturn从当前方法返回对象引用
    0xb1return从当前方法返回 void
    0xb2getstatic获取指定类的静态域, 并将其压入栈顶
    0xb3putstatic为指定类的静态域赋值
    0xb4getfield获取指定类的实例域, 并将其压入栈顶
    0xb5putfield为指定类的实例域赋值
    0xb6invokevirtual调用实例方法
    0xb7invokespecial调用超类构建方法, 实例初始化方法, 私有方法
    0xb8invokestatic调用静态方法
    0xb9invokeinterface调用接口方法
    0xbainvokedynamic调用动态方法
    0xbbnew创建一个对象, 并将其引用引用值压入栈顶
    0xbcnewarray创建一个指定的原始类型 (如 int, float, char 等) 的数组, 并将其引用值压入栈顶
    0xbdanewarray创建一个引用型 (如类, 接口, 数组) 的数组, 并将其引用值压入栈顶
    0xbearraylength获取数组的长度值并压入栈顶
    0xbfathrow将栈顶的异常抛出
    0xc0checkcast检验类型转换, 检验未通过将抛出 ClassCastException
    0xc1instanceof检验对象是否是指定类的实际, 如果是将 1 压入栈顶, 否则将 0 压入栈顶
    0xc2monitorenter获得对象的锁, 用于同步方法或同步块
    0xc3monitorexit释放对象的锁, 用于同步方法或同步块
    0xc4wide扩展本地变量的宽度
    0xc5multianewarray创建指定类型和指定维度的多维数组 (执行该指令时, 操作栈中必须包含各维度的长度值), 并将其引用压入栈顶
    0xc6ifnull为 null 时跳转
    0xc7ifnonnull不为 null 时跳转
    0xc8goto_w无条件跳转 (宽索引)
    0xc9jsr_w跳转至指定的 32 位 offset 位置, 并将 jsr_w 的下一条指令地址压入栈顶
  • 相关阅读:
    Java之IO流详解(二)——字节流
    C/C++总结笔记—— 关键字2:const关键字以及指针常量,常量指针,指向常量的常量指针
    鲲鹏devkit训练营——《锁长期等待》项目解析
    变分推断公式推导
    Spring原理学习(五)初始化与销毁
    Jörg Schilling逝世:创建CD刻录工具cdrtools,一生致力于开源
    [双重 for 循环]打印一个倒三角形
    纷享销客数字化营销(一):企业专属微站和员工智能名片
    准备HarmonyOS开发环境
    学弟:我适不适合转行做软件测试?
  • 原文地址:https://blog.csdn.net/qq_35024768/article/details/133781690