即使在 Java 中初始化子 class 后,静态变量的值也没有改变
Value of static variable not changed even after initializing the child class in Java
当我使用Checks.y
(Checks
是一个子类)调用静态变量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
指的是par
class的一个字段。根据 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 未加载,因此分配不会发生!
当我使用Checks.y
(Checks
是一个子类)调用静态变量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
指的是par
class的一个字段。根据 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 未加载,因此分配不会发生!