为什么实例字段的值变为空? [英] Why is the value of the instance field coming null?
问题描述
我有这段简单的代码.
abstract class X {
X() {
read();
}
private void read() {
Object obj = new Object();
readValue(obj);
}
protected abstract void readValue(Object obj);
}
class Y extends X {
Object obj = null;
Y() {
super();
}
@Override
protected void readValue(Object obj) {
this.obj = obj;
}
void printer() {
System.out.println("Object = " + obj);
}
}
class Runner {
public static void main(String[] args) {
Y y = new Y();
y.printer();
}
}
当我运行上面的代码时,对象被打印为空.(我得到Object = null")
令人惊讶的是,当我删除空声明时,在 Y 类中
When I run the above code, the object gets printed as null. (I get "Object = null")
Surprisingly, in class Y when I remove null declaration
Object obj;
打印对象的实际值.
类似于 ("Object = java.lang.Object@3cd1a2f1")
为什么会观察到这种行为?这"指的是什么?任何对象只要声明就被初始化为null,那为什么会出现这样的异常行为?
The actual value of the object is printed.
Something like ("Object = java.lang.Object@3cd1a2f1")
Why is such a behavior observed? What is 'this' pointing to? Any object is initialized by null if we just declare it, then why such an aberrant behavior?
推荐答案
这说明了从超类构造函数调用子类中继承方法的危险.主要的危险是子类中变量的初始化程序在超类构造函数完成之后运行.
This illustrates the dangers of calling an inherited method in a subclass from a superclass constructor. The main danger is that initializers for variables in a subclass run after the superclass constructor completes.
这是发生了什么.
- 创建了一个
y
对象. - 超类构造函数
X()
被调用,它调用read()
. read
方法创建一个新的Object
并将其传递给readValue
,后者在Y
中实现.Y
中的readValue
方法将obj
设置为新对象.- 超类构造函数
X()
完成,初始化程序现在在Y
中运行,将obj
设置为<代码>空代码>. printer
方法打印Object = null"
.
- An object of
y
is created. - The superclass constructor
X()
is called, which callsread()
. - The
read
method creates a newObject
and passes it toreadValue
, which is implemented inY
. - The
readValue
method inY
setsobj
to the new object. - The superclass constructor
X()
completes, and initializers run now inY
, settingobj
tonull
. - The
printer
method prints"Object = null"
.
如果删除 Y
中 obj
的声明,则没有初始化程序可以运行,并且 obj
变量保留其值.
If you remove the declaration of obj
in Y
, then there is no initializer to run, and the obj
variable retains its value.
JLS,第 12.5 节,声明:
[A]新对象中的所有实例变量,包括在超类中声明的实例变量,都被初始化为其默认值(第 4.12.5 节).
[A]ll the instance variables in the new object, including those declared in superclasses, are initialized to their default values (§4.12.5).
就在对新创建的对象的引用作为结果返回之前,处理指示的构造函数以使用以下过程初始化新对象:
Just before a reference to the newly created object is returned as the result, the indicated constructor is processed to initialize the new object using the following procedure:
将构造函数的参数分配给此构造函数调用新创建的参数变量.
Assign the arguments for the constructor to newly created parameter variables for this constructor invocation.
如果此构造函数以同一类中另一个构造函数的显式构造函数调用(第 8.8.7.1 节)开头(使用 this),则评估参数并使用相同的五个步骤递归处理该构造函数调用.如果那个构造函数调用突然完成,那么这个过程也会因为同样的原因突然完成;否则,继续第 5 步.
If this constructor begins with an explicit constructor invocation (§8.8.7.1) of another constructor in the same class (using this), then evaluate the arguments and process that constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason; otherwise, continue with step 5.
此构造函数不以对同一类中另一个构造函数的显式构造函数调用开始(使用 this).如果此构造函数用于 Object 以外的类,则此构造函数将以显式或隐式调用超类构造函数(使用 super)开始.使用这五个相同的步骤,递归地评估超类构造函数调用的参数和过程.如果该构造函数调用突然完成,则该过程出于同样的原因突然完成.否则,继续第 4 步.
This constructor does not begin with an explicit constructor invocation of another constructor in the same class (using this). If this constructor is for a class other than Object, then this constructor will begin with an explicit or implicit invocation of a superclass constructor (using super). Evaluate the arguments and process that superclass constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, continue with step 4.
为这个类执行实例初始化器和实例变量初始化器,将实例变量初始化器的值按出现的从左到右的顺序赋值给对应的实例变量以文本形式在类的源代码中.如果执行这些初始化程序中的任何一个导致异常,则不会处理其他初始化程序,并且此过程会以相同的异常突然完成.否则,继续第 5 步.
Execute the instance initializers and instance variable initializers for this class, assigning the values of instance variable initializers to the corresponding instance variables, in the left-to-right order in which they appear textually in the source code for the class. If execution of any of these initializers results in an exception, then no further initializers are processed and this procedure completes abruptly with that same exception. Otherwise, continue with step 5.
执行此构造函数的其余部分.如果该执行突然完成,那么这个过程也会出于同样的原因突然完成.否则,此过程正常完成.
Execute the rest of the body of this constructor. If that execution completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, this procedure completes normally.
(强调我的)
和
与 C++ 不同,Java 编程语言不会在创建新类实例期间指定更改的方法调度规则.如果调用的方法在正在初始化的对象的子类中被覆盖,那么即使在新对象完全初始化之前,也会使用这些覆盖方法.
Unlike C++, the Java programming language does not specify altered rules for method dispatch during the creation of a new class instance. If methods are invoked that are overridden in subclasses in the object being initialized, then these overriding methods are used, even before the new object is completely initialized.
这篇关于为什么实例字段的值变为空?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!