即使在 Java 中初始化子 class 后,静态变量的值也没有改变

Value of static variable not changed even after initializing the child class in Java

当我使用Checks.yChecks是一个子类)调用静态变量y时,静态块没有被执行并且y的值没有'得到更新。

class Par {
    static int y = 4;
}

class Checks extends Par {
    static {
        y = 5;
    }
}

public class Check {
    public static void main(String args[]) {
        System.out.println(Checks.y); // here printing 4
    }
}

由于静态在所有子类之间共享,因此应该更新该值。

背后的原因是什么?

字段 y 未由 class Checks 声明。

读取静态字段不会触发引用 class (Checks) 的初始化,除非 class 是声明该字段的字段(请参阅下面的 JLS 引用).在此示例中,即使通过 Checks 访问 y,也只会触发 Par 的初始化,因为 Par 是 class 声明的 y.

换句话说,class Checks 在某种意义上在运行时不被使用。

这可能是为什么通过子 class 访问 static 成员是错误的一个例子,这会导致编译时警告。


the specification中有一个简单的解释:

12.4.1. When Initialization Occurs

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.

  • 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.
    ...
    A reference to a static field (§8.3.1.1) causes initialization of only the class or interface that actually declares it, even though it might be referred to through the name of a subclass, a subinterface, or a class that implements an interface.

最后一条说明解释了为什么您的子 class 没有被初始化。

那是因为 checks class 中的 static 块没有被执行。尽管您提到 class checks JVM 根本不加载它。

您可以通过在静态块中添加另一个 System.out.println 来确认这一点。

class checks extends par {

    static {
        System.out.println("Test");
        y = 5;
    }
}

单词 Test 永远不会打印出来。


阅读 Java 语言规范的 12.4.1. When Initialization Occurs 部分以了解更多信息。

来自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.

由于 y 未在支票中声明,因此满足上述条件 none。

说明此行为的另一种方式:

class par {
    static int y = 4;
    static {
        System.out.println("static constructor of par");
    }
}

class checks extends par {
    static int x = 6;
    static {
        System.out.println("checks static constructor");
        y = 5;
    }
}

public class check{
    public static void main(String args[]){
        System.out.println(checks.y);
        System.out.println(checks.x);
        System.out.println(checks.y);
    }
}

输出

static constructor of par
4
checks static constructor
6
5

因此在调用满足第二条规则的 checks.x 之后,将调用静态构造函数。

正如其他人所提到的,静态块没有被执行,因为 class 没有被初始化为

请注意 class 约定名称以大写字母开头。

没有使用更清晰的示例来显示覆盖 class:

class Par {
    static int y = 4;
    public static void main(String args[]) {
        System.out.println(Checks.y);    // Here printing 4
        System.out.println(new Checks().y);    // Here printing 5
    }
}

class Checks extends Par {
   static {
        y = 5;
    }
}

这里:

System.out.println(checks.y); // Here printing 4

y指的是parclass的一个字段。根据 JLS(重点是我的),此字段访问导致加载 par class(父 class):

12.4. Initialization of Classes and Interfaces

....

12.4.1. When Initialization Occurs

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

但它不会加载 checks class 因为(重点是我的):

A reference to a static field (§8.3.1.1) causes initialization of only the class or interface that actually declares it, even though it might be referred to through the name of a subclass, a subinterface, or a class that implements an interface.

根据您的示例,class Check 的静态块永远不会被调用。在创建对象之前,静态块总是 运行。如果您添加支票 object = new checks(),在支票 class 中您应该会看到预期值。

这是强制初始化 Checks class 的一种变体。

class Par {
    static int y = 4;
}

class Checks extends Par {
    public static int x;
    static {
        y = 5;
    }
}

 class Check {
    public static void main(String args[]) {
        System.out.println(checks.y); // Prints 4
        System.out.println(checks.x); // Prints 0
        System.out.println(checks.y); // Prints 5
    }
}

到目前为止未提及的一个方面可能会让 Java 新程序员感到困惑:在某种程度上,您如何组织源代码并不重要!

你有两个 classes(应该是名称 Parent 和 Child 顺便说一句,以遵循 Java 命名约定)在一个文件中,或者一个例子。所以你可能假设:这些东西在运行时自动组合在一起。

但是在运行时,每个 class 有单独的 class 个文件。正如其他人所说:代码中没有任何内容引用 child class。因此 class 未加载,因此分配不会发生!