代码、内容参考来自于张秀宏大佬的自己动手写Java虚拟机 (Java核心技术系列)以及尚硅谷宋红康:JVM全套教程。
如果数组的元素是基本类型,就把它叫作基本类型数组 ,数组的元素是引用类型,把它叫作引用类型数组 。基本类型数组肯定都是一维数组 ,如果引用类型数组的元素也是数组,那么它就是多维数组 。
1.数组概述
数组类和普通的类是不同的。普通的类从class文件中加载,但是数组类由Java虚拟机在运行时生成。数组的类名是左方括号([)+数组元素的类型描述符;数组的类型描述符就是类名本身。
还有指令等的不同,这些将在下面说。
将在类和对象的基础上实现数组类和数组对象,先来看数组对象。
2.数组对象
和普通对象一样,数组也是分配在堆中的,通过引用来使用。所以需要改造Object结构体,让它既可以表示普通的对象,也可以表示数组。打开ch08\rtda\heap\object.go,修改Object结构体,把fields字段改为data,类型也从Slots变成了interface{}。改动如下:
type Object struct {
class *Class
data interface{} // Slots for Object, []int32 for int[] ...
}
Go语言的interface{}该类型的变量可以容纳任何类型的值。对于普通对象来说,data字段中存放的仍然还是Slots变量。但是对于数组,可以在其中放各种类型的数组。
newObject()用来创建普通对象,因此需要做相应的调整,改动如下:
// create normal (non-array) object
func newObject(class *Class) *Object {
return &Object{
class: class,
data: newSlots(class.instanceSlotCount),
}
}因为Fields()方法也仍然只针对普通对象,所以它的代码也需要做相应调整,如下所示:
func (self *Object) Fields() Slots {
return self.data.(Slots)
}
需要给Object结构体添加几个数组特有的方法,在ch08/rtda/heap目录下创建array_object.go文件,在其中实现8个方法,分别针对引用类型数组和7种基本类型数组返回具体的数组数据。在其中添加一个ArrayLength()方法,<t>aload和<t>astore系 列指令各有8条,所以针对每种类型都提供一个方法,返回相应的 数组数据。因为arraylength指令只有一条,所以ArrayLength()方法 需要自己判断数组类型,而Booleans()方法将使用[]int8来表示布尔数组,所以只需要Bytes()方法即可。
package heap
func (self *Object) Bytes() []int8 {
return self.data.([]int8)
}
func (self *Object) Shorts() []int16 {
return self.data.([]int16)
}
func (self *Object) Ints() []int32 {
return self.data.([]int32)
}
func (self *Object) Longs() []int64 {
return self.data.([]int64)
}
func (self *Object) Chars() []uint16 {
return self.data.([]uint16)
}
func (self *Object) Floats() []float32 {
return self.data.([]float32)
}
func (self *Object) Doubles() []float64 {
return self.data.([]float64)
}
func (self *Object) Refs() []*Object {
return self.data.([]*Object)
}
func (self *Object) ArrayLength() int32 {
switch self.data.(type) {
case []int8:
return int32(len(self.data.([]int8)))
case []int16:
return int32(len(self.data.([]int16)))
case []int32:
return int32(len(self.data.([]int32)))
case []int64:
return int32(len(self.data.([]int64)))
case []uint16:
return int32(len(self.data.([]uint16)))
case []float32:
return int32(len(self.data.([]float32)))
case []float64:
return int32(len(self.data.([]float64)))
case []*Object:
return int32(len(self.data.([]*Object)))
default:
panic("Not array!")
}
}
3.数组类
在/rtda/heap目录下创建array_class.go文件,在其中定义NewArray()方法,NewArray()方法专门用来创建数组对象。如果类并不是数组类,就调用panic()函数终止程序执行,否则根据数组类型创建数组对象。代码如下:
package heap
// 判断是否是数组类
func (self *Class) IsArray() bool {
return self.name[0] == '['
}
// 用来创建数组对象
func (self *Class) NewArray(count uint) *Object {
if !self.IsArray() {
panic("Not array class: " + self.name)
}
switch self.Name() {
case "[Z":
//布尔数组是使用字节数组来表示
return &Object{self, make([]int8, count)}
case "[B":
return &Object{self, make([]int8, count)}
case "[C":
return &Object{self, make([]uint16, count)}
case "[S":
return &Object{self, make([]int16, count)}
case "[I":
return &Object{self, make([]int32, count)}
case "[J":
return &Object{self, make([]int64, count)}
case "[F":
return &Object{self, make([]float32, count)}
case "[D":
return &Object{self, make([]float64, count)}
default:
return &Object{self, make([]*Object, count)}
}
}注意,布尔数组是使用字节数组来表示的:

4.加载数组类
打开\rtda\heap\class_loader.go文件,修改LoadClass()方法,增加了类型判断,如果要加载的类是数组类,则调用新的loadArrayClass()方法,否则还按照原来的逻辑。loadArrayClass()方法需要生成一个Class结构体实例,代码如下:
func (self *ClassLoader) LoadClass(name string) *Class {
if class, ok := self.classMap[name]; ok {
// already loaded
return class
}
// 若类名的第一个字符是'[',表示该类是数组类
if name[0] == '[' {
// array class
return self.loadArrayClass(name)
}
return self.loadNonArrayClass(name)
}
func (self *ClassLoader) loadArrayClass(name string) *Class {
//生成Class结构体,数组类由Java虚拟机在运行时生成所以没有像loadNonArrayClass()那样读取class文件
class := &Class{
// 访问标识
accessFlags: ACC_PUBLIC, // todo
// 类名
name: name,
// 类加载器
loader: self,
// 数组类不需要初始化,所以设成true
initStarted: true,
// 数组类的超类是java/lang/Object
superClass: self.LoadClass("java/lang/Object"),
interfaces: []*Class{
// 数组类实现了java.lang.Cloneable接口
self.LoadClass("java/lang/Cloneable"),
// 数组类实现了java.io.Serializable
self.LoadClass("java/io/Serializable"),
},
}
self.classMap[name] = class
return class
}注意:因为数组类不需要初始化,所以把initStarted字段设置成true。数组类的超类是java.lang.Object,并且实现了java.lang.Cloneable和java.io.Serializable接口。
5.参考
尚硅谷宋红康:JVM全套教程:https://www.bilibili.com/video/BV1PJ411n7xZ
周志明:深入理解java虚拟机
张秀宏:自己动手写Java虚拟机 (Java核心技术系列)


