java - Interview:我们可以实例化抽象类?

  显示原文与译文双语对照的内容

面试官问- 我们可以实例化一个抽象类? 我说,不,他告诉我- 错误,我们可以。

我对此进行了一点论证。 然后他让我在你的家里亲自尝试。


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

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

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

时间:

在这里,我正在创建我的类的实例

否,你没有在这里创建抽象类的实例。 相反,你正在创建一个匿名子类的实例。 然后你调用方法抽象类引用指向子类对象。

这里行为在 JLS - 节 # 15.9.1中明确列出: -

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

  • 如果T 表示一个类,则声明了由T 命名的类的匿名直接子类。 这是compile-time错误如果类用t是一个 final 类。
  • 如果T 表示接口,则声明实现了由T 命名的接口的匿名直接子类。
  • 无论哪种情况,子类的主体都是类实例创建表达式中给出的ClassBody 。
  • 正在实例化的类为匿名子类。

强调挖掘。

同样,在 JLS - # 12.5 中,你可以阅读对象创建过程 。 我将从这里引用一个语句: -

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

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

你可以阅读我提供的链接上的完整过程。


实际上,要看到正在实例化的类是匿名子类,你只需要编译你的类。 假设你将这些类放在两个不同的文件中:

My.java:


abstract class My {
 public void myMethod() {
 System.out.print("Abstract");
 }
}

Poly.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

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

上面的实例是一个匿名内部类,它是 my 抽象类的子类。 它并不是严格等价于实例化抽象类本身。 OTOH,每个子类实例都是它的所有超类和接口的实例,所以大多数抽象类都是通过实例化它们的一个具体子类来实现的。

如果面试官只是说"错误"而没有解释,并给出了一个独特的counter-example,我想他不知道他在谈论什么,尽管。

= my() {}; 意味着有一个匿名实现,而不是一个对象的简单实例化,它应该是: = my() 。不能实例化抽象类。

你可以做的只是观察:

  1. 为什么 poly 扩展 my这毫无用处。。
  2. 编译的结果是什么? 三个文件:my.classpoly.classpoly$1.class
  3. 如果我们可以实例化一个抽象类,我们也可以实例化一个接口。。 奇怪的。。


我们可以用英镑来实例化一个抽象类?

不,我们不能。我们可以做的就是创建一个匿名类( 那是第三个文件) 并实例化它。


超类别实例化的?

抽象超类没有实例化我们但java。

编辑:请他测试这个


public static final void main(final String[] args) {
 final my m1 = new my() {
 };
 final my m2 = new my() {
 };
 System.out.println(m1 == m2);

 System.out.println(m1.getClass().toString());
 System.out.println(m2.getClass().toString());

}

输出是:


false
class my$1
class my$2

抽象类不能实例化,但可以对它们进行子类化。 查看这里链接

最好的例子是

尽管的日历类有一个抽象方法 getInstance(),但是当你说 Calendar calc=Calendar.getInstance();

calc指的是类实例的类gregoriancalendar"gregoriancalendar日历延伸"

事实上 annonymous内部类型允许你创建一个no-name抽象类的子类和的一个实例。

事实是 abstract class不是可以实例化每个人都回答。

当程序定义匿名类时,编译器实际上创建一个具有不同名称的新类( 有模式 EnclosedClassName$n,其中 n 是匿名类号)

因此,如果你反编译这个Java类,你会发现下面的代码:

my.class


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

poly$1.类( 生成的类的"匿名类"


class poly$1 extends my 
{
} 

ploly.cass


public class poly extends my
{
 public static void main(String[] a)
 {
 my m = new poly.1();//instance of poly.1 class NOT the abstract my class

 m.mymethod();
 }
}

...