这是一个非常有趣的问题!
这好像是JLS 第 12.4.1 节 http://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.4.1应该明确地涵盖这一点。但是,Oracle JDK 和 OpenJDK(javac 和 HotSpot)的行为与此处指定的不同。特别是,本节中的示例 12.4.1-3 涵盖了接口初始化。示例如下:
interface I {
int i = 1, ii = Test.out("ii", 2);
}
interface J extends I {
int j = Test.out("j", 3), jj = Test.out("jj", 4);
}
interface K extends J {
int k = Test.out("k", 5);
}
class Test {
public static void main(String[] args) {
System.out.println(J.i);
System.out.println(K.j);
}
static int out(String s, int i) {
System.out.println(s + "=" + i);
return i;
}
}
其预期输出为:
1
j=3
jj=4
3
事实上我得到了预期的输出。但是,如果在接口中添加默认方法I
,
interface I {
int i = 1, ii = Test.out("ii", 2);
default void method() { } // causes initialization!
}
输出更改为:
1
ii=2
j=3
jj=4
3
这清楚地表明了该接口I
正在初始化,之前没有初始化!仅存在默认方法就足以触发初始化。不必调用或重写甚至提及默认方法,抽象方法的存在也不会触发初始化。
我的猜测是 HotSpot 实现希望避免将类/接口初始化检查添加到关键路径中invokevirtual
称呼。在 Java 8 和默认方法之前,invokevirtual
永远不可能最终在接口中执行代码,所以这不会出现。人们可能会认为这是类/接口准备阶段的一部分(JLS 12.3.2 http://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.3.2)它初始化方法表之类的东西。但也许这太过分了,意外地进行了完全初始化。
I've 提出这个问题 http://mail.openjdk.java.net/pipermail/compiler-dev/2014-April/008733.html在 OpenJDK 编译器开发邮件列表上。曾经有一个亚历克斯·巴克利的回复 http://mail.openjdk.java.net/pipermail/compiler-dev/2014-April/008734.html(JLS 的编辑),他在其中针对 JVM 和 lambda 实现团队提出了更多问题。他还指出,这里的规范中有一个错误,其中说“T 是一个类,并且调用 T 声明的静态方法”,如果 T 是一个接口,也应该适用。因此,这里可能同时存在规范和 HotSpot 错误。
披露:我在 Oracle 工作,使用 OpenJDK。如果人们认为这给了我在获得这个问题的奖金方面不公平的优势,我愿意对此保持灵活性。