gpt4 book ai didi

Java-继承实现

转载 作者:太空宇宙 更新时间:2023-11-04 06:59:21 25 4
gpt4 key购买 nike

让我们考虑一下简单的界面:

interface Simple{
void doSth();
}

还有两个实现它的类:

class A implements Simple{

void someOtherMethod(){ .... }

void doSth(){ ... }

private void doSth(int x){ ... }
}

class B implements Simple{

void methodA(){ ..}
// many other methods

void doSth(){ ... }

private void doSth(Object o, long y){ ... }
}

现在,我可以轻松地写:

Simple s = new A();
s.doSth();

Java 的多态性性质将完成剩下的工作。有谁知道如何例如热点,确保链接器将链接到正确的方法,考虑到实现类中可以定义更多的方法,甚至它们的返回类型可以是原始类的子类? Java 是否确保接口(interface)方法始终从 vtable 中的某个偏移量开始,例如0?

最佳答案

在我们研究这个之前,让我们简化一下这个例子:

interface Foo {
void bar();
}

class AFoo implements Foo {
int i;

@Override
public void bar() {
i++;
}
}

class AnotherFoo implements Foo {
int i;

@Override
public void bar() {
i--;
}
}

public class Test {
public static void main(String[] args) {
Foo foo = new AFoo();
foo.bar();
}
}

编译后,我们使用

javap.exe -verbose Test.class

检查生成的字节代码:

  public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: new #16 // class tools/AFoo
3: dup
4: invokespecial #18 // Method tools/AFoo."<init>":()V
7: astore_1
8: aload_1
9: invokeinterface #19, 1 // InterfaceMethod tools/Foo.bar:()V
14: return

在类加载时,代码会被链接起来,即 specified Java 语言规范如下:

The binary representation of a class or interface references other classes and interfaces and their fields, methods, and constructors symbolically, using the binary names (§13.1) of the other classes and interfaces (§13.1). For fields and methods, these symbolic references include the name of the class or interface type of which the field or method is a member, as well as the name of the field or method itself, together with appropriate type information.

Before a symbolic reference can be used it must undergo resolution, wherein a symbolic reference is checked to be correct and, typically, replaced with a direct reference that can be more efficiently processed if the reference is used repeatedly.

请注意,这个“直接引用”是指方法的声明。如果有多个实现,运行时此时无法知道将使用哪个方法。也就是说,多态性不是在 Java 语言规范所谓的链接期间解决的,而是在执行实际方法调用表达式时解决的。这是specified根据 Java 虚拟机规范:

Let C be the class of objectref. The actual method to be invoked is selected by the following lookup procedure:

  • If C contains a declaration for an instance method with the same name and descriptor as the resolved method, then this is the method to be invoked, and the lookup procedure terminates.

  • Otherwise, if C has a superclass, this same lookup procedure is performed recursively using the direct superclass of C; the method to be invoked is the result of the recursive invocation of this lookup procedure.

  • Otherwise, an AbstractMethodError is raised.

如何实际实现这一点取决于 JVM 的实现。对于 Oracle Hotspot JVM,文档包含 rather detailed explanation :

When an invokeinterface call is linked, the linker resolves the call to an abstract target method, in an interface. This boils down to a target interface and a so-called itable index within that interface.

Target interfaces are never statically guaranteed by the JVM verifier; every invokeinterface receiver is typed as a simple object reference. Therefore (unlike invokevirtual calls), no assumptions can be made about the receiver's vtable layout. Instead, the receiver's class (as represented by its _klass field) must be checked more carefully. Where a virtual call can blindly perform two or three indirections to reach the target method, an interface call must first inspect the receiver's class to determine (a) if that class actually implements the interface, and (b) if so, where that interface's methods are recorded within that particular class.

There is no simple prefixing scheme in which an interface's methods are displayed at fixed offsets within every class that implements that interface. Instead, in the general (non-monomorphic) case, an assembly-coded stub routine must fetch a list of implemented interfaces from the receiver's InstanceKlass, and walk that list seeking the current target interface.

Once that interface is found (within the receiver's InstanceKlass), things get a little easier, because the interface's methods are arranged in an itable, or "interface method table", a display of methods whose slot structure is the same for every class that implements the interface in question. Therefore, once the interface is found within the receiver's InstanceKlass, an associated offset directs the assembly stub to an itable embedded in the InstanceKlass (just after the vtable, as one might expect). At that point, invocation proceeds as with virtual method calls.

Nearly the same optimizations apply to interface calls as to virtual calls. As with virtual calls, most interface calls are monomorphic, and can therefore be rendered as direct calls with a cheap check.

Here is a generic instruction trace of a polymorphic interface call:

callSite:
set #calledInterface, CHECK
call #itableStub[itableSlot]
---
itableStub[itableSlot]:
load (RCVR + #klass), KLASS_TEM
load (KLASS_TEM + #vtableSize), TEM
add (KLASS_TEM + TEM), SCAN_TEM
tryAgain:
# this part is repeated zero or more times, usually zero
load (SCAN_TEM + #itableEntry.interface), TEM
cmp TEM, CHECK
jump,eq foundInterface
test TEM
jump,z noSuchInterface
inc #sizeof(itableEntry), SCAN_TEM
jump tryAgain
tryAgain:
load (SCAN_TEM + #itableEntry.interface), TEM
cmp TEM, CHECK
jump,eq foundInterface
foundInterface:
load (SCAN_TEM + #itableEntry.offset), TEM
load (KLASS_TEM + TEM + #itableSlot), METHOD
load (METHOD + #compiledEntry), TEM
jump TEM
---
compiledEntry:
...

In all, that is six memory references and two nonlocal jumps.

迂腐的注释:以上所有内容都适用于调用接口(interface)方法。调用类中声明的抽象方法使用 different bytecode instruction ,还有一个稍微简单一点的 implementation in the Oracle Hotspot JVM .

关于Java-继承实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22331796/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com