英文:
Why an instance constant has a value when the object has all fields set to default?
问题
以下是您要求的代码翻译部分:
public class Main {
public static void main(String[] args) {
new B();
}
}
class A {
A() {
System.out.println("A 构造函数之前");
action();
System.out.println("A 构造函数之后");
}
protected void action() {
System.out.println("从未被调用");
}
}
class B extends A {
private final int finalField = 42;
private int field = 99;
B() {
System.out.println("B 构造函数");
action();
}
public void action() {
System.out.println("B 动作,finalField=" + finalField + ",field=" + field);
}
}
至于您提到的代码中的一行:
B action, finalField=42, field=0
变量 "finalField" 在对象 B 创建之初已经初始化,但是变量 "field" 在父类构造函数中调用 "action" 方法时仍然具有默认值。
英文:
Here is the code:
public class Main {
public static void main(String[] args) {
new B();
}
}
class A {
A() {
System.out.println("A constructor before");
action();
System.out.println("A constructor after");
}
protected void action() {
System.out.println("Never called");
}
}
class B extends A {
private final int finalField = 42;
private int field = 99;
B() {
System.out.println("B constructor");
action();
}
public void action() {
System.out.println("B action, finalField=" + finalField + ", field=" + field);
}
}
And the result is:
A constructor before
B action, finalField=42, field=0
A constructor after
B constructor
B action, finalField=42, field=99
I confused by this line :
B action, finalField=42, field=0
Object B is not completely initialized and when we call method "action" from super class constructor - variable "field" has a default value, but the final variable "finalField" already has value 42.
When was the "finalField" initialized?
答案1
得分: 5
当使用常量表达式(15.29)初始化最终字段时,它被称为常量变量(4.12.4):
private final int finalField = 42;
这意味着字符串
"B action, finalField=" + finalField + ", field="
本身就是一个常量表达式,其值在编译时确定。如果您检查已编译的类文件,您实际上会在常量池部分找到字符串 B action, finalField=42, field=
。
通常情况下,当使用常量变量字段时,它必须在编译时被其值替换。在运行时不允许(13.1)引用字段:
> 3. 对于常量变量(§4.12.4)的字段的引用必须在编译时解析为常量变量初始化器所表示的值 V。
>
> 如果这样的字段是非静态的,则代码中不能存在对字段的引用,除了在包含字段的类中(它将是类,而不是接口,因为接口只有静态字段)。该类应该有代码,在实例创建(§12.5)期间将字段的值设置为 V。
字段初始化程序仍然在您预期时运行:在 A
构造函数返回之后,在 B
构造函数开始之前。观察未初始化的值很棘手,因为编译器会内联变量的使用,但您可以通过反射访问字段的值:
public void action() {
try {
System.out.println("B action, finalField="
+ getClass().getDeclaredField("finalField").get(this)
+ ", field=" + field);
} catch (IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
}
输出:
A构造函数之前
B action, finalField=0, field=0
A构造函数之后
B构造函数
B action, finalField=42, field=99
英文:
When a final field is initialized with a constant expression (15.29), it's called a constant variable (4.12.4):
private final int finalField = 42;
This means that the string
"B action, finalField=" + finalField + ", field="
is a constant expression itself and its value is determined at compile time. If you inspect the compiled class file you will in fact find the string B action, finalField=42, field=
in the constant pool section.
In general, when a field that is a constant variable is used, it has to be replaced by its value at compile time. It is not allowed (13.1) to reference the field at run time:
> 3. A reference to a field that is a constant variable (§4.12.4) must be resolved at compile time to the value V denoted by the constant variable's initializer.
>
> If such a field is non-static, then no reference to the field should be present in the code in a binary file, except in the class containing the field. (It will be a class rather than an interface, since an interface has only static fields.) The class should have code to set the field's value to V during instance creation (§12.5).
The field initializer still runs when you would expect: after A
constructor has returned and before B
constructor starts. Observing the uninitialized value is tricky because the compiler inlines the uses of the variable, but you can access the value of the field through reflection:
public void action() {
try {
System.out.println("B action, finalField="
+ getClass().getDeclaredField("finalField").get(this)
+ ", field=" + field);
} catch (IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
}
Output:
A constructor before
B action, finalField=0, field=0
A constructor after
B constructor
B action, finalField=42, field=99
答案2
得分: 1
在Java中有两种类型的流程。
1. 静态流程
2. 实例流程
**静态流程**
每当我们执行父子类时,将自动执行以下事件序列。
- 从父类到子类识别静态成员
- 从父类到子类执行静态变量赋值和静态块
- 只执行子类的main方法
**实例流程**
- 每当我们执行Java类时,静态控制流将首先执行。
- 从父类到子类识别实例成员
- 只在父类中执行实例变量赋值和实例块
- 执行父类构造函数
- 在子类中执行实例变量赋值和实例块
- 执行子类构造函数
基于这些规则,我将尝试解释JVM如何执行您的代码。
1. 识别静态成员:-> public static void main(String[] args);
2. 执行main方法
3. 首先识别类A中的实例成员,然后是类B中的实例成员
> //A类成员
>
> protected void action();
>
> //B类成员
>
> private final int finalField = 42;(因为这是final,编译器将直接赋值)
>
> private int field = 0;(为这个成员添加默认值)
>
> public void action();
4. 只在父类中执行实例变量赋值和实例块
// 没有成员需要赋值
5. 执行父类构造函数
B构造函数中的第一行是super(),因此控制权将转到A类构造函数。
**System.out.println("A constructor before")**
**action();** //这有点棘手,因为这两个方法编译器已经识别出来了,
JVM有两个选项可供选择,调用A类中的action()还是B类中的action(),
但由于在B类中覆盖了action()方法,Java会使用运行时多态规则来执行B类中的action()方法。
// 此时field的值为0
**System.out.println("A constructor after")**
6. 只在子类中执行实例变量赋值和实例块
> private int field = 99
9. 执行子类构造函数
英文:
In java there are two types of flows .
- Static flow
- Instance flow
Static Flow
Whenever we are executing Parent Child Class the following sequence of events will
be performed automatically.
-
Identification of static Members from Parent to Child
-
Execution of Static Variable Assignments and static blocks from parent to child
-
Execution of Only Child class main
Instance Flow
-
Whenever we are executing a Java class static control flow will be executed first.
-
Identification of Instance Members from Parent to Child
-
Execution of Instance variable assignments and instance block only in parent
class -
Execution of Parent Class Constructor
-
Execution of Instance variable assignments and instance blocks in child class
-
Execution of child class constructor
Based on these rules i will try to explain how jvm executes your code .
-
Identifies static member:-> public static void main(String[] args) ;
-
Execute main method
-
Identify instance members in A class first and then B class
> //A class members
>
>
> protected void action();
>
>
> //B class members
>
>
> private final int finalField =42; (since this is final complier will direclty assing it )
>
>
>
>
> private int field =0;(add default values for this member)
>
>
>
>
> public void action();
- Execution of Instance variable assignments and instance blocks only in parent
// no member to assign
-
Execute Parent Construcor
First line in B constructor is super() so the control will go in A class constructor.
System.out.println("A constructor before")
action(); //This is little bit tricky ,since the both methods are already
identified
by the compiler jvm has two options to choose ,action() in A class or
action() B ,but
.since action method is overriden in B class and java for overriden methods
uses
Runtime Polimorifizem rule ,it will execute action() method in B classs// at this time value of field is 0
System.out.println("A constructor after")
-
Execution of Instance variable assignments and instance blocks only in child
class
> private int field =99
- Execute child class constructor
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论