Java 抽象类:为什么newInstance()未给出编译错误,但构造函数调用给出错误?

Java 抽象类:为什么newInstance()未给出编译错误,但构造函数调用给出错误?,java,abstract-class,instantiation,instanceof,Java,Abstract Class,Instantiation,Instanceof,编译器知道AbstractDemo是一个抽象类,不能实例化抽象类 但当我调用newInstance()方法时,为什么它没有给出编译时错误 import java.lang.reflect.Constructor; public abstract class AbstractDemo{ public AbstractDemo(){ System.out.println("Default constructor"); } public static void

编译器知道
AbstractDemo
是一个抽象类,不能实例化抽象类

但当我调用
newInstance()
方法时,为什么它没有给出编译时错误

import java.lang.reflect.Constructor;

public abstract class AbstractDemo{
    public AbstractDemo(){
        System.out.println("Default constructor");
    }
    public static void main(String args[]){
        try{
            /* No compilation error for this statement */
            AbstractDemo demo = AbstractDemo.class.newInstance(); 

            Constructor[] ctors = AbstractDemo.class.getDeclaredConstructors();
            for ( int i=0; i < ctors.length; i++){
                System.out.println(ctors[i]);
                /* No compilation error for this statement too */
                AbstractDemo demo1 = (AbstractDemo) ctors[i].newInstance();
            }
            /* Compilation error here */
            // AbstractDemo demo2 = new AbstractDemo(); 
        }catch(Exception err){
            err.printStackTrace();
        }
    }
}
编辑:

编译器可以智能地给出此语句的错误:

但不适用于此声明

我是否错过了重要的一课

类上存在
newInstance()
方法。由于AbstractDemo.class只是类的一个实例(而不是
的子类),编译器无法强制执行此操作。这里要知道的一点是,当您使用反射时,您是元编程,不能真正依赖于编译器

要以不同的方式指出这一点,请按以下代码排序:

Class clazz = AbstractDemo.class;
// code that could possibly re-assign clazz here
Object o = clazz.newInstance();

除非运行代码,否则无法知道调用
newInstance()
clazz
变量可能包含什么。因此,编译器无法标记该类。

您要求的是在运行时而不是在编译时创建类


在这种情况下,
newInstance
方法将在运行时抛出一个
InstantiationException

编译器的任务是检查编译时规则(er,并编译代码)。您正在调用的方法是
Class#newInstance
,与
AbstractDemo
没有任何(直接)关系。
Class#newInstance
将抛出(因为调用它的
Class
的实例用于抽象类)这一事实是一个运行时问题

虽然从理论上讲,有时可以在编译时确定对
的特定实例的特定引用引用引用抽象类(例如
AbstractDemo.Class
),但通常是不可能的,例如:

void someMethodInMyOwnClass(Class c) {
    Object o = c.newInstance();
}
即使是这样,我们也需要某种内置的规则或注释系统(例如编译时信息)来说明“如果
实例引用抽象类,则不能调用此类的此方法。”

所以我们讨论的是非平凡的工作,而这项工作没有真正的价值,有时是编译时错误,有时是运行时错误

考虑:编译器还可以计算出这会抛出一个NPE:

String s = null;
if (s.equalsIgnoreCase("foo")) {
    // ...
}
或者该循环的主体将永远不会执行:

int x = 10;
while (x < 10) {
    System.out.println("Never gets here");
}
intx=10;
而(x<10){
System.out.println(“从未到达这里”);
}

但我们没有让它这么做;这些都是运行时问题。

编译器没有关于给定的
构造函数
实例与哪个类关联的静态信息。对于您的特定代码,一个足够聪明的编译器可以找到它,但是由于这种分析不能在一般情况下执行,因此不能依赖于它,编译器不需要执行它。我不知道有谁能做到这一点

但是,请注意,
newInstance()
调用将在运行时失败,方法是抛出一个
InstantiationException
。事实上,这是该异常类唯一明确的目的


但是,当您试图直接实例化抽象类时,编译器确切地知道您实例化的是什么类,并且它是抽象的,因此它可以而且必须在编译时拒绝代码。

运行时会发生什么?编译器如何知道
构造函数
对象与Absact类相关?使用此方法可以有效地绕过编译器执行的编译时异常检查。关键课程:编译器无法阻止您编写损坏的代码。@Titus该部分实际上指的是抛出检查过的异常的构造函数。@Kayaman哦,你说得对,下一行提到的事实是,某些东西并不总是可检测的,这并不是一个合理的理由,为什么当它可检测时会被忽略。你的最后一个例子很好。将
x
的声明更改为
final
,将出现编译器错误。问题不在于是否可以检测到某些东西,而在于规范中包含了什么。
void someMethodInMyOwnClass(Class c) {
    Object o = c.newInstance();
}
String s = null;
if (s.equalsIgnoreCase("foo")) {
    // ...
}
int x = 10;
while (x < 10) {
    System.out.println("Never gets here");
}