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


573

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

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

我对此有些争论。然后他告诉我自己在家尝试一下。

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

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

在这里,我正在创建类的实例和抽象类的调用方法。有人可以向我解释一下吗?面试中我真的错了吗?


2
虽然只是轻微的关系,可以或许实例化C ++抽象类:如果你得到一个非抽象类B从一个抽象的A建设部分中B实例,它由运行A的构造函数,该对象的运行时类型实际上是A。但是只是暂时的。
弗拉德2012年

8
@jWeavers:他给出的例子是完全错误的。您应该问他“那么抽象类的用途是什么”。如果要扩展它,那么为什么要创建扩展类的实例?这是一个完全新的对象,在这里你没有数据结束了..
柠檬汁

3
或者,面试官可能想根据他的提议来检查您对自己的陈述是否有信心!
2013年

5
他对你撒谎。当您未能指出该代码不能做什么并解释匿名子类是什么时,您就丢下了球。他可能已经知道了,想看看您是否知道。
candied_orange

2
这不是测验节目,而是工作面试,对吗?那么,如果Java或C ++允许实例化抽象类怎么办?您不会这样做,因为这不是一件聪明的事情。在Objective-C中,抽象类仅按约定是抽象的,实例化它们是一个错误。
gnasher729

Answers:


722

在这里,我正在创建我的课程的实例

不,您不是在这里创建抽象类的实例。而是要创建抽象类的匿名子类的实例。然后,您在抽象类引用上调用指向子类对象的方法

JLS-第15.9.1节中明确列出了此行为:-

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

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

强调我的。

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

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

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

您可以在我提供的链接上阅读有关完整过程的信息。


要实际上看到要实例化的类是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

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


172
@coders。确切的答案是:-您不能实例化抽象类,但是可以实例化抽象类的具体子类。
罗希特·贾因

16
在一行中,您可以说:-您永远不能实例化一个抽象类。这就是抽象类的目的。
Rahul Tripathi

66
听起来面试官对他的回答比对您的回答更投入了……
Neil T.

7
根据另一条评论(带有JLS参考),“据说一个对象是其类及其类的所有超类的实例”-因此,我们实际上不是在技术上在这里创建抽象类的实例吗?即实例化抽象类?
arshajii 2012年

6
@ARS我想说的是instance of和之间的区别instantiating。您只能实例化一个类,而由于继承,您创建的对象可以是多个类的实例。
西蒙·福斯伯格

89

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

如果面试官只是说“错!” 作为一个独特的反例,我认为他不知道他在说什么。


10
严格来说,抽象超类没有实例化。调用它的构造函数初始化实例变量。
感知2012年

4
是的,它是:subclassInstance instanceof SuperClass将返回true,因此该对象是超类的实例,这意味着该超类已实例化。但这仅仅是语义挑剔。
JB Nizet

5
确实可以是语义。Java 根据通过new关键字创建对象的方式定义了实例化(您不能使用抽象类来创建对象)。但是,当然,具体的子类将正确地报告其父级层次结构的每个成员的实例。
感知2012年

11
JLS的第4.12.6段说:“据说一个对象是其类及其类的所有超类的实例。”
JB Nizet

85

= my() {};表示存在一个匿名实现,而不是对象的简单实例化,应该是:= my()。您永远不能实例化一个抽象类。


30

您可以进行以下观察:

  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

观察3的+1:例如,我们可以做Serializable s = new Serializable() {};(这几乎没有用),如果标记到您的代码上可以class my$3(或者提供任何封闭的类和数字)
恢复莫妮卡-notmaynard 2013年

18

您只需要一行即可回答

,您永远不能实例化Abstract Class

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

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

并且,根据匿名类,该类在同一位置/行声明并实例化

因此,面试官可能有兴趣检查您的置信度以及您对OOP的了解程度。


17

技术部分已经在其他答案中得到了很好的介绍,它的主要结尾是:
“他错了,他不了解东西,要求他加入SO并把所有事情都清除掉:)”

我想解决一个事实(在其他答案中已经提到),这可能是一个压力问题,并且是许多面试官了解您的重要工具,以及您如何应对困难和异常情况。通过给您不正确的代码,他可能想看看您是否提出反对。要知道在这种情况下您是否有信心与老年人抗衡。

PS:我不知道为什么,但是我有一种感觉,面试官已经阅读了这篇文章。


13

抽象类不能实例化,但是可以被子类化。 查看此链接

最好的例子是

尽管Calender类具有抽象方法getInstance(),但是当您说Calendar calc=Calendar.getInstance();

calc引用类GregorianCalendar的类实例为“ GregorianCalendar扩展Calendar

实际上的匿名内部类型 允许您创建抽象类的无名称子类及其实例。


11

技术答案

抽象类无法实例化-这是根据定义和设计。

从JLS,第8章。类:

命名类可以声明为抽象(第8.1.1.1节),如果实现不完整,则必须声明为抽象。这样的类不能实例化,但是可以由子类扩展。

从JSE 6 Java文档中获取Classes.newInstance():

InstantiationException-如果此Class表示一个抽象类,一个接口,一个数组类,一个原始类型或void;或如果类没有空构造函数;或者 或者实例化由于其他原因而失败。

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

与此不同的角度-团队合作和社交智慧:

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

“人际技巧”在这里比“技术技巧”更为重要。如果通过竞争和积极地尝试证明自己的观点,那么理论上你可能是正确的,但在打架/破坏“面孔” /制造敌人方面,也可能造成更大的损失,而不是值得的。和解与谅解可以解决您的分歧。谁知道-也许您“对”,但对术语的含义却略有不同?

谁知道-尽管不太可能,但面试官可能故意引入了小的冲突/误解,使您陷入充满挑战的境地,并观察您在情感和社交方面的表现。与同事保持亲切和建设性,听取上级的建议,面试后通过电子邮件或电话解决任何挑战/误解。显示您的积极性和注重细节。


7

这是一个公认的事实,abstract class可以为大家回答被实例化。

当程序定义匿名类时,编译器实际上会创建一个具有不同名称的新类(其模式为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();
    }
}

4

不,您不能实例化一个抽象类,我们仅实例化匿名类,在抽象类中,我们声明抽象方法并仅定义具体方法。


4

关于抽象类

  • 无法创建抽象类的对象
  • 可以创建变量(行为类似于数据类型)
  • 如果子级不能覆盖父级的至少一种抽象方法,则子级也将变为抽象级
  • 没有子类,抽象类是无用的

抽象类的目的是表现得像基类。在继承层次结构中,您将看到顶部的抽象类。


3

您可以说:
我们无法实例化一个抽象类,但是我们可以使用new关键字通过{}在抽象类末尾添加作为实现主体来创建一个匿名类实例。


3

扩展类并不意味着您要实例化该类。实际上,在您的情况下,您正在创建子类的实例。

我很确定抽象类不允许启动。因此,我会拒绝:您无法实例化抽象类。但是,您可以扩展/继承它。

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

  1. 创建一个空类
  2. 从抽象类继承
  3. 实例化派生类

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


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的接收者将在他身上调用这些方法并获得逻辑,这些逻辑是在具体的下级实现中(部分在抽象类的方法中实现),而不知道他得到的具体实现是什么。这也称为控制反转或依赖注入。


2

不,我们不能创建抽象类的对象,但是可以创建抽象类的引用变量。reference变量用于引用派生类的对象(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类引用变量用于引用Class Rectangle和Triangle的对象。


0

实际上,我们不能直接创建抽象类的对象。我们创建的是抽象调用的引用变量。reference变量用于引用继承Abstract类的类的对象,即Abstract类的子类。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.