Java 8字节码如何标记接口中的默认方法



由于java 8,它可以在接口中进行预定方法。其中一些标准实现已经在"标准"接口(例如Charsequence(上实施。如果您尝试读取具有JVM 7的Java 8字节码(例如Java主文件夹的Rt.jar(,则会发生错误。

,例如。类型Java.lang.CharSequence无法解决。

这可能不是级别之间的唯一区别,但我想了解新字节代码的结构。

public interface com.company.ITest {
public void HelloWorld();
Code:
   0: getstatic     #1                  // Field java/lang/System.out:Ljava/io/PrintStream;
   3: ldc           #2                  // String hello world
   5: invokevirtual #3                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8: return
}

此javaCode生产了此bbytecode:

public interface ITest {
    default void HelloWorld(){
        System.out.println("hello world");
    }
}

所以这是我的问题:默认接口对常数池有何影响?

这些标志中的任何一个相关:

constant_methodhandle constant_methodtype contents_invokedy namic

以以下示例:

public interface A {
    default void foo(){
       System.out.println("Calling A.foo()");
    }
}
public class Clazz implements A {
}

从客户端代码的角度来看,默认方法只是普通的虚拟方法。因此,名称 - 虚拟扩展方法。因此,如果示例使用客户端代码,则调用默认方法将在呼叫站点上生成InvokeInterface。

A clazz = new Clazz();
clazz.foo(); // invokeinterface foo()
Clazz clazz = new Clazz();
clazz.foo(); // invokevirtual foo()

在默认方法解决冲突的情况下,当我们覆盖默认方法并希望将调用委派给一个接口之一时,请推断出InvokeSpecial的一个接口,因为我们将专门称为实现:

public class Clazz implements A, B {
    public void foo(){
       A.super.foo(); // invokespecial foo()
    }
}
public void foo();
Code:
0: aload_0
1: invokespecial #2 // InterfaceMethod A.foo:()V
4: return

如您所见,InvokeSpecial指令用于调用接口方法FOO((。从字节码的角度来看,这也是新事物,因为以前您只能通过超级调用指向类(父类(而不是接口的方法。

这取决于您实际在做什么,您会偶然发现哪种障碍。你说

如果您尝试读取JVM 7的Java 8字节(例如Java Home文件夹的Rt.jar(,则会发生错误。

,例如。类型Java.lang.CharSequence无法解决。

但这甚至无法远程匹配您尝试"读取使用JVM 7"读取Java 8字节码的情况。如果您尝试使用Java 7 JVM加载Java 8类,则通常会获得VerifyError,并带有一条消息,告诉您不支持类文件版本。

相反,错误消息看起来非常类似于Eclipse编译器的众所周知的错误消息,因为它在读取类文件的字节代码失败时,这与JVM无关。正如本答案中所述,Eclipse似乎并没有在没有找到的类别之间有所作为,或者无法解析的类,只是说" 无法解决"。它似乎也忽略了类文件版本号,因此当它们不使用诸如default方法之类的较新功能时,它恰好与较新的类文件一起使用。

在技术层面上,差异很小。default方法与恒定池之间没有关系。游泳池条目类型CONSTANT_MethodHandleCONSTANT_MethodTypeCONSTANT_InvokeDynamicdefault方法无关,它们甚至不是新方法 - 它们已经是标准的一部分(这并不是只能阻止某些工具供应商忽略它们,只要它们做到了不遇到它们(。default方法只是一种不是abstract而不是static的方法,就像普通的public实例方法一样,而是在接口中。新事物是现在现在。一个简单的类文件解析器不在乎它是读取class还是interface的读取不会遇到困难。但是根据工具对数据的作用,它可能会在这样的类文件中(如果尚未停止版本编号停止(,就像较旧的Eclipse编译器一样。

如果JVM的验证者尚未停止在版本号上,则只会扔另一个VerifierError,说类文件违反了所有方法必须是abstract

anurag已经解释了有关默认方法的实现的一些解释,但是我想在您的问题中解决其他观点:

如果您尝试阅读Java 8字节码(例如Java Home的Rt.jar 文件夹(使用JVM 7,发生错误。

例如。类型Java.lang.CharSequence无法解决。

这是因为每个classFile都有一个版本代码,代表其编译为Java的版本。JVM将拒绝任何版本代码高于本身的classFile(但是还可以,但要向后兼容(。

这意味着,即使对字节码更改的其他内容也没有其他内容,JVM仍然会拒绝从Java的未来版本加载classfiles。您可以使用Java 10编译" Hello World",即使没有新功能或字节差异,Java 9 JVM也会拒绝加载它。

最新更新