抽象类:
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();
}
}
这里我创建了一个抽象类和一个扩展父抽象类的Child类。
父抽象类包含对其自己的实例的引用,并通过静态方法返回实例。
在Test类中,如果我没有创建ChildClass的对象,java会抛出NullPointerException。
但是在创建了ChildClass的对象,然后查询ParentClass的实例并调用抽象方法之后,它调用了ChildClass实现的方法。
我无法理解这种行为。请有人解释一下。
答案 0 :(得分:3)
第一次实例化ChildClass时,使用parentClass的默认构造函数,该构造函数使用ChildClass类型实例化私有字段。如果不这样做,私有字段mpParentClass不会实例化。所以你有一个NullPointerException
答案 1 :(得分:3)
ParentClass.getInstance()
是一种静态方法,因此它不需要运行类的实例。
通过调用此方法,您将返回静态成员mpParentClass
。
但默认情况下,此成员包含null
引用。
因此,如果没有做任何事情,这确实会导致NullPointerException
,因为您没有调用ParentClass
的构造函数。
在您的示例中,您首先要创建ChildClass
。
这将调用该类的默认构造函数。这个默认构造函数具有调用超类的默认构造函数的标准行为(通过调用super()
)。
因此,通过实例化ChildClass
,您调用ParentClass
的构造函数,将mpParentClass
数据库设置为此。这里指的是您正在创建的ChildClass
的实例。
因此,构造mpParentClass
将包含新创建的ChildClass
实例。
答案 2 :(得分:0)
这里发生了什么。
当您调用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
变量为什么设置为,如果不是null
?
答案 3 :(得分:0)
父类是抽象的,因此您无法直接使用构造函数。默认情况下,静态变量实例为null,这会导致您的NullPointerException。设置此变量的唯一方法是调用构造函数,该构造函数仅在调用子类的构造函数时调用。
答案 4 :(得分:0)
这是因为您只分配到mpParentClass
构造函数中的静态字段ParentClass
。因此,在创建ChildClass
的实例之前,mpParentClass
为空。在您的示例中,在创建派生类ParentClass
的实例时,将隐式调用基类ChildClass
构造函数。
我建议你改用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;
}
}
答案 5 :(得分:-1)
您正在创建ChildClass
,然后通过不必要的诡计调用其method1()
。
您确实意识到this
将是ChildClass
构造函数中的ParentClass
实例,对吧?它必须是,因为父类是抽象的,因此永远不会有一个代表它的this
实例。