【问题标题】:How is my private member getting set to null?我的私人成员如何设置为空?
【发布时间】:2015-03-13 21:01:26
【问题描述】:

我从事 Java 专业编程已经十多年了。这是我曾经试图追踪的最奇怪的错误之一。我有一个私有成员,我对其进行了初始化,然后它自行更改为 null。

public class MyObject extends MyParent
{
    private SomeOtherClass member = null;

    public MyObject()
    {
        super();
    }

    public void callbackFromParentInit()
    {
        member = new SomeOtherClass();
        System.out.println("proof member initialized: " + member);
    }

    public SomeOtherClass getMember()
    {
        System.out.println("in getMember: " + member);
        return member;
    }
}

输出:

proof member initialized: SomeOtherClass@2a05ad6d
in getMember: null

如果你运行这段代码,显然它会正常工作。在我的实际代码中,这种精确模式中只有这三种情况(如果计算 println,则为 5 次)。

我是否在 JVM 中遇到了一些错误?除非我错了,父类不能干涉私有成员,无论我在我展示给你的代码行之间放了什么,我都不能在不使用标识符“member”的情况下更改 member 的值”。

【问题讨论】:

  • 你有完整的工作示例吗?
  • 什么是输出:MyObject foo = new MyObject(); foo.callbackFromParentInit(); foo.getMember();
  • 我无法发布工作代码。我可以告诉你,“成员”标识符在所示模式的代码中只使用了五次。不应该有一种方法可以将其更改为 null 而无需在此类内部的另一行中使用标识符。
  • 您在类实例化时将其设置为 null,并且仅在函数 callbackFromParentInit() 的范围内初始化。当你调用“getMember()”时,它返回类的空值,因为它从未在类的范围内实例化。
  • 无法保证“in getMember” println 来自与打印“proof member initialized”相同的实例。

标签: java null private-members


【解决方案1】:

这是因为初始化成员变量和调用构造函数的顺序。

您正在从超类MyParent 的构造函数中调用callbackFromParentInit()

当这个方法被调用时,它会设置member。但之后,执行了对象初始化的子类部分,并执行了member的初始化器,将member设置为null

参见,例如:

Java 语言规范的paragraph 12.5 中描述了调用构造函数和初始化字段的顺序。

【讨论】:

    【解决方案2】:

    在执行父构造函数后将null 分配给字段member

    【讨论】:

      【解决方案3】:

      解决办法是改变:

      private SomeOtherClass member = null;
      

      到:

      private SomeOtherClass member;
      

      【讨论】:

      • 这很有趣,我显然以前从未做过这种精确的初始化模式。这似乎是一个愚蠢的命令。
      • 好的,解决了眼前的问题,但真正的解决方案是不要从超类构造函数中调用可覆盖的方法。见this question
      【解决方案4】:

      永远,永远不要从超类的构造函数调用非 final 方法。

      这被认为是一种不好的做法,正是因为它会导致令人讨厌的、难以调试的错误,就像您正在遭受的错误一样。

      在 X 的构造函数中执行类 X 的初始化。不要依赖 java 的层次结构的初始化顺序。如果您无法初始化类属性,即因为它具有依赖项,请使用构建器或工厂模式。

      这里,子类将属性member 重置为null,这是由于超类和子类的构造函数以及初始化程序块执行顺序,如前所述,您不应该依赖其中。

      请参阅this related question,了解有关this 引用的构造函数、层次结构和隐式转义的概念。

      我只能考虑坚持一套(可能不完整的)规则/原则来避免这个问题和其他类似问题:

      • 仅在构造函数中调用 private 方法

      • 如果你喜欢肾上腺素并想从构造函数中调用protected 方法,请执行此操作,但将这些方法声明为final,这样它们就不会被子类覆盖

      • 切勿在构造函数中创建内部类,无论是匿名的、本地的、静态的还是非静态的

      • 在构造函数中,不要将this直接作为参数传递给任何东西

      • 避免上述规则的任何传递组合,即不要在构造函数中调用的 privateprotected final 方法中创建匿名内部类

      • 使用构造函数只构造一个类的实例,让它只初始化类的属性,无论是使用默认值还是使用提供的参数

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-04-20
        • 2011-06-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-10-26
        相关资源
        最近更新 更多