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

时间:2012-12-02 16:01:59

标签: java oop class object abstract

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

我对此有所争论。然后他告诉我自己在家里试试。

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

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

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

16 个答案:

答案 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)

您可以做出的观察:

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

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

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


    超类实例化怎么样?

    抽象超类不是由我们实例化的,而是由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抽象类,但你可以:

  1. 创建一个空类
  2. 从抽象类继承它
  3. 实例化dervied类
  4. 因此,您可以通过派生类实例访问抽象类中的所有方法和属性。

答案 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类的子类。