关于Java:我们可以实例化抽象类吗?

Interview: Can we instantiate abstract class?

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

我在这个问题上有点争论。然后他让我在你家自己试试这个。

1
2
3
4
5
6
7
8
9
10
11
12
abstract class my {
    public void mymethod() {
        System.out.print("Abstract");
    }
}

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

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


Here, i'm creating instance of my class

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

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

If the class instance creation expression ends in a class body, then
the class being instantiated is an anonymous class. Then:

  • If T denotes a class, then an anonymous direct subclass of the class named by T is declared. It is a compile-time error if the
    class denoted by T is a final class.
  • If T denotes an interface, then an anonymous direct subclass of Object that implements the interface named by T is declared.
  • In either case, the body of the subclass is the ClassBody given in the class instance creation expression.
  • The class being instantiated is the anonymous subclass.

强调我的。

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

Whenever a new class instance is created, memory space is allocated
for it with room for all the instance variables declared in the class
type and all the instance variables declared in each superclass of the
class type, including all the instance variables that may be hidden.

Just before a reference to the newly created object is returned as the
result, the indicated constructor is processed to initialize the new
object using the following procedure:

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

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

My.java:

1
2
3
4
5
abstract class My {
    public void myMethod() {
        System.out.print("Abstract");
    }
}

Poly.java:

1
2
3
4
5
6
class Poly extends My {
    public static void main(String a[]) {
        My m = new My() {};
        m.myMethod();
    }
}

现在,编译两个源文件:

1
javac My.java Poly.java

现在,在编译源代码的目录中,您将看到以下类文件:

1
2
3
My.class
Poly$1.class  // Class file corresponding to anonymous subclass
Poly.class

看那一类-Poly$1.class。它是编译器创建的类文件,与使用以下代码实例化的匿名子类相对应:

1
new My() {};

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

通常,类中的所有匿名子类都将以这种方式命名:

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

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


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

如果面试官只是说"错了!"不过,我认为他不知道自己在说什么,我只是举了个例子作为一个独特的反例。


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


只要你能观察到:

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

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

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

    < BR>

    超级类的实例化怎么样?

    抽象超类不是由我们实例化的,而是由Java来实例化的。

    编辑:让他测试这个

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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());

    }

    输出为:

    1
    2
    3
    false
    class my$1
    class my$2


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

    No, you can never instance Abstract Class

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

    all you can do is, you can create an Anonymous Class.

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

    所以,面试官可能会有兴趣检查你的信心水平以及你对失败者的了解程度。


    技术部分已经在其他答案中得到了很好的涵盖,主要结束于:"他错了,他不知道什么,请他加入,然后把一切都弄清楚:)"

    我想说明一个事实(在其他答案中已经提到过),这可能是一个压力问题,是许多面试官了解你的重要工具,以及你对困难和异常情况的反应。给你错误的代码,他可能想看看你是否反驳。要知道你是否有信心在类似的情况下对抗你的前辈。

    P.S:我不知道为什么,但我觉得面试官读过这篇文章。


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

    最好的例子是

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

    calc将类gregoricalendar的类实例称为"gregoricalendar扩展日历"。

    infact annonymous inner类型允许您创建抽象类的无名称子类和该类的一个实例。


    技术回答

    抽象类无法实例化-这是由定义和设计决定的。

    摘自《捷豹路虎》,第8章。Classes:

    A named class may be declared abstract (§8.1.1.1) and must be declared
    abstract if it is incompletely implemented; such a class cannot be
    instantiated, but can be extended by subclasses.

    从JSE 6 JAVA DOC到类NeWistStand():

    InstantiationException - if this Class represents an abstract class, an interface, an array
    class, a primitive type, or void; or if the class has no nullary constructor; or if the
    instantiation fails for some other reason.

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

    从另一个角度来看-团队合作和社交智能:

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

    "人的技能"在这里比"技术技能"更重要。如果你有竞争性和侵略性地试图证明你的观点,那么你在理论上是对的,但是你也可以在战斗中造成更多的伤害/伤害"脸"/创造一个比它价值更大的敌人。在解决你的分歧时要和解和理解。谁知道——也许你"两个都是对的",但对术语的理解却略有不同??

    谁知道——尽管不太可能,但面试官可能故意引入一个小冲突/误解,让你陷入一个充满挑战的境地,看看你在情感和社交方面的表现。与同事和蔼可亲,富有建设性,听从长者的建议,并在面试后跟进,以解决任何挑战/误解-通过电子邮件或电话。显示出你的积极性和细节导向。


    很好的一个事实是,abstract class不能像每个人回答的那样被实例化。

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

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

    我的班级

    1
    2
    3
    4
    5
    6
    abstract class my {
        public void mymethod()
        {
            System.out.print("Abstract");
        }
    }

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

    1
    2
    3
    class poly$1 extends my
    {
    }

    普拉斯

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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();
        }
    }

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


    关于抽象类

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

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


    您可以说:
    我们不能实例化抽象类,但是我们可以使用new关键字创建匿名类实例,只需在抽象类的末尾添加{}作为实现体。


    扩展类并不意味着您正在实例化类。实际上,在您的例子中,您正在创建子类的一个实例。

    我确信抽象类不允许初始化。所以,我会说不:你不能实例化一个抽象类。但是,您可以扩展/继承它。

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

  • 创建空类
  • 从抽象类继承它
  • 实例化Dervied类
  • 因此,您可以通过派生类实例访问抽象类中的所有方法和属性。


    不,我们不能创建抽象类的对象,而是创建抽象类的引用变量。引用变量用于引用派生类(抽象类的子类)的对象。

    下面的例子说明了这个概念

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    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类的引用变量用于引用矩形和三角形类的对象。


    无法实例化抽象类。您真正能做的是,在抽象类中实现一些常见的方法,让其他未实现的方法(将它们声明为抽象的),并让具体的子代根据需要实现它们。然后您可以创建一个工厂,它返回这个抽象类的一个实例(实际上是他的实现者)。然后在工厂中,您决定选择哪个实现者。这被称为工厂设计模式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
       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
        }

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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;
        }
    }

    最后,工厂看起来是这样的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    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的接收者会调用他身上的方法,并获取逻辑,在具体的子代(部分在抽象类方法中)中实现,而不知道他得到的具体实现是什么。这也被称为控制反转或依赖注入。