Java抽象类的异常行为

3

抽象类:

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

我创建了一个父类抽象类和一个继承父类的子类。

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

在测试类中,如果我不创建ChildClass的对象,Java会抛出NullPointerException。

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

我无法理解这种行为。请问有人能够解释一下吗?

6个回答

3

当您第一次实例化ChildClass时,使用的是parentClass的默认构造函数,该函数使用ChildClass类型实例化私有字段。如果您不这样做,私有字段mpParentClass将不会被实例化。因此,您将遇到NullPointerException。


3
ParentClass.getInstance()是一个静态方法,因此不需要实例化你的类即可运行。
调用此方法将返回静态成员mpParentClass。 但默认情况下,此成员包含一个null引用。
因此,如果您未调用ParentClass的构造函数,则不做任何处理确实会导致NullPointerException
在您的示例中,您首先创建了ChildClass的实例。
这将调用该类的默认构造函数。此默认构造函数具有调用超类的默认构造函数的标准行为(通过调用super())。
因此,通过实例化ChildClass,您调用了ParentClass的构造函数,该函数将mpParentClass数据成员设置为此。这里的“this”指的是您正在创建的ChildClass的实例。
因此,在构建后,mpParentClass将包含新创建的ChildClass实例。

0

这里是正在发生的事情。

当您调用ChildClass的构造函数时,隐含着该方法中第一个实际调用的是超类构造函数。如果您有一个需要/允许替代参数的超类构造函数,则可以手动调用它。但是它会自动为您完成。

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

如果您调用:

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

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

关于你的NullPointerException——唯一一个将mpParentClass赋值为一个值的地方是在ParentClass的构造函数中。如果你从未创建过ParentClass的实例,那么这段代码永远不会被调用,mpParentClass将保留其原始值,即null。尝试在null引用上调用方法或访问属性会产生NullPointerException

需要问的问题是:如果你从未实例化任何一个实现(通过'调用'它们的构造函数),那么如果不是null,你期望mpParentClass变量被设置为什么?


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

我可以建议您改用单例模式吗?

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

0

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


-1
你正在创建一个ChildClass,然后通过不必要的诡计调用它的method1()
你应该意识到,在ParentClass构造函数中,this将是一个ChildClass实例,对吧?这是必须的,因为父类是抽象的,因此永远不可能有一个代表它的this实例。

我认为他不知道,这就是他在问的原因。 - Craig Otis
@CraigOtis 这并不是很有帮助。我在第二段中给出了答案。 - Kayaman
好吧,没问题。已取消踩踏操作。你的回答(“不必要的花招”,“你意识到…”)听起来有点傲慢,特别是对于初学者来说。 - Craig Otis
我并不是有贬低的意思,我的意思是这样做就相当于直接在创建的实例上调用方法,而不是从父类中检索引用,然后再调用它。 - Kayaman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接