面试官问道 - 我们可以实例化一个抽象类吗?我说,不,他告诉我 - 错,我们可以。
我对此有所争论。然后他告诉我自己在家里试试。
abstract class my {
public void mymethod() {
System.out.print("Abstract");
}
}
class poly {
public static void main(String a[]) {
my m = new my() {};
m.mymethod();
}
}
在这里,我正在创建我的类的实例并调用抽象类的方法。有人可以向我解释一下吗?在我的采访中我真的错了吗?
答案 0 :(得分:694)
在这里,我正在创建我的班级实例
不,您不是在这里创建抽象类的实例。而是在创建抽象类的匿名子类的实例。然后,您在抽象类引用上调用指向子类对象的方法。
JLS - Section # 15.9.1中明确列出了此行为: -
如果类实例创建表达式在类主体中结束,那么 被实例化的类是一个匿名类。然后:
- 如果T表示一个类,则声明由T命名的类的匿名直接子类。如果这是一个编译时错误 由T表示的类是最终类。
- 如果T表示接口,则声明实现由T命名的接口的Object的匿名直接子类。
- 在任何一种情况下,子类的主体都是类实例创建表达式中给出的ClassBody。
- 正在实例化的类是匿名子类。
强调我的。
此外,在JLS - Section # 12.5中,您可以阅读对象创建过程。我在这里引用一句话: -
每当创建新的类实例时,都会分配内存空间 为它提供了在类中声明的所有实例变量的空间 type和在每个超类中声明的所有实例变量 类类型,包括可能隐藏的所有实例变量。
在对新创建的对象的引用之前返回为 结果,处理指示的构造函数以初始化new 使用以下过程对象:
您可以在我提供的链接上阅读完整的程序。
要实际看到要实例化的类是 Anonymous SubClass ,您只需要编译两个类。假设您将这些类放在两个不同的文件中:
<强> 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
这些数字表示这些匿名类在封闭类中出现的顺序。
答案 1 :(得分:89)
上面实例化了一个匿名内部类,它是my
抽象类的子类。它并不完全等同于实例化抽象类本身。 OTOH,每个子类实例都是其所有超类和接口的实例,因此大多数抽象类确实通过实例化其中一个具体的子类来实例化。
如果面试官只是说“错了!”没有解释,并举了这个例子,作为一个独特的反例,我认为他不知道他在说什么。
答案 2 :(得分:83)
= my() {};
表示存在匿名实现,而不是对象的简单实例化,应该是:= my()
。你永远不能实例化一个抽象类。
答案 3 :(得分:30)
您可以做出的观察:
poly
延伸my
?这没用...... my.class
,poly.class
和poly$1.class
我们可以实例化一个抽象类吗?
不,我们做不到。我们可以做的是,创建一个匿名类(这是第三个文件)并实例化它。
超类实例化怎么样?
抽象超类不是由我们实例化的,而是由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
答案 4 :(得分:18)
你可以在一行中简单地回答
否,您永远不能实例抽象类
但是,面试官仍然不同意,那么你可以告诉他/她
你所能做的就是,你可以创建一个匿名类。
而且,根据Anonymous类,类声明并在同一位置/行实例化
因此,面试官可能有兴趣检查您的置信水平以及您对OOP的了解程度。
答案 5 :(得分:16)
技术部分在其他答案中得到了很好的阐述,主要结束于:
“他错了,他不知道什么,请他加入SO并将其全部清除:)”
我想解决这个事实(在其他答案中已经提到过),这可能是stress-question,并且是许多采访者了解更多关于你的重要工具,以及你如何应对困难和异常情况。通过给你错误的代码,他可能想知道你是否反驳过。要知道你是否有信心在类似的情况下与你的老年人站在一起。
P.S:我不知道为什么,但我觉得面试官已经读过这篇文章了。答案 6 :(得分:13)
抽象类无法实例化,但它们可以是子类。 See This Link
最好的例子是
虽然 Calender类有一个抽象方法getInstance(),但是当你说Calendar calc=Calendar.getInstance();
calc指的是GregorianCalendar类的类实例为“GregorianCalendar extends Calendar ”
Infact 匿名内部类型 允许您创建抽象类的无名子类及其实例。
答案 7 :(得分:11)
技术答案
抽象类无法实例化 - 这是定义和设计。
来自JLS,第8章。类:
命名类可以声明为abstract(第8.1.1.1节)并且必须声明 摘要如果没有完全实施;这样的班级不可能 实例化,但可以通过子类进行扩展。
来自JSE 6的Java doc for Classes.newInstance():
InstantiationException - 如果此Class表示抽象类,接口,数组 类,原始类型或空白;或者如果该类没有无效的构造函数;或者如果 实例化由于其他原因而失败。
当然,您可以实例化抽象类的具体子类(包括匿名子类),并执行对抽象类型的对象引用的类型转换。
对此不同的角度 - Teamplay&amp;社交情报:
当我们处理复杂的技术和法律规范时,这种技术上的误解在现实世界中经常发生。
“人才技能”在这里比“技术技能”更重要。如果竞争激烈并且积极地试图证明你的观点,那么理论上你可能是正确的,但你也可以在战斗/破坏“面子”/创造一个敌人而不是它的价值时做更多的伤害。在解决分歧方面需要和解和理解。谁知道 - 也许你是“两个都正确”但是对于条款的工作方式略有不同?
谁知道 - 尽管不太可能,面试官有可能故意引入一个小小的冲突/误解,让你陷入困境,看看你在情感和社交方面的表现如何。与同事保持亲切和建设性,遵循老年人的建议,并在面试结束后通过电子邮件或电话解决任何挑战/误解。表明你是有动力和注重细节的。
答案 8 :(得分:7)
一个公认的事实是abstract class
可以不实例化,因为每个人都会回答。
当程序定义匿名类时,编译器实际上创建了一个具有不同名称的新类(具有模式EnclosedClassName$n
,其中n
是匿名类号)
因此,如果您反编译此Java类,您将找到如下代码:
my.class
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();
}
}
答案 9 :(得分:4)
关于抽象类
抽象类的目的是表现得像一个基础。在继承层次结构中,您将看到顶部的抽象类。
答案 10 :(得分:4)
不,你不能立即抽象一个抽象类。我们只实例化匿名类。在抽象类中,我们声明抽象方法并仅定义具体方法。
答案 11 :(得分:3)
扩展类并不意味着您要实例化该类。实际上,在您的情况下,您正在创建子类的实例。
我很确定抽象类不允许启动。所以,我会说不:你不能实例化一个抽象类。但是,你可以扩展它/继承它。
您无法直接实例化抽象类。但这并不意味着你不能间接获得类的实例(不是实际的原始抽象类的实例)。我的意思是你无法实例化orginial抽象类,但你可以:
因此,您可以通过派生类实例访问抽象类中的所有方法和属性。
答案 12 :(得分:3)
您可以说:我们无法实例化抽象类,但我们可以使用new
关键字来创建匿名类实例,只需添加{{ 1}}作为抽象类末尾的实现主体。
答案 13 :(得分:2)
实例化抽象类是不可能的。 你真正可以做的是,在抽象类中实现一些常用方法,让其他人未实现(声明它们是抽象的),让具体的下行程序根据需要实现它们。 然后你可以创建一个工厂,它返回这个抽象类的实例(实际上是他的实现者)。然后在工厂中决定选择哪个实施者。这被称为工厂设计模式:
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
}
具体实现者只需要实现声明为abstract的方法,但是可以访问抽象类中那些未实现为abstract的类中实现的逻辑:
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的接收器将调用他的方法并获取逻辑,在具体的下行程序中(部分在抽象类方法中)实现,而不知道他得到的具体实现是什么。这也称为控制或依赖注入的反转。
答案 14 :(得分:2)
不,我们不能创建抽象类的对象,而是创建抽象类的引用变量。引用变量用于引用派生类的对象(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());
}
}
这里我们看到我们不能创建类型为Figure的对象,但我们可以创建一个类型为Figure的引用变量。这里我们创建了一个类型为Figure的引用变量,而Figure类引用变量用于引用Class Rectangle和Triangle的对象。
答案 15 :(得分:0)
实际上,我们不能直接创建抽象类的对象。我们创建的是抽象调用的引用变量。 reference变量用于引用继承Abstract类的类的对象,即Abstract类的子类。