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



我认为在调用Derived.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) 和断言语句 (§14.10) 在 T (§8.1.3) 中执行词法嵌套。

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

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

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

JLS 仅针对静态字段,而不是静态方法,从此规则中排除

! 初始化 Base 和 Derived 似乎是有意义的 - 如果 f()在其主体中使用任何静态字段,那么 Derived.f() 将使用 Derived 的静态字段(如果有的话),并且可能使用从 Base 继承的静态字段(如果 Derived 没有) - 这对于初始化这两个类是有意义的。

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

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...
}
}

也许混淆在于成员变量(包括静态成员变量)在 Java 中的工作方式。 与方法不同,派生类中的成员变量不继承其超类对应项;他们只是影子。 因此,如果你在Base中有一个变量foo,在Derived中有一个变量foo,那么如果你在Base中打印该变量,你将得到Base的版本,如果你从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中的变量。

最新更新