不能实例化抽象类是真的吗?
是的。
JVM是否在内部创建抽象类的对象?
不,但这是一种常见的误解(这并不是一种不合理的方式;像 JavaScript 这样的原型语言就是这样做的)。
JVM 创建一个对象,它属于您创建的类(在您的情况下是ConcreteClass
)。这个对象的某些方面是从它的超类(MyAbstractClass
)和它的子类(ConcreteClass
)中得到的,但只有一个对象。
对象是其所有部分的集合,包括似乎具有相同名称的部分,例如被子类覆盖的超类的方法。事实上,这些方法具有不同的完全限定名称并且不会相互冲突,这就是为什么可以调用超类版本的重写方法的原因。
所以如果它只是一个对象,为什么你会看到对MyAbstractClass
's 的构造函数的调用?在我们回答这个问题之前,我需要提一下 Java 编译器正在做的一些你在源代码中看不到的事情:
它正在为ConcreteClass
.
在那个构造函数中,它正在调用MyAbstractClass
构造函数。
只是为了彻底:在MyAbstractClass
构造函数中,它添加了对超类 ( Object
) 构造函数的调用,因为在构造函数中没有super(...)
编写调用MyAbstractClass
。
以下是 Java 编译器为您添加的代码的样子:
public abstract class MyAbstractClass {
public MyAbstractClass() {
super(); // <== The Java compiler adds this call to Object's constructor (#3 in the list above)
System.out.println("abstract default constructor");
}
}
public class ConcreteClass extends MyAbstractClass{
ConcreteClass() { // <== The Java compiler adds this default constuctor (#1 in the list above)
super(); // <== Which calls the superclass's (MyAbstractClass's) constructor (#2 in the list above)
}
public static void main(String[] args) {
new ConcreteClass();
}
}
好的,顺便说一句,让我们谈谈 TheLostMind在评论中非常有用地提到的一点:构造函数不创建对象,它们初始化它们。JVM 创建对象,然后根据需要针对该对象运行尽可能多的构造函数(它们实际上应该称为初始化程序),以使每个超类有机会初始化其对象部分。
因此,在该代码中,会发生什么(您可以在调试器中逐步完成以完全理解它)是:
JVM 创建一个对象
构造ConcreteClass
函数被调用
构造函数做的第一件事是调用它的超类的构造函数,在这个例子中MyAbstractClass
是构造函数。(请注意,这是绝对要求:Java 编译器不允许您在调用超类构造函数之前在构造函数本身中包含任何逻辑。)
构造函数做的第一件事就是调用它的超类的构造函数(Object
's)
当Object
构造函数返回时,构造函数的其余部分MyAbstractClass
运行
当MyAbtractClass
构造函数返回时,构造函数的其余部分ConcreteClass
运行
对象作为new ConcreteClass()
表达式的结果返回。
请注意,如果存在带有初始化程序的实例字段,则上述内容会变得更加复杂。有关完整详细信息,请参阅 JLS 和 JVM 规范。