Java 为什么我的程序没有';当最终类变量未初始化时,是否显示编译时错误?

Java 为什么我的程序没有';当最终类变量未初始化时,是否显示编译时错误?,java,static,compilation,final,class-variables,Java,Static,Compilation,Final,Class Variables,对于以下代码: public class StaticFinal { private final static int i ; public StaticFinal() {} } 我得到编译时错误: StaticFinal.java:7: variable i might not have been initialized {} ^ 1 error 这是符合的,它说: 如果一个空的final(§4.12.4)类变量没有被声明它的类的静

对于以下代码:

public class StaticFinal
{
    private final static int i ;
    public StaticFinal()
    {}
}
我得到编译时错误:

StaticFinal.java:7: variable i might not have been initialized
        {}
         ^
1 error
这是符合的,它说:

如果一个空的final(§4.12.4)类变量没有被声明它的类的静态初始值设定项(§8.7)明确赋值(§16.8),则这是一个编译时错误

因此,上述错误完全可以理解。
但是现在考虑以下内容:

public class StaticFinal
{
    private final static int i ;
    public StaticFinal()throws InstantiationException
    {
        throw new InstantiationException("Can't instantiate"); // Don't let the constructor to complete.
    }
}
这里,构造函数永远不会完成,因为在构造函数的中间抛出“代码>实例化异常< /代码>。这段代码编译得很好
为什么?为什么这段代码没有显示关于
final
变量
i
未初始化的编译时错误


编辑

我在命令提示符下使用
javac1.6.0_25
编译它(不使用任何IDE)

在添加了使代码打印
I
的主要方法之后。代码将打印值0。这意味着java编译器会自动初始化值为0的i。 我用IntelliJ编写了它,必须禁用代码检查才能构建代码。否则它不会让我给出与抛出异常之前相同的错误

JAVA代码:未初始化 反编译 相同的

JAVA代码:已初始化 反编译 在对代码进行反编译之后,事实证明情况并非如此。因为反编译代码和原始代码是相同的。唯一的另一种可能性是通过Java虚拟机完成初始化。我最近所做的改变足以证明情况确实如此

你发现了这一点,我不得不说你很好

相关问题:

据我所知,我们都是开发人员,所以我相信我们不会在我们中间找到真正的反应……这件事与编译器内部有关……我认为这是一个bug,或者至少是一种不必要的行为

除了Eclipse,它有某种增量编译器(因此能够立即检测问题),命令行javac执行一次编译。现在,第一个片段

public class StaticFinal {
    private final static int i ;
}
这基本上与使用空构造函数(如第一个示例中)相同,抛出编译时错误,这很好,因为它遵守规范

在第二段中,我认为编译器中有一个bug;编译器似乎根据构造函数所做的事情做出了一些决定。如果你试着编译这个

public class StaticFinal
{
    private final static int i ;

    public StaticFinal() 
    {
        throw new RuntimeException("Can't instantiate"); 
    }
}
这比您的示例更奇怪,因为未经检查的异常没有在方法签名中声明,并且只会在运行时被发现(至少这是我在阅读本文之前所想的)

观察我可以说的行为(但根据规范是错误的)

对于静态最终变量,编译器会尝试查看它们是否已显式初始化,或者是否已在静态初始化程序块中初始化,但出于某种奇怪的原因,它也会在构造函数中查找某些内容:

  • 如果它们在构造函数中初始化,编译器将产生一个错误(您不能在那里为最终静态变量赋值)
  • 如果构造函数为空,编译器将产生错误(如果编译第一个示例,即带有显式零参数构造函数的示例,编译器将中断,指示构造函数的右括号作为错误行)
  • 如果由于引发异常而导致构造函数未完成而无法实例化该类(例如,如果您编写System.exit(1)而不是引发异常…它将不会编译!),则默认值将分配给静态变量(!)

我想说这仅仅是因为当你添加
抛出时,你基本上是在处理错误,所以编译器会说“哦,好吧,他可能知道他在做什么”。毕竟,它仍然会给出一个运行时错误。

有趣的是,无论字段是否标记为
static
,代码都会编译——在IntelliJ中,它会抱怨(但编译)静态字段,而不会对非静态字段说一句话

您是对的,JLS§8.1.3.2中有关于[静态]最终字段的特定规则。然而,关于final字段还有一些其他规则在这里起着重要作用,这些规则来自Java语言规范,它们指定了
final
字段的编译语义

但在我们进入那个蜡球之前,我们需要确定当我们看到
抛出时会发生什么-,强调我的:

throw语句导致抛出异常(§11)。结果是控制权的立即转移(§11.3),可能会退出多个语句和多个构造函数、实例初始值设定项、静态初始值设定项和字段初始值设定项求值,以及方法调用,直到找到捕捉抛出值的try语句(§14.20)。如果未找到此类try语句,则在调用线程所属线程组的uncaughtException方法后,终止执行抛出的线程(§17)的执行(§11.3)

用外行的话说——在运行时,如果遇到
抛出
语句,它可能会中断构造函数的执行(形式上是“突然完成”),导致对象无法构造,或者在不完整的状态下构造。这可能是一个安全漏洞,这取决于平台和构造函数的部分完整性

JVM期望§4.5给出的是:

宣布为最终决定;不得直接分配给对象构造后(JLS§17.5)

所以,我们有点麻烦了——我们期望这种行为在运行时发生,但在编译时不会发生。为什么IntelliJ在
public class StaticFinal
{

    public StaticFinal()
    {
        throw new InstantiationError("Can't instantiate!");
    }

    public static void main(String args[])
    {
        System.out.print(0);
    }

    private static final int i = 0;
}
public class StaticFinal {
    private final static int i ;
}
public class StaticFinal
{
    private final static int i ;

    public StaticFinal() 
    {
        throw new RuntimeException("Can't instantiate"); 
    }
}
// class version 51.0 (51)
// access flags 0x21
public class com/stackoverflow/sandbox/DecompileThis {

    // compiled from: DecompileThis.java

    // access flags 0x1A
    private final static I i = 10

    // access flags 0x1
    public <init>()V
            L0
    LINENUMBER 7 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
            L1
    LINENUMBER 9 L1
            RETURN // <- Pay close attention here.
    L2
    LOCALVARIABLE this Lcom/stackoverflow/sandbox/DecompileThis; L0 L2 0
    MAXSTACK = 1
    MAXLOCALS = 1
}
// class version 51.0 (51)
// access flags 0x21
public class com/stackoverflow/sandbox/DecompileThis {

  // compiled from: DecompileThis.java

  // access flags 0x1A
  private final static I i

  // access flags 0x1
  public <init>()V throws java/lang/InstantiationException 
   L0
    LINENUMBER 7 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
   L1
    LINENUMBER 8 L1
    NEW java/lang/InstantiationException
    DUP
    LDC "Nothin' doin'."
    INVOKESPECIAL java/lang/InstantiationException.<init> (Ljava/lang/String;)V
    ATHROW // <-- Eeek, where'd my RETURN instruction go?!
   L2
    LOCALVARIABLE this Lcom/stackoverflow/sandbox/DecompileThis; L0 L2 0
    MAXSTACK = 3
    MAXLOCALS = 1
}
public boolean trueOrDie(int val) {
    if(val > 0) {
        return true;
    } else {
        throw new IllegalStateException("Non-natural number!?");
    }
}