📚《深入浅出JVM字节码》

《Java虚拟机字节码:从入门到实战》的开源版本。作者通过自己的实战经验,整合出一套适合新手的高效学习教程。归纳并提炼知识点,制定合理路线,帮助读者更快掌握核心技术。


创建数组与访问数组元素

数组的创建和访问也是我们编程中经常用到的。在java中,如果访问一个数组的索引超过数组的大小,会被虚拟机检测到,并抛出数组越界异常,这是因为在虚拟机执行访问数组的指令时,数组的大小已经是确定的,可以在执行指令之前检测索引是否超过数组大小。我们分基本数据类型数组、引用类型数组两种不同的数组,分别看下如何在字节码层面实现创建数组与访问数组元素。

基本数据类型数组

基本数据类型的数组创建与访问案例代码如下。

public static void baseArray() {
   int[] array = new int[2];
   array[1] = 100;
   int a = array[1];
}

使用javap命令输出baseArray方法的字节码如下。

public static void baseArray();
    Code:
       0: iconst_2
       1: newarray       int
       3: astore_0
       4: aload_0
       5: iconst_1
       6: bipush        100
       8: iastore
       9: aload_0
      10: iconst_1
      11: iaload
      12: istore_1
      13: return

偏移量为0和1的字节码指令完成数组的创建,iconst_2指令设置数组的大小为2,将立即数2放入操作数栈顶,newarray指令需要一个操作数,操作数占一个字节,如创建int类型的数组则操作数的值为10,创建long类型的数组则操作数为11等。

偏移量为4、5、6、8的这四条指令分别是将数组引用放入操作数栈顶、将访问数组的索引1放入操作数栈顶、将立即数100放入栈顶、最后iastore指令将栈顶int类型值100保存到int类型数组的索引为1的位置。偏移量为9、10、11这三条指令是读取int类型数组索引为1的元素放入操作数栈的栈顶,iaload指令将int类型数组指定索引位置处的元素放入操作数栈的栈顶。

iaload指令和iastore指令都不需要操作数。iastore指令执行之前,要求当前操作数栈必须存在int类型数组的引用、访问数组的索引、将要赋给数组元素的值,这三项是按顺序入栈的。iaload指令执行之前,要求当前操作数栈必须存在int类型数组的引用、访问数组的索引,按顺序入栈,返回值存放在操作数栈的栈顶。

访问基本数据类型数组的指令还有,对应long类型的laload和lastore指令,对应float类型的faload和fastore指令,对应double类型的daload和dastore指令,对应char类型的caload和castore指令,对应short类型的saload和sastore指令。

指令的操作码 指令的助记符 操作数 描述
0x2E iaload 将int类型数组中指定索引的元素放入操作数栈顶
0x2F laload 将long类型数组中指定索引的元素放入操作数栈顶
0x30 faload 将float类型数组中指定索引的元素放入操作数栈顶
0x31 daload 将double类型数组中指定索引的元素放入操作数栈顶
0x34 caload 将char类型数组中指定索引的元素放入操作数栈顶
0x35 saload 将short类型数组中指定索引的元素放入操作数栈顶
0x33 baload 将boolean类型数组中指定索引的元素放入操作数栈顶
0x4F iastore 将栈顶int类型值保存到指定int类型数组的指定索引处
0x50 lastore 将栈顶long类型值保存到指定long类型数组的指定索引处
0x51 fastore 将栈顶float类型值保存到指定int类型数组的指定索引处
0x52 dastore 将栈顶double类型值保存到指定double类型数组的指定索引处
0x55 castore 将栈顶char类型值保存到指定char类型数组的指定索引处
0x56 sastore 将栈顶short类型值保存到指定short类型数组的指定索引处
0x54 bastore 将栈顶boolean类型值保存到指定boolean类型数组的指定索引处

引用类型数组

引用类型数组的创建与访问案例代码如下。

public static void objArray(){
    User[] users = new User[2];
    users[0] = new User();
    users[1] = users[0];
}

使用javap命令输出objArray方法的字节码如下。

public static void objArray();
    Code:
       0: iconst_2
       1: anewarray     #2                  // class com/wujiuye/asmbytecode/book/third/model/User
       4: astore_0
       5: aload_0
       6: iconst_0
       7: new           #2                  // class com/wujiuye/asmbytecode/book/third/model/User
      10: dup
      11: invokespecial #3                  // Method com/wujiuye/asmbytecode/book/third/model/User."<init>":()V
      14: aastore
      15: aload_0
      16: iconst_1
      17: aload_0
      18: iconst_0
      19: aaload
      20: aastore
      21: return

与访问基本数据类型数组的逻辑差不多,创建数组使用的是anewarray指令,获取数组指定索引位置处的元素使用aaload指令,将对象的引用写到数组指定索引位置处使用aastore指令。

引用类型数组访问指令如下表所示。

指令的操作码 指令的助记符 操作数 描述
0x2E aaload 将引用类型数组中指定索引的元素放入操作数栈顶
0x53 aastore 将栈顶引用类型值保存到引用类型数组的指定索引位置处

创建数组的指令如下表所示。

指令的操作码 指令的助记符 操作数 描述
0xBC newarray 创建基本数据类型的数组
0x53 anewarray 创建引用类型的数组

访问数组的字节码指令还有获取数组一维长度的arrayleng指令,创建多维度的数组指令multinewarray指令。


发布于:2021 年 08 月 21 日
作者: 吴就业
链接: https://github.com/wujiuye/JVMByteCodeGitBook
来源: Github Pages 开源电子书《深入浅出JVM字节码》(《Java虚拟机字节码从入门到实战》的第二版),未经作者许可,禁止转载!


📚目录