采访:我们可以实例化抽象类吗?

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

我在这方面辩论了一下。 然后他让我在你家里试试这个。

abstract class my { public void mymethod() { System.out.print("Abstract"); } } class poly { public static void main(String a[]) { my m = new my() {}; m.mymethod(); } } 

在这里,我正在创build我的类的实例并调用抽象类的方法。 任何人都可以向我解释这个吗? 在面试中我真的错了吗?

在这里,我正在创build我的class的实例

不,你不是在这里创build你的抽象类的实例。 而是创build一个抽象类的匿名子类的实例。 然后你调用指向子类对象的 抽象类引用的方法。

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

如果类实例创buildexpression式在一个类体中结束,那么被实例化的类就是一个匿名类。 然后:

  • 如果T表示一个类,则声明由T命名的类的匿名直接子类。 如果T表示的类是最后一个类,那么这是一个编译时错误。
  • 如果T表示一个接口,则声明实现由T命名的接口的Object的匿名直接子类。
  • 无论哪种情况,子类的主体都是类实例创buildexpression式中给出的ClassBody。
  • 被实例化的类是匿名的子类。

强调我的。

另外,在JLS中的第12.5节中 ,您可以阅读关于对象创build过程 。 我会在这里引用一个陈述:

无论何时创build新的类实例,都会为其分配内存空间,以便在类types中声明的所有实例variables以及在类types的每个超类中声明的所有实例variables(包括可能隐藏的所有实例variables)。

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

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


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

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 。 它是由编译器创build的类文件,与使用以下代码实例化的匿名子类相对应:

 new My() {}; 

所以很显然,有一个不同的类被实例化。 只是这个类只有在编译器编译后才有名字。

一般来说,你的类中的所有匿名子类将以这种方式命名:

 Poly$1.class, Poly$2.class, Poly$3.class, ... so on 

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

上面实例化了一个匿名的内部类,它是my抽象类的一个子类。 这不等同于抽象类本身的实例化。 OTOH中,每个子类实例都是其所有超类和接口的一个实例,所以大多数抽象类实际上是通过实例化其中一个具体的子类来实例化的。

如果面试官只是说“错”! 而没有解释,并给出了这个例子,作为一个独特的反例,但我认为他不知道他在说什么。

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

只要观察一下,你就可以

  1. 为什么poly延伸my ? 这是没用的…
  2. 汇编的结果是什么? 三个文件: my.classpoly.classpoly$1.class
  3. 如果我们可以像这样实例化一个抽象类,我们可以实例化一个接口,太怪了…

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

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

那么超类实例化呢?

抽象超类不是由我们而是由java实例化的。

编辑:让他来testing这个

 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 

你可以简单地回答,只需一行

,你永远不能实例抽象类

但面试官还是不同意,那么你可以告诉他/她

你所能做的就是,你可以创build一个匿名类。

而且,根据匿名类, 类声明和实例化在同一个地方/行

所以,面试官可能有兴趣检查一下你的信心水平,以及你对OOP的了解程度。

其他答案已经涵盖了技术部分,主要结论是:
“他错了,他不知道什么东西,请他join并清理干净:)”

我想解决这个问题(在其他答案中已经提到过),这可能是一个压力问题 ,是许多访调员更了解你的重要工具,你如何对困难和不寻常的情况作出反应。 通过给你不正确的代码,他可能想看看你是否反驳。 要知道你是否有信心在类似的情况下站起来对抗你的前辈。

PS:我不知道为什么,但我有一种感觉,采访者已经读过这篇文章。

抽象类不能被实例化,但可以被分类。 看到这个链接

最好的例子是

虽然Calender类有一个抽象方法getInstance() ,但是当你说Calendar calc=Calendar.getInstance();

calc将GregorianCalendar类的类实例称为“GregorianCalendar extends Calendar

Infact annonymous内部types 允许您创build抽象类的一个无名称的子类和这个的一个实例。

技术答案

抽象类不能实例化 – 这是根据定义和devise。

来自JLS,第8章。类:

一个命名的类可能被声明为抽象的(§8.1.1.1),如果它不完全实现,则必须声明为抽象的; 这样的类不能被实例化,而是可以被子类扩展。

从JSE 6的Classes.newInstance()的java文档:

InstantiationException – 如果此类表示抽象类,接口,数组类,基元types或void; 或者如果class级没有空的构造函数; 或者如果实例化由于其他原因而失败。

当然,您可以实例化抽象类(包括匿名子类)的具体子类,也可以对抽象types执行对象引用的types转换。

不同的angular度 – 团队和社交情报:

当我们处理复杂的技术和法律规范时,这种技术误解在现实世界中经常发生。

“技能”比“技能”更重要。 如果竞争激烈地试图certificate你的论点,那么你可能在理论上是正确的,但你也可以做一个战斗/破坏性的“面子”/创造一个敌人,而不是它的价值更多的伤害。 和解和理解解决你的分歧。 谁知道 – 也许你是“正确的”,但是在条件上略有不同的含义?

谁知道 – 尽pipe不太可能,面试官可能会故意引入一个小小的冲突/误解,让你陷入一个充满挑战的境地,看看你在情绪和社交方面的performance如何。 与同事亲切而有build设性地听从老年人的意见,并在面试后继续解决任何挑战/误解 – 通过电子邮件或电话。 显示你的动机和细节为导向。

abstract class 不能像大家所回答的那样被实例化是一个公认的事实。

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

