当搜寻通过Java语言规范来回答这个问题),我学到的是
在初始化类之前,必须先初始化其直接超类, 但不初始化由该类实现的接口。 同样,在初始化接口之前,不会初始化接口的超级接口。
出于我自己的好奇心,我尝试了一下,并且未如预期的那样对接口InterfaceType
进行了初始化。
public class Example {
public static void main(String[] args) throws Exception {
InterfaceType foo = new InterfaceTypeImpl();
foo.method();
}
}
class InterfaceTypeImpl implements InterfaceType {
@Override
public void method() {
System.out.println("implemented method");
}
}
class ClassInitializer {
static {
System.out.println("static initializer");
}
}
interface InterfaceType {
public static final ClassInitializer init = new ClassInitializer();
public void method();
}
该程序打印
implemented method
但是,如果接口声明了一个default
方法,则确实会发生初始化。考虑InterfaceType
给定的接口
interface InterfaceType {
public static final ClassInitializer init = new ClassInitializer();
public default void method() {
System.out.println("default method");
}
}
然后上面的相同程序将打印
static initializer
implemented method
换句话说,static
接口的字段已初始化(详细初始化过程中的步骤9),并static
执行了正在初始化的类型的初始化程序。这意味着接口已初始化。
我在JLS中找不到任何东西来表明这应该发生。不要误会我的意思,我知道如果实现类没有提供该方法的实现,应该发生这种情况,但是如果实现了该怎么办?Java语言规范中是否缺少这种情况?我错过了什么吗?还是我误解了?
这是一个非常有趣的问题!
似乎JLS第12.4.1节应该明确地涵盖这一点。但是,OracleJDK和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
实际上我得到了预期的输出。但是,如果将默认方法添加到interface 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
永远不会最终在接口中执行代码,因此这没有发生。可能有人认为这是类/接口准备阶段(JLS12.3.2)的一部分,该阶段初始化诸如方法表之类的东西。但这可能太过分了,而意外地进行了完全初始化。
我在OpenJDK编译器-
dev邮件列表中提出了这个问题。AlexBuckley(JLS的编辑)收到了答复,他提出了更多针对JVM和lambda实现团队的问题。他还指出,规范中存在一个错误,该错误表明“
T是一个类,并且调用了T声明的静态方法”,如果T是接口,则也应适用。因此,这里可能同时存在规范和HotSpot错误。
披露 :我在OpenJDK上为Oracle工作。如果人们认为这使我在悬赏这个问题上获得了不公平的优势,那么我愿意对此灵活。
在搜索Java语言规范来回答这个问题时,我了解到 在初始化一个类之前,必须初始化它的直接超类,但是该类实现的接口没有初始化。类似地,在初始化接口之前,不会初始化接口的超级接口。 出于我自己的好奇心,我尝试了它,正如预期的那样,接口没有初始化。 这个程序打印 但是,如果接口声明了一个方法,则会发生初始化。考虑<代码>接口类型>代码>接口 然后上面相同的程序会打印 换句话说,初始化接口的字段(详细初始
具有默认方法的接口将被初始化,即使重写了此方法,并且根本没有调用该方法。 示例: 印刷品: 这里到底有什么问题?
问题内容: 如果未将显式值传递给方法,我想用一些默认值初始化方法的参数-像这样: 我得到了错误: 我该如何解决? 问题答案: 这里常见的习惯用法是将默认值设置为一些哨兵值用于此目的,这是典型值,然后您可以检查该值。 您可能还会看到用于哨兵的实例。 后一个版本的优点是可以传递给函数,但有一些缺点(请参见下面@larsmans的评论)。如果您不认为需要将有意义的参数传递给您的方法,那么我建议您使用它。
考虑以下代码 VS2013编译器发出以下警告: 警告C4351:新行为:数组“B::member”的元素将默认初始化1 这里有记载 使用C 11,并应用“默认初始化”的概念,意味着B. part的元素将不会被初始化。 但我认为,成员{}应该执行值初始化,而不是默认初始化。VS2013编译器是否损坏? 8.5美元/6 默认初始化类型为T的对象意味着:-如果T是(可能是cv限定的)类类型(第9条),则
我在Visual Studio2019中有一个Xamarin表单解决方案。我现在只使用Android版本。完整的错误是: 在此进程中,java.lang.IllegalStateException Message=Default FirebaseApp未初始化AppCardView.Sample.android。确保首先调用FirebaseApp.InitializeApp(上下文)。 在我的An
null 必需的 获取java对象(根元素),它完全表示xml和每个由默认值初始化的成员。 当我试图在不显式设置值的情况下marshall xml时,默认值并不能使sence...有什么方法可以在不自定义生成的类的情况下,用默认值填充xml吗? .xsd的示例: 和java类: