派生类的静态初始化程序未被调用

12
以下的Java代码没有调用类B的静态初始化器。为什么?
代码:
class A 
{
    static 
    {
        System.out.println("A static init");
    }

    public static void f() 
    {
        System.out.println("f() called");
    }
}

class B extends A 
{
    static 
    {
        System.out.println("B static init");
    }
}

public class App
{
    public static void main( String[] args)
    {
        B.f(); //invokestatic  #16                 // Method com/db/test/B.f:()V
    }
}

程序输出:

A static init
f() called

在JDK 1.8.0_25上进行了测试。


1
可能是 https://dev59.com/V2Yr5IYBdhLWcg3wssKP 的重复。 - h7r
2个回答

14

“静态构造函数”并不存在。它是静态初始化块,只有在类初始化时才会执行。由于您正在调用类A的静态方法(即使是通过类B引用它),因此无需初始化类B。调用B.f();与调用A.f();相同。

如果您创建类B的实例或访问类B的静态成员/方法,则会执行类B的静态初始化块。

以下是触发类初始化的唯一条件(JLS 12.4.1):

A class or interface type T will be initialized immediately before the first occurrence of any one of the following:

 T is a class and an instance of T is created.

 T is a class and a static method declared by T is invoked.

 A static field declared by T is assigned.

 A static field declared by T is used and the field is not a constant variable 4.12.4).

 T is a top level class 7.6), and an assert statement 14.10) lexically nested within T 8.1.3) is executed.

附加:如果类B覆盖了f(),会发生什么? - Niels
@Niels,对于static方法不存在覆盖的情况。如果在类B中存在一个static方法f(),那么它将被调用。 - Konstantin Yovkov
@Niels 你不能覆盖一个静态方法。如果B定义了自己的静态f()方法,那么类B将被加载并执行其静态初始化块,并且该f()方法将被执行。在这种情况下,A也会被加载,因为它是B的超类。 - Eran
能否调用这个静态初始化程序? - Thomas
@Thomas 静态初始化程序会自动调用。您无法显式地调用它。 - Eran
这个怎么样:Class.forName(MyClass.class.getName())?这不是确保初始化程序被调用的适当方式吗? - Thomas

5

由于只有类A定义了方法f(),所以类B加载但没有初始化

您可以使用java -verbose:class MyClassName来检查这个问题。

在jdk6 / jdk 8机器上,将打印以下内容。

[Loaded App from file:/C:/XXXX/]
[Loaded A from file:/C:/XXXXXX]
[Loaded B from file://C:/XXXXXXX]
A static init
f() called

B 将被延迟初始化但是会贪婪地加载(因为正在引用它)。

将您的代码更改为 A.f()。然后您将看到B未被加载。

[Loaded App from file:/C:/XXXX/]
[Loaded A from file:/C:/XXXXX]
A static init
f() called

注意:类的加载和初始化是两个不同的概念。有关详细信息,请查看Class.forName()文档。

你知道B是否有保证被加载,还是可能与平台相关? - Paul Boddington
1
@pbabcdefp - 这将取决于虚拟机的实现。一些实现将贪婪地加载 B,而其他实现可能会直接解析对 A 的调用,因此不会加载 B - TheLostMind

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接