【问题标题】:Java Abstract class Unusual behaviorJava 抽象类异常行为
【发布时间】:2015-08-21 12:21:04
【问题描述】:

抽象类:

   public abstract class ParentClass {
    private static ParentClass mpParentClass;

    public ParentClass() {
        mpParentClass = this;
    }

    public abstract void method1();

    public static ParentClass getInstance() {
        return mpParentClass;
    }
}

儿童班:

public class ChildClass extends ParentClass{
    @Override
    public void method1() {
        System.out.print("ChildClass class method");
    }
}

测试类:

public class TestClass {
    public static void main(String[] args) {
         ChildClass cl = new ChildClass();
        ParentClass.getInstance().method1();
    }
}

在这里,我创建了一个抽象类和一个扩展父抽象类的子类。

父抽象类持有对其自身实例的引用,并通过静态方法返回该实例。

在Test类中,如果我不创建ChildClass的对象,java会抛出NullPointerException。

但是在创建了 ChildClass 的对象之后,然后查询 ParentClass 的实例并调用抽象方法,它调用了 ChildClass 实现的方法。

我无法理解这种行为。请任何人解释。

【问题讨论】:

    标签: java oop inheritance abstract-class abstract


    【解决方案1】:

    第一次实例化 ChildClass 时,您使用 parentClass 的默认构造函数,它用 ChildClass 类型实例化私有字段。如果您不这样做,则私有字段 mpParentClass 不会被实例化。所以你有一个 NullPointerException

    【讨论】:

      【解决方案2】:

      ParentClass.getInstance() 是一个静态方法,因此它不需要您的类的实例来运行。

      通过调用此方法,您将返回静态成员mpParentClass。 但默认情况下,此成员包含 null 引用。

      所以不做任何事情,这确实会导致NullPointerException,因为你没有调用ParentClass的构造函数。


      在您的示例中,您首先创建了ChildClass 的实例。

      这将调用该类的默认构造函数。这个默认构造函数具有调用超类的默认构造函数的标准行为(通过调用super())。

      因此,通过实例化ChildClass,您可以调用ParentClass 的构造函数,它将mpParentClass 数据成员设置为此。这里指的是您正在创建的ChildClass 的实例。

      所以在构造后mpParentClass 将包含ChildClass 的新创建实例。

      【讨论】:

        【解决方案3】:

        这就是正在发生的事情。

        当您为ChildClass 调用构造函数时,暗示该方法中的第一个实际 调用是对超类构造函数的调用。如果您有一个需要/允许备用参数的超类构造函数,您可以手动调用它。但它正在发生在你身上。

        当调用超类构造函数时,一个static 引用被分配给那个新实例,一个ChildClass 实例。 (因为在这种情况下,this 就是这样。)

        如果你打电话:

        new ChildClass();
        new ParentClass() {
            public void method1() {
                System.out.println("Anonymous class!");
            }
        };
        ParentClass.getInstance().method1();
        

        您会看到"Anonymous class!",因为每次创建ParentClass 实现的任何实例时都会重新分配一个静态 引用。

        关于您的NullPointerException - 将mpParentClass 分配给值的唯一位置是在ParentClass 的构造函数中。如果您从不创建ParentClass 的实例,则永远不会调用此代码,mpParentClass 将保留其原始值,即null。尝试调用 null 引用上的方法或访问属性会产生 NullPointerException

        要问的问题是:如果您从不实例化您的任何实现(通过“调用”它们的构造函数),您期望mpParentClass 变量设置为什么,如果不是@ 987654337@?

        【讨论】:

          【解决方案4】:

          父类是抽象的,所以你不能直接使用构造函数。静态变量实例默认为 null,这会导致 NullPointerException。设置此变量的唯一方法是调用构造函数,该构造函数仅在调用子类的构造函数时调用。

          【讨论】:

            【解决方案5】:

            这是因为您仅在 ParentClass 构造函数中分配给静态字段 mpParentClass。因此,在您创建 ChildClass 的实例之前,mpParentClass 为空。在您的示例中,当您创建派生类 ChildClass 的实例时,将隐式调用基类 ParentClass 构造函数。

            我可以建议您改用singleton pattern 吗?

            public class Singleton {
              // Private constructor prevents instantiation from other classes
              private Singleton() {}
            
             /**
               * SingletonHolder is loaded on the first execution of Singleton.getInstance() 
               * or the first access to SingletonHolder.INSTANCE, not before.
               */
              private static class SingletonHolder { 
                private static final Singleton INSTANCE = new Singleton();
              }
            
              public static Singleton getInstance() {
                return SingletonHolder.INSTANCE;
              }
            }
            

            【讨论】:

              【解决方案6】:

              您正在创建一个ChildClass,然后通过不必要的诡计调用它的method1()

              您确实意识到this 将是ChildClass 构造函数中的ChildClass 实例,对吧?必须如此,因为父类是抽象的,因此永远不可能有一个 this 实例来代表它。

              【讨论】:

              • 我认为他没有,这就是他问的原因。
              • @CraigOtis 这不是很有帮助。我在第二段给出了答案。
              • 很公平。已删除反对票。你的回答("unnecessary trickery""you do realize...")只是有点居高临下,尤其是从初学者的角度来看。
              • 我并没有居高临下的意思,我的意思是它完全等同于直接在创建的实例上调用方法,而不是从父类中检索引用然后调用它。跨度>
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-05-19
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多