当前位置: 首页 > 面试题库 >

当在子类上调用其父类中声明的静态方法时,为什么不调用子类的静态初始化器?

仲涵亮
2023-03-14
问题内容

给定以下类别:

public abstract class Super {
    protected static Object staticVar;

    protected static void staticMethod() {
        System.out.println( staticVar );
    }
}

public class Sub extends Super {
    static {
        staticVar = new Object();
    }

    // Declaring a method with the same signature here, 
    // thus hiding Super.staticMethod(), avoids staticVar being null
    /*
    public static void staticMethod() {
        Super.staticMethod();
    }
    */
}

public class UserClass {
    public static void main( String[] args ) {
        new UserClass().method();
    }

    void method() {
        Sub.staticMethod(); // prints "null"
    }
}

我的目标不是像“因为在JLS中这样指定”这样的答案。我知道是的,因为JLS是12.4.1发生初始化时,其读取内容仅为:

类或接口类型T将在以下任何一种首次出现之前立即初始化:

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

我对没有这样的句子是否有充分的理由感兴趣:

  • T是S的子类,并且在T上调用S声明的静态方法。


问题答案:

我认为这与jvm规范的这一部分有关:

每个框架(第2.6节)都包含对当前方法类型的运行时常量池(第2.5.5节)的引用,以支持方法代码的动态链接。方法的类文件代码是指要调用的方法和要通过符号引用访问的变量。动态链接将这些符号方法引用转换为具体的方法引用,根据需要加载类以解析尚未定义的符号,并将变量访问转换为与这些变量的运行时位置关联的存储结构中的适当偏移量。

方法和变量的这种较晚的绑定使方法使用的其他类中的更改不太可能破坏此代码

在jvm规范的第5章中,他们还提到:由于以下原因,可能会初始化类或接口C:

执行引用C的Java虚拟机指令new,getstatic,putstatic或invokestatic的任何一条(§new,§getstatic,§putstatic,§invokestatic)。这些指令通过字段引用或方法引用
直接或间接 引用类或接口。

在执行getstatic,putstatic或invokestatic指令后,如果尚未 解析已声明字段或方法的类或接口, 则将对其进行初始化。

在我看来,文档的第一部分似乎指出,任何符号引用都可以简单地解析和调用,而不考虑其来源。关于方法解析的该文档对此有以下说法:

[M] ethod解析尝试在C及其超类中定位引用的方法:

如果C完全使用方法引用指定的名称声明了一个方法,并且声明是签名多态方法(第2.9节),则方法查找成功。描述符中提到的所有类名都将被解析(第5.4.3.1节)。

解析的方法是签名多态方法声明。C不必使用方法参考指定的描述符来声明方法。

否则,如果C使用方法参考指定的名称和描述符声明方法,则方法查找成功。

否则,如果C具有超类,则对C的直接超类递归调用方法解析的步骤2。

因此,从子类调用它的事实似乎被忽略了。为什么要这样呢?在您提供的文档中,他们说:

目的是类或接口类型具有一组初始化器,这些初始化器将其置于一致状态,并且该状态是其他类观察到的第一个状态。

在您的示例中,当Sub静态初始化时,您更改了Super的状态。如果在调用Sub.staticMethod时发生初始化,则jvm认为相同方法的行为会有所不同。这可能是他们在谈论要避免的不一致之处。

另外,这是一些执行staticMethod的反编译类文件代码,显示了invokestatic的使用:

Constant pool:
    ...
    #2 = Methodref          #18.#19        // Sub.staticMethod:()V

...

Code:
  stack=0, locals=1, args_size=1
     0: invokestatic  #2                  // Method Sub.staticMethod:()V
     3: return


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

  • 问题内容: 说,我有一个带有SomeType的具有静态方法的Class对象的引用。有没有一种方法可以调用该方法而不先实例化SomeType?最好不要转义强类型。 编辑:好的,我搞砸了。 在这种情况下,someMethod()始终不能是静态的。 问题答案: 根据定义,静态方法是在类上调用的,而不是在该类的实例上调用的。 因此,如果您使用: 您没有实例化任何东西(不考虑由JVM处理并且超出了您的范围的

  • 据我所知,通常应该使用类的引用来调用静态方法,或者如果它位于静态方法或静态块中,则可以不引用直接调用它。 但当从子类静态块调用静态方法时,这是否适用? 为什么它允许这样的事情,因为静态方法不是继承的,所以应该只允许使用父类名对吗? 为什么我的子类静态块可以在没有引用或类名的情况下调用父类静态方法?

  • 这是我的代码: 还有我的测试,我单独运行。 当我运行test foo时,我将看到: 但是当我运行测试栏时,我看到的是: 引用本页内容。。 类对象由Java虚拟机在加载类时自动构造,并通过调用类加载器中的defineClass方法来构造。 所以我的理解是,在测试条中,愚蠢的类被加载,否则我会看到一个空的,我猜?所以类对象被创建,因为类本身被加载。。 现在引用这一页 静态初始化块在JVM(类加载器-具

  • 问题内容: 因此,问题或多或少是我写的。我知道可能还不清楚,所以我举一个例子。 我有Tree类,其中有Node类,并且Tree的空构造函数被编写为: Eclipse给我一个错误:空构造函数中的“ new RBTree()”没有“由于某些中间构造函数调用而导致RBTree类型的封闭实例不可用”。但是,如果将RBNode更改为静态类,则没有问题。 那么,为什么在类为静态的时候它可以工作。 顺便说一句,

  • 想知道是否有一种在Java中实现这一点的方法。 想象一下,我想创建一个父类(可能是抽象的)并创建多个不同的子类。我希望所有类都有一个特定的字段,但我需要该字段在子上下文中是静态的。有没有办法在父类中定义它? 例如,假设我对名为Foo的字段有getter和setter。在父类中,静态字段初始化为字符串“foo”。我有两个儿童班AbsChildOne和AbsChildOne。我希望能够做到的是: 返回