当前位置: 首页 > 知识库问答 >
问题:

当通过派生类引用调用基类的静态方法时,哪些类被初始化?

公孙棋
2023-03-14

我认为只有Base类在调用Derive. f()时才被初始化。当我们在Base中有一个(非编译时间常量)静态字段,而不是静态方法时,就会发生这种情况。我的疑问只是JLS对此不太清楚。

class Base {
    static void f() {}
}

class Derived extends Base {

}

Derived.f(); // called legally from some other class

根据JLS(见下文),似乎只有基类被初始化。但我是否正确阅读和理解JLS?

我还知道,在(非编译时间常量)静态字段的情况下,只有定义静态字段的类才被初始化(称为静态初始化器):

class Base {
    static int x = 1;
}

class Derived extends Base {

}

//somewhere in some other class
int y = Derived.x;  // triggers only Base to be initialized

12.4.1. 发生初始化时(JLS)

类或接口类型T将在第一次出现以下任一情况之前立即初始化:

T是一个类,并且创建了T的一个实例。

T是一个类,调用由T声明的静态方法。

分配由T声明的静态字段。

使用由T声明的静态字段,该字段不是常量变量(§4.12.4)。

T是一个顶层类(§7.6),并执行一个词法嵌套在T(§8.1.3)中的断言语句(§14.10)。

对静态字段(§8.3.1.1)的引用只会导致实际声明它的类或接口的初始化,即使它可能通过子类、子接口或实现接口的类的名称来引用。但是JLS在这里并没有对静态方法进行同样的说明!

此外,12.4。类和接口的初始化清楚地表明:

在类初始化之前,必须初始化它的直接超类

JLS只对静态字段,而不是静态方法给出了这个规则的排除!

初始化基字段和派生字段似乎都有意义——如果f()在其主体中使用任何静态字段,则使用派生字段。f()将使用派生的静态字段(如果有的话),并可能使用从基继承的静态字段(如果派生的没有)-这对初始化两个类都有意义。

毕竟,简单测试表明只有基类被初始化:

class Base {
    static { System.out.println("Base init"); }

    static void f() {}
}

class Derived extends Base {
    static { System.out.println("Derived init"); }
}

public class Driver {
    public static void main(String[] args)  {
        Derived.f(); // Only "Base init" printed...
    }
}

共有1个答案

宿洋
2023-03-14

也许混淆是关于成员变量,包括静态成员变量,如何在Java中工作。与方法不同,派生类中的成员变量不继承它们的超级类对应项;它们只是影子。因此,如果你有一个变量fooBase和一个变量foo派生中,那么如果你在Base中打印该变量,你将在Base<中获得版本/code>,如果从Derived打印,则在Derived中获取版本。

public class MyTest {

    public static void main(String[] args) {

        Base.f();
        Derived.f();
    }
}

class Base {
    static void f() {
        System.out.println("bar = " + bar);
    }

    protected static int bar = 1;
}

class Derived extends Base {
    static void f() {
        System.out.println("bar = " + bar);
    }

    private static int bar = 2;
}

给予:

bar = 1
bar = 2

因此,当您调用Base中的静态方法时,没有理由运行Derived中的初始化器,因为Base永远无法直接访问Derived中的变量。

 类似资料:
  • 考虑以下代码: GCC v6.1编译它,叮当声3.8拒绝它,错误如下: 2:错误:没有成员名为'foo'在'U' struct S{静态constexpr int bar=T::foo;}; 哪个编译器是对的? 会不会是因为在我们尝试在中使用它时不是一个完整的类型? 在这种情况下,它应该被认为是GCC的错误,但我想知道我是否正确之前在错误跟踪器上搜索/打开问题… 编辑 与此同时,我已经向GCC打开

  • 给定以下类: 我不是针对“因为JLS中规定了这样的问题”这样的答案。我知道是这样的,因为JLS 12.4.1在初始化时只读取: 类或接口类型T将在第一次出现以下任一情况之前立即初始化: > T是一个类,调用由T声明的静态方法。 ... 我感兴趣的是,为什么没有这样一句话,是否有一个很好的理由: T是S的子类,在T上调用由S声明的静态方法

  • 我可以写: 但这当然不起作用,因为调用返回的是而不是我想要的。 我可以通过在(以及和等)中键入类型并抑制警告来避免这个问题,但是即使只有(比方说)10个扩展的实体类,这仍然是为了键入而编写的样板代码。此外,我认为,如果我必须为每个派生类编写代码(无论多么小)(也会有其他类似的方法),那么拥有类层次结构的好处就会丧失很多。 对此有更好的解决方案吗? 更新:使用Java7。

  • 问题内容: 给定以下类别: 我的目标不是像“因为在JLS中这样指定”这样的答案。我知道是的,因为JLS是12.4.1发生初始化时,其读取内容仅为: 类或接口类型T将在以下任何一种首次出现之前立即初始化: … T是一个类,并调用T声明的静态方法。 … 我对没有这样的句子是否有充分的理由感兴趣: T是S的子类,并且在T上调用S声明的静态方法。 问题答案: 我认为这与jvm规范的这一部分有关: 每个框架

  • 问题内容: 因此,此代码的输出为。现在,我想到了一个问题:作为 派生 类对象的 po 怎么能调用作为基类的 PrivateOverride 的私有方法? 问题答案: 因为您在类中定义了main方法。如果将main方法放在Derived类中,它将无法编译,因为在该类中不可见。 class中的po.f()调用不是多态的,因为in 类为,所以in class中的值不会被覆盖。

  • 问题内容: 我有一些用Java定义的类,类似于下面的代码。我正在尝试通过派生的Java类进行访问,这在Java中是允许的,但在kotlin中是不允许的。 有没有一种方法可以通过派生类访问字段? 问题答案: 在Kotlin中,嵌套类型和伴随对象不会自动继承。 此行为并非特定于Java,您可以仅在Kotlin中重现相同的行为: 因此,您必须显式地使用基类对嵌套类进行限定。 为了避免Java中与通过派生