所以,如果你反编译这个Java类,你会发现代码如下:

我的课

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

poly $ 1.class(“匿名类”的生成类)

 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(); } } 

不,你不能瞬间抽象类。我们只实例化匿名类。在抽象类中,我们声明抽象方法,只定义具体的方法。

关于抽象类

  • 无法创build抽象类的对象
  • 可以创buildvariables(可以像数据types一样)
  • 如果一个孩子不能覆盖至less一个父母的抽象方法,那么孩子也变得抽象
  • 抽象类没有子类是没用的

抽象类的目的是performance得像一个基础。 在inheritance层次结构中,您会看到顶层的抽象类。

你可以说:
我们不能实例化一个抽象类,但是我们可以使用new关键字来创build一个匿名类实例,方法是在抽象类的最后添加{}作为实体。

扩展一个类并不意味着你正在实例化这个类。 实际上,就你而言,你正在创build一个子类的实例。

我很确定抽象类不允许启动。 所以,我会说不,你不能实例化一个抽象类。 但是,你可以扩展它/inheritance它。

你不能直接实例化一个抽象类。 但是,这并不意味着你不能间接获得一个类的实例(不是一个原始抽象类的实例)。 我的意思是你不能实例化最初的抽象类,但你可以:

  1. 创build一个空的类
  2. 从抽象类inheritance
  3. 实例化dervied类

所以你可以通过派生类实例访问抽象类中的所有方法和属性。

实例化一个抽象类是不可能的。 你真正能做的,是在抽象类中实现一些常用的方法,让别人不实现(声明为抽象类),让具体的下行根据需要实现它们。 然后你可以创build一个工厂,它返回这个抽象类的实例(实际上是他的实现者)。 在工厂中,您决定select哪个实施者。 这被称为工厂devise模式:

  public abstract class AbstractGridManager { private LifecicleAlgorithmIntrface lifecicleAlgorithm; // ... more private fields //Method implemented in concrete Manager implementors abstract public Grid initGrid(); //Methods common to all implementors public Grid calculateNextLifecicle(Grid grid){ return this.getLifecicleAlgorithm().calculateNextLifecicle(grid); } public LifecicleAlgorithmIntrface getLifecicleAlgorithm() { return lifecicleAlgorithm; } public void setLifecicleAlgorithm(LifecicleAlgorithmIntrface lifecicleAlgorithm) { this.lifecicleAlgorithm = lifecicleAlgorithm; } // ... more common logic and getters-setters pairs } 

具体的实现者只需要实现被声明为抽象的方法,但是可以访问抽象类中的那些类中实现的逻辑,这些抽象类没有声明为抽象的:

 public class FileInputGridManager extends AbstractGridManager { private String filePath; //Method implemented in concrete Manager implementors abstract public Grid initGrid(); public class FileInputGridManager extends AbstractGridManager { private String filePath; //Method implemented in concrete Manager implementors abstract public Grid initGrid(); public Grid initGrid(String filePath) { List<Cell> cells = new ArrayList<>(); char[] chars; File file = new File(filePath); // for example foo.txt // ... more logic return grid; } } 

最后,工厂看起来像这样:

 public class GridManagerFactory { public static AbstractGridManager getGridManager(LifecicleAlgorithmIntrface lifecicleAlgorithm, String... args){ AbstractGridManager manager = null; // input from the command line if(args.length == 2){ CommandLineGridManager clManager = new CommandLineGridManager(); clManager.setWidth(Integer.parseInt(args[0])); clManager.setHeight(Integer.parseInt(args[1])); // possibly more configuration logic ... manager = clManager; } // input from the file else if(args.length == 1){ FileInputGridManager fiManager = new FileInputGridManager(); fiManager.setFilePath(args[0]); // possibly more method calls from abstract class ... manager = fiManager ; } //... more possible concrete implementors else{ manager = new CommandLineGridManager(); } manager.setLifecicleAlgorithm(lifecicleAlgorithm); return manager; } } 

AbstractGridManager的接收者会调用他的方法并获得逻辑,在具体的下行(部分在抽象类的方法中)中实现,而不知道他得到了什么具体的实现。 这也被称为控制或dependency injection的反转。

不,我们不能创build抽象类的对象,而是创build抽象类的引用variables。 引用variables用于引用派生类的对象(Abstract类的子类)

这是说明这个概念的例子

 abstract class Figure { double dim1; double dim2; Figure(double a, double b) { dim1 = a; dim2 = b; } // area is now an abstract method abstract double area(); } class Rectangle extends Figure { Rectangle(double a, double b) { super(a, b); } // override area for rectangle double area() { System.out.println("Inside Area for Rectangle."); return dim1 * dim2; } } class Triangle extends Figure { Triangle(double a, double b) { super(a, b); } // override area for right triangle double area() { System.out.println("Inside Area for Triangle."); return dim1 * dim2 / 2; } } class AbstractAreas { public static void main(String args[]) { // Figure f = new Figure(10, 10); // illegal now Rectangle r = new Rectangle(9, 5); Triangle t = new Triangle(10, 8); Figure figref; // this is OK, no object is created figref = r; System.out.println("Area is " + figref.area()); figref = t; System.out.println("Area is " + figref.area()); } } 

在这里我们看到,我们不能创buildtypes图的对象,但我们可以创build一个types为图的引用variables。 这里我们创build了一个types为Figure和Reference的引用variables类引用variables用于引用Class Rectangle和Triangle的对象。