在这里,我正在创建我的类的实例
不,您不是在此处创建抽象类的实例。相反,您正在创建抽象类的匿名子类的实例。然后,在指向子类对象的抽象类引用上调用该方法。
此行为在 JLS - 第 15.9.1 节中明确列出: -
如果类实例创建表达式以类体结束,则要实例化的类是匿名类。然后:
- 如果 T 表示一个类,则声明由 T 命名的类的匿名直接子类。如果 T 表示的类是最终类,则为编译时错误。
- 如果 T 表示接口,则声明 Object 的匿名直接子类,该子类实现由 T 命名的接口。
- 在任一情况下,子类的主体都是类实例创建表达式中给出的 ClassBody。
- 正在实例化的类是匿名子类。
强调我的。
此外,在 JLS - 第 12.5 节中,您可以阅读有关对象创建过程的信息。我在这里引用一句话: -
每当创建新的类实例时,都会为其分配内存空间,并为类类型中声明的所有实例变量和在类类型的每个超类中声明的所有实例变量(包括可能隐藏的所有实例变量)留出空间。
在将对新创建对象的引用作为结果返回之前,将使用以下过程处理指示的构造函数以初始化新对象:
您可以在我提供的链接上阅读有关完整过程的信息。
要实际看到要实例化的类是匿名子类,您只需要编译这两个类即可。假设您将这些类放在两个不同的文件中:
我的.java:
abstract class My {
public void myMethod() {
System.out.print("Abstract");
}
}
聚.java:
class Poly extends My {
public static void main(String a[]) {
My m = new My() {};
m.myMethod();
}
}
现在,编译两个源文件:
javac My.java Poly.java
现在,在编译源代码的目录中,您将看到以下类文件:
My.class
Poly$1.class // Class file corresponding to anonymous subclass
Poly.class
请参阅该类 - 。它是由编译器创建的类文件,对应于您使用以下代码实例化的匿名子类:Poly$1.class
new My() {};
因此,很明显,正在实例化不同的类。只是,只有在编译器编译后,才会为该类指定一个名称。
通常,类中的所有匿名子类都将按以下方式命名:
Poly$1.class, Poly$2.class, Poly$3.class, ... so on
这些数字表示这些匿名类在封闭类中的出现顺序。