自制java虚拟机_自制Java虚拟机(五)实现继承、多态、invokevirtual

自制Java虚拟机(五)实现继承、多态、invokevirtual

本篇文章将研究如何实现面向对象的继承和多态特性,同时实现invokevirtual。

一、实例属性的继承

继承实现了数据与方法的复用。

类属性与实例属性

类属性的修饰符要加上static,是属于类的

类属性只有一份,该类创建的多个对象共享同一份类属性,jvm中由getstatic、putstatic指令操作

实例属性每个对象各自一份,各管各的互不干扰,jvm中由getfield、putfield指令操作

上篇文章在没有考虑没有继承的情况下实现了getfield、putfield指令,本篇文章来对其进行修正。

public、protected与private

不管是父类的实例属性是public、protected还是private,子类继承自父类时,这些属性都会一一继承,只不过private的属性在子类中不能直接访问而已。你看不到它们,但它们确实存在。

现在问题来了:

创建对象时,我们要给从父类(以及父类的父类…)继承过来的实例属性分配内存,并指定索引以便访问,如何安排这些索引?

子类中访问父类的public、protected实例属性时,索引是指向子类常量池的Fieldref类型的结构,而这个结构的class_index是指向当子类的,必须能够正确地解析到定义该属性的类

解决方法:

实例属性的安排。在类的继承链上,自顶向下给每个类的实例属性从小到大依次指定索引,这样就保证每个类的实例属性在该继承链中索引是唯一的。如下图布局1所示:

5a7ac5f8c9d10087a4c3ce749e969b93.png

因此我们在创建对象的时候必须从当前类开始,沿着继承链遍历父类,计算继承而来的属性个数并重新给每个类的实例属性指定索引(之前计算的的索引是在当前类的)。

修正之后创建对象的函数newObject实现可以如下:

Object* newObject(OPENV *env, Class* pclass) {

CONSTANT_Class_info* parent_class_info;

Object *obj;

Class *tmp_class;

int i = 0, total_size = 0;

// step 1: load parent class recursively

tmp_class = pclass;

while (tmp_class->super_class) {

parent_class_info = tmp_class->constant_pool[tmp_class->super_class];

if (NULL == parent_class_info->pclass) {

parent_class_info->pclass = systemLoadClassRecursive(env, (CONSTANT_Utf8_info*)(tmp_class->constant_pool[parent_class_info->name_index]));

tmp_class->parent_class = parent_class_info->pclass;

}

tmp_class = tmp_class->parent_class;

}

// step 2: calculate fields size of parent class recursively, if not calculated yet

if (pclass->parent_fields_size == -1) {

if (pclass->parent_class == NULL) {

pclass->parent_fields_size = 0;

} else {

pclass->parent_fields_size = getClassFieldsSize(pclass->parent_class);

}

for(i=0; ifields_count; i++) {

// 重新计算属性的索引:过滤掉类属性

if (NOT_ACC_STATIC((pclass->fields[i])->access_flags)) {

(pclass->fields[i])->findex += pclass->parent_fields_size;

}

}

}

// 该对象属性的总大小 = 父类实例属性大小 + 本类实例属性大小

total_size = pclass->parent_fields_size + pclass->fields_size;

obj = (Object*)malloc(sizeof(Object) + ((total_size+1)<<2));

obj->fields = (char*)(obj+1);

obj->pclass = pclass;

obj->length = (total_size+1) << 2;

return obj;

}

由于父类可能需要多次用到,我们可以把解析好的父类保存在一个哈希中,用类的全限定名做为索引,如test/Parent,需要引用父类的时候先用哈希表中找,找不到就加载,加载完毕保存在哈希表中,为了方便,在当前类中设置个字段指向父类。

typedef struct _ClassFile{

...

struct _ClassFile *parent_class; // 指向父类

int parent_fields_size;

int fields_size;

} ClassFile;

typedef ClassFile Class;

(java/lang/Object是所有类的终极父类,我们一直往上解析的话最终会涉及到这个类,可以从Java的安装目录的jdk目录下找到src.zip这个包,这个包有jdk的源码,把它解压到指定目录,编译成class文件,到时我们的类加载函数从这里加载就行)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值