当前位置: 首页 > 面试题库 >

为什么实例字段的值变为空?

施学
2023-03-14
问题内容

我有这段简单的代码。

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();
    }
}

当我运行上面的代码时,该对象被打印为null。(我得到 “ Object = null”
令人惊讶的是,在类Y中,当我删除null声明时

Object obj;

打印对象的实际值。
类似于( “ Object = java.lang.Object@3cd1a2f1” )之
类的现象为何被观察到?“这个”指的是什么?如果仅声明对象,则将其初始化为null,那么为什么会有这种异常行为呢?


问题答案:

这说明了从超类构造函数的子类中调用继承方法的危险。主要危险在于,超类构造函数完成 ,子类中变量的初始化器就会运行。

这是发生了什么。

  1. 的对象y已创建。
  2. X()调用超类构造函数,该构造函数调用read()
  3. read方法创建一个新方法Object并将其传递给readValue,该方法在中实现Y
  4. 中的readValue方法Y设置obj为新对象。
  5. 超类构造函数X()完成,并且初始化器 现在 在中运行,并将Y设置objnull
  6. printer方法打印"Object = null"

如果删除objin 中的声明Y,则没有初始化程序可运行,并且obj变量保留其值。

在JLS,12.5节,状态:

将新对象中的实例变量(包括在超类中声明的实例变量)初始化为其默认值(第4.12.5节)。

在返回对新创建对象的引用作为结果之前,使用以下过程处理指示的构造函数以初始化新对象

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

2.
如果此构造函数以同一个类中的另一个构造函数的显式构造函数调用(第8.8.7节)开始(使用此方法),则使用相同的五个步骤评估参数并递归处理该构造函数调用。如果该构造函数调用突然完成,则该过程由于相同的原因而突然完成;否则,请继续执行步骤5。

3.
此构造函数并不以对同一个类中的另一个构造函数的显式构造函数调用(使用此函数)开头。如果此构造函数用于Object以外的其他类,则此构造函数将以显式或隐式调用超类构造函数(使用super)开始。使用这五个相同的步骤来递归评估超类构造函数调用的参数和过程。如果该构造函数调用突然完成,则出于相同原因,此过程也会突然完成。否则,请继续执行步骤4。

  1. 执行该类 的实例初始值设定项 和实例变量初始
    值设定项,并按从左到右的顺序将实例变量初始值设定项的值分配给相应的实例变量,这些变量在文本中显示在该类的源代码中。如果执行这些初始化程序中的任何一个导致异常,则不会再处理其他初始化程序,并且该过程会因相同的异常而突然完成。否则,请继续执行步骤5。

  2. 执行此构造函数的其余部分。如果该执行突然完成,则出于相同原因,此过程也会突然完成。否则,此过程将正常完成。

(强调我的)

与C
++不同,Java编程语言没有在创建新的类实例的过程中为方法分派指定更改的规则。如果调用的方法在要初始化的对象的子类中被覆盖,则即使在完全初始化新对象之前,也会使用这些覆盖方法。



 类似资料:
  • 我读到了这个关于不可变对象的问题,留下了一个关于不可变对象和final字段的问题: 为什么我们需要不可变类中的实例变量成为最终变量? 例如,考虑这个不可变类: 如果在上面的代码中没有设置方法,并且实例变量只在构造函数中设置,那么为什么需要将实例变量声明为final?

  • 问题内容: 我有一个具有字段()的Spring 类(),但是该字段是我尝试使用它时所用的。日志显示该bean和该bean都在创建,但是每当我尝试在服务bean上调用该方法时,都会得到一个a 。Spring为什么不自动接线该领域? 控制器类: 服务等级: 应该自动连接的服务bean,但不是: 当我尝试时,出现以下异常: 问题答案: 本文向大家介绍为什么我的Spring @Autowired字段为空?

  • 问题内容: 我读了这个问题不可变对象,并留下了关于不可变对象,并最终场一个问题: 为什么我们需要不可变类中的实例变量为最终变量? 例如,考虑以下不可变的类: 如果在上面的代码中没有set方法,而实例变量仅在构造函数中设置,为什么要求将实例变量声明为final? 问题答案: 有没有 要求 这样做的变量。但是,当您确实明确打算永远不更改变量时,通常这样做是一种好习惯,因为这不仅可以使变量避免错别字或其

  • 我有一些奇怪的行为,我正努力向自己解释。浮点字段,称为“文本缩放”,变为零。 如果某些代码正在更改值,则可以解释这一点。然而,我希望通过将其设置为“私有最终浮点”,能够导致构建失败,或者至少是运行时异常,那么无论改变值的是什么,都将失败。但是如果我这样做,代码就不会失败——它工作得很好! 有谁能帮我理解这里可能发生的事情吗?为什么这个浮动会变成零,除非我把它设为最终值?这里有我不熟悉的Java i

  • 问题内容: 为什么在类中声明的变量具有默认值,但是在方法内部声明的变量(称为“局部变量”)在Java中没有默认值? 例如 在上面的示例中,变量的默认值为0,但是变量给出了可能尚未初始化的错误。 问题答案: 所有成员变量都必须加载到堆中,因此在创建类的实例时必须使用默认值进行初始化。对于局部变量,它们不会被加载到堆中,直到在Java7之前被使用,它们才会存储在堆栈中,因此我们需要显式初始化它们。现在

  • 问题内容: 我正在努力从以下代码中获取正确的输出: 游乐场片段 打印时,结构字段为空。我敢肯定某个地方有一个愚蠢的错误,但是我仍然对Go还是陌生的,而且我已经在这里呆了几个小时。请帮忙。 问题答案: 这已经出现了很多次了。问题在于只能对导出的字段进行封送处理。 通过以大写(大写)字母开头来导出结构域。 在Go Playground上尝试一下。 请注意,JSON文本包含带有小写字母文本的字段名称,但