在 Java 中,对 super() 参数列表中的静态方法的调用是有效的。为什么?

2022-09-04 08:05:09

让我们看一下 Java 中的以下代码片段。

package trickyjava;

class A
{
    public A(String s)
    {
        System.out.println(s);
    }
}

final class B extends A
{
    public B()
    {
        super(method());      // Calling the following method first.      
    }

    private static String method()
    {
        return "method invoked";
    }
}

final public class Main
{
    public static void main(String[] args)
    {
        B b = new B();
    }
}

按照惯例,Java 中的 super() 构造函数必须是相关构造函数体中的第一个语句。在上面的代码中,我们在 super() 构造函数参数列表本身中调用静态方法 super(method());


这意味着在构造函数 B() 中对 super 的调用中,在调用 super 之前调用了一个方法!编译器应该禁止这样做,但它工作得很好。这在某种程度上等效于以下语句。

String s = method();
super(s);

但是,导致编译时错误是非法的,该错误指示“对 super 的调用必须是构造函数中的第一个语句”。为什么?以及为什么它是等效的 super(method()); 是有效的,编译器不再抱怨了?


答案 1

这里的关键是修饰符。静态方法绑定到,实例方法(普通方法)绑定到对象(类实例)。构造函数从类初始化对象,因此该类必须已完全加载。因此,将静态方法作为构造函数的一部分调用是没有问题的。static

加载类和创建对象的事件顺序如下所示:

  1. 荷载类
  2. 初始化静态变量
  3. 创建对象
  4. 初始化对象< -- 使用构造函数
  5. 对象现在已准备就绪,可供使用

(简体*)

调用对象构造函数时,静态方法和变量已可用。

将类及其成员视为该类的对象的蓝图。仅当蓝图已经存在时,才能创建对象。static

构造函数也称为初始值设定项。如果从构造函数引发异常并打印堆栈跟踪,则会注意到它在堆栈帧中被调用。只能在构造对象后调用实例方法。不能使用实例方法作为构造函数中调用的参数。<init>super(...)

如果创建同一类的多个对象,则步骤 1 和 2 仅发生一次。

(*为清楚起见,省略了静态初始值设定项和实例初始值设定项)


答案 2

是的,检查JVM规范(尽管不可否认是旧的):

在实例 init 方法中,在调用同一类中的另一个 init 方法或超类中的 init 方法之前,可能不会发生对“this”的引用(包括返回的隐式引用)。

据我所知,这确实是唯一真正的限制。


推荐