为什么实例字段的值变为空? [英] Why is the value of the instance field coming null?

查看:39
本文介绍了为什么实例字段的值变为空?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这段简单的代码.

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.

这是发生了什么.

  1. 创建了一个 y 对象.
  2. 超类构造函数X()被调用,它调用read().
  3. read 方法创建一个新的Object 并将其传递给readValue,后者在Y 中实现.
  4. Y 中的 readValue 方法将 obj 设置为新对象.
  5. 超类构造函数X()完成,初始化程序现在Y中运行,将obj设置为<代码>空.
  6. printer 方法打印Object = null".
  1. An object of y is created.
  2. The superclass constructor X() is called, which calls read().
  3. The read method creates a new Object and passes it to readValue, which is implemented in Y.
  4. The readValue method in Y sets obj to the new object.
  5. The superclass constructor X() completes, and initializers run now in Y, setting obj to null.
  6. The printer method prints "Object = null".

如果删除 Yobj 的声明,则没有初始化程序可以运行,并且 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:

  1. 将构造函数的参数分配给此构造函数调用新创建的参数变量.

  1. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