java 虚拟表_虚拟表和Java中的抽象

这是执行代码的顺序。更多细节如下。

> main()

>调用Derived。< init>()(隐式nullary构造函数)

>调用Base。< init>()

>将Base.x设置为1。

>调用Derived.foo()

>打印Derived.x,它仍然具有默认值0

>将Derived.x设置为2。

>调用Derived.foo()。

>打印Derived.x,现在是2。

要完全了解发生了什么,您需要知道几件事情。

场阴影

Base的x和Derived的x是完全不同的字段,恰好具有相同的名称。 Derived.foo打印Derived.x,而不是Base.x,因为后者被前者“shadowed”。

隐式构造函数

由于Derived没有明确的构造函数,编译器会生成一个隐式的零参数构造函数。在Java中,每个构造函数必须调用一个超类构造函数(除了Object,它没有超类),这给了超类一个安全地初始化其字段的机会。编译器生成的nullary构造函数只需调用其超类的空值构造函数。 (如果超类没有空值构造函数,则会生成编译错误。)

所以,Derived的隐式构造函数看起来像

public Derived() {

super();

}

初始化程序块和字段定义

初始化程序块以声明顺序组合,形成一个插入到所有构造函数中的大块代码。具体来说,它是在super()调用之后但在构造函数的其余部分之前插入的。字段定义中的初始值分配与初始化程序块类似。

所以如果我们有

class Test {

{x=1;}

int x = 2;

{x=3;}

Test() {

x = 0;

}

}

这相当于

class Test {

int x;

{

x = 1;

x = 2;

x = 3;

}

Test() {

x = 0;

}

}

这就是编译构造函数实际上是如何的:

Test() {

// implicit call to the superclass constructor, Object.()

super();

// initializer blocks, in declaration order

x = 1

x = 2

x = 3

// the explicit constructor code

x = 0

}

现在让我们返回Base和Derived。如果我们反编译它们的构造函数,我们会看到类似的东西

public Base() {

super(); // Object.()

x = 1; // assigns Base.x

foo();

}

public Derived() {

super(); // Base.()

x = 2; // assigns Derived.x

}

虚拟调用

在Java中,实例方法的调用通常通过虚拟方法表。 (这里有例外,最终类的构造方法,私有方法,final方法和方法不能被覆盖,所以这些方法可以被调用而不经过一个vtable,而超级调用不会通过vtables,因为它们本身就不是多态)

每个对象都保存一个指向一个类句柄的指针,它包含一个vtable。一旦对象被分配(使用NEW)并在调用任何构造函数之前,就会设置此指针。所以在Java中,构造函数可以安全地进行虚拟方法调用,并且它们将被正确地定向到目标的虚拟方法的实现。

所以当Base的构造函数调用foo()时,它调用Derived.foo,它打印Derived.x。但是Derived.x尚未分配,因此读取和打印默认值0。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值