项目地址:
数组介绍
数组在Java虚拟机中是个比较特殊的概念。为什么这么说呢?有下面几个原因:
首先,数组类和普通的类是不同的。普通的类从class文件中加载,但是数组类由Java虚拟机在运行时生成。数组的类名是左方括号
([)+数组元素的类型描述符;数组的类型描述符就是类名本身。例如,int[]的类名是[I,int[][]的类名是[[I,Object[]的类名是[Ljava/lang/Object;,String[][]的类名是[[java/lang/String;等等。其次,创建数组的方式和创建普通对象的方式不同。普通对象由new指令创建,然后由构造函数初始化。基本类型数组由
newarray指令创建;引用类型数组由anewarray指令创建;另外还有一个专门的multianewarray指令用于创建多维数组。最后,数组和普通对象存放的数据也是不同的。普通对象中存放的是实例变量,通过
putfield和getfield指令存取。数组对象中存放的则是数组元素,通过<t>aload和<t>astore系列指令按索引存取。其中<t>可以是a、b、c、d、f、i、l或者s,分别用于存取引用、byte、char、double、float、int、long或short类型的数组。另外,还有一个arraylength指令,用于获取数组长度。创建数组的Class
一般来说,一个 MyClass 实例对应一个 class 文件,但是数组的 Class 是没有对应的 class 文件的。所以我们直接创建一个 MyClass 对象,给它设置一个 className 就好了。
我们实现一下 ANewArray 指令,先看例子:
看看编译后的字节码:

anewarray 后面跟了一个类名 String。表示需要创建String类型的数组,数组的大小由前一条指令给出,也就是4。
看看代码实现:
逻辑很简单,resolvedClass 就是上面例子中的 String 类。我们需要根据这个类名创建出数组的类名,具体的规则就是:
有了类名后,使用 ClassLoader 加载一下,当然 ClassLoader 里面的逻辑也需要特殊处理:
主要就是设置 MyClass 里面的一些字段:
创建数组的Object
有了数组的 MyClass 之后,就需要创建对应的 MyObject 对象了,由于操作数组的指令需要改变对应 index 位置的值,所以我采用了数组来实现 ArrayObject:
<t>aload指令
<t>aload系列指令按索引取数组元素值,然后推入操作数栈。看一个例子,AALoad:
先从操作数栈里面取出一个索引值,再从操作数栈里面取出一个数组对象,然后拿到数组对象[索引]的值,重新 push 到操作数栈里面。
<t>astore
<t>astore系列指令按索引给数组元素赋值。与<t>aload系列指令是相反的逻辑就不贴代码了。
字符串
在class文件中,字符串是以MUTF8格式保存的。补充一个知识点,JNI里面也是 MUTF8 格式的。
前面我们加载了 String 的 class 文件,并且也可以创建出对应的 MyObject 对象,但是有一个问题,String 的一个 api 我们用不了,为啥呢?看一个例子:
看看编译后字节码:

ldc 指令会根据不同的常量池类型返回不同的类型值:
当常量池索引的类型是 String 类型的时候,我们获取到的是一个 Java 类型的 String 类型,需要将它转换成 MyObject 类型。不过这里需要注意的是,一个 java 的 String 对象肯定是做了初始化的,比如代码
String s = “xxx”; ,s变量的 value 字段需要初始化:
当一个String对象被创建的时候,value 的值就已经固定了,我们需要模拟这个过程:
测试
测试用例:

测试配置:

测试输出:











