小编典典

我们可以实例化一个抽象类吗?

all

在我的一次采访中,有人问我“我们是否可以实例化一个抽象类?”

我的回答是“不,我们不能”。但是,面试官告诉我“错了,我们可以。”

我为此争论了一下。然后他让我自己在家试试。

abstract class my {
    public void mymethod() {
        System.out.print("Abstract");
    }
}

class poly {
    public static void main(String a[]) {
        my m = new my() {};
        m.mymethod();
    }
}

在这里,我正在创建我的类的实例并调用抽象类的方法。谁能给我解释一下?我面试的时候真的错了吗?


阅读 141

收藏
2022-03-06

共1个答案

小编典典

在这里,我正在创建我的班级的实例

不,您不是在这里创建抽象类的实例。相反,您正在创建抽象类的 匿名子类的实例。 然后您在指向 子类 object的 抽象类 引用上调用该方法。
__

这种行为在JLS - 第 15.9.1
中明确列出:
-

如果类实例创建表达式以类体结尾,则被实例化的类是匿名类。然后:

  • 如果 T 表示一个类,则声明一个由 T 命名的类的匿名直接子类。如果 T 表示的类是最终类,则这是编译时错误。
  • 如果 T 表示一个接口,则声明一个实现由 T 命名的接口的 Object 的匿名直接子类。
  • 在任何一种情况下,子类的主体都是类实例创建表达式中给出的 ClassBody。
  • 被实例化的类是匿名子类。

强调我的。

此外,在JLS - 第 # 12.5
中,您可以阅读有关
对象创建过程 的信息。我将在这里引用一个声明:-

每当创建一个新的类实例时,都会为其分配内存空间,并为该类类型中声明的所有实例变量和该类类型的每个超类中声明的所有实例变量(包括所有可能隐藏的实例变量)分配空间。

就在作为结果返回对新创建对象的引用之前,使用以下过程处理指示的构造函数以初始化新对象:

您可以在我提供的链接上阅读完整的程序。


要实际看到被实例化的类是 Anonymous SubClass ,您只需要编译您的两个类。假设您将这些类放在两个不同的文件中:

我的.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

这些数字表示这些匿名类出现在封闭类中的顺序。

2022-03-06