类与对象
public class MyObject {
public static int staticVar;
public int instanceVar;
public static void main(String[] args) {
int x = 32768; // ldc
MyObject myObj = new MyObject(); // new
MyObject.staticVar = x; // putstatic
x = MyObject.staticVar; // getstatic
myObj.instanceVar = x; // putfield
x = myObj.instanceVar; // getfield
Object obj = myObj;
if (obj instanceof MyObject) { // instanceof
myObj = (MyObject) obj; // checkcast
System.out.println(myObj.instanceVar);
}
}
以上面这段为例,我们继续说一下JVM对类与对象的实现机制
PUTFIELD
myObj.instanceVar = x; // putfield
type PUT_FIELD struct {
base.Index16Instruction
}
func (self *PUT_FIELD) Execute(frame *rtda.Frame) {
currentMethod := frame.Method()
currentClass := currentMethod.Class()
cp := currentClass.ConstantPool()
fieldRef := cp.GetConstant(self.Index).(*heap.FieldRef)
field := fieldRef.ResolvedField()
if field.IsStatic() {
panic("java.lang.IncompatibleClassChangeError")
}
if field.IsFinal() {
if currentClass != field.Class() || currentMethod.Name() != "<init>" {
panic("java.lang.IllegalAccessError")
}
}
descriptor := field.Descriptor()
slotId := field.SlotId()
stack := frame.OperandStack()
switch descriptor[0] {
case 'Z', 'B', 'C', 'S', 'I':
val := stack.PopInt()
ref := stack.PopRef()
if ref == nil {
panic("java.lang.NullPointerException")
}
ref.Fields().SetInt(slotId, val)
// ...
default:
// TODO
}
}
其实可以发现,PUTFIELD的实现与PUTSTATIC很相似,下面的代码同样是从当前函数栈帧中获取当前函数的类,再找到对应的fieldRef并解引用,获取fieldRef对应的Field,检查其Static和Final并进行对应的处理。
currentMethod := frame.Method()
currentClass := currentMethod.Class()
cp := currentClass.ConstantPool()
fieldRef := cp.GetConstant(self.Index).(*heap.FieldRef)
field := fieldRef.ResolvedField()
if field.IsStatic() {
panic("java.lang.IncompatibleClassChangeError")
}
if field.IsFinal() {
if currentClass != field.Class() || currentMethod.Name() != "<init>" {
panic("java.lang.IllegalAccessError")
}
}
不同点在于下面:这次我们不是直接去修改Class的StaticVars,而是去修改了当前实例的对应的内存
descriptor := field.Descriptor()
slotId := field.SlotId()
stack := frame.OperandStack()
switch descriptor[0] {
case 'Z', 'B', 'C', 'S', 'I':
val := stack.PopInt()
ref := stack.PopRef()
if ref == nil {
panic("java.lang.NullPointerException")
}
ref.Fields().SetInt(slotId, val)
// ...
default:
// TODO
}
我们分开来说:
val := stack.PopInt()
ref := stack.PopRef()
val
是我们将要赋予的值,而ref
是一个Object
,是当前类实例的引用,没错就是this
指针,让我们来回顾一下Object
的结构
type Object struct {
class *Class
data interface{}
extra interface{} // 记录Object结构体实例的额外信息
}
func newObject(class *Class) *Object {
return &Object{
class: class,
data: newSlots(class.instanceSlotCount), // 分配类实例内存
}
}
Object.data
是与Class.instanceSlotCount
占用量相同的数据区。
func (self *Object) Fields() Slots {
return self.data.(Slots)
}
func (self Slots) SetInt(index uint, val int32) {
self[index].num = val
}
ref.Fields().SetInt()
就是在Object的对应数据区设置val。
GETFIELD
x = myObj.instanceVar; // getfield
type GET_FIELD struct {
base.Index16Instruction
}
func (self *GET_FIELD) Execute(frame *rtda.Frame) {
cp := frame.Method().Class().ConstantPool()
fieldRef := cp.GetConstant(self.Index).(*heap.FieldRef)
field := fieldRef.ResolvedField()
if field.IsStatic() {
panic("java.lang.IncompatibleClassChangeError")
}
stack := frame.OperandStack()
ref := stack.PopRef() // this指针
if ref == nil {
panic("java.lang.NullPointerException")
}
descriptor := field.Descriptor()
slotId := field.SlotId()
slots := ref.Fields() // 当前实例所有的字段
switch descriptor[0] {
// ...
case 'L', '[':
stack.PushRef(slots.GetRef(slotId)) // 从指定的字段中获取数据 并压入操作数栈
default:
// TODO
}
}