Java Final变量是否具有默认值?

use*_*874 78 java final

我有一个这样的程序:

class Test {

    final int x;

    {
        printX();
    }

    Test() {
        System.out.println("const called");
    }

    void printX() {
        System.out.println("Here x is " + x);
    }

    public static void main(String[] args) {
        Test t = new Test();
    }

}
Run Code Online (Sandbox Code Playgroud)

如果我尝试执行它,我收到编译器错误:variable x might not have been initialized基于java默认值我应该得到以下输出权?

"Here x is 0".
Run Code Online (Sandbox Code Playgroud)

最终变量是否具有dafault值?

如果我改变我的代码,

class Test {

    final int x;

    {
        printX();
        x = 7;
        printX();
    }

    Test() {
        System.out.println("const called");
    }

    void printX() {
        System.out.println("Here x is " + x);
    }

    public static void main(String[] args) {
        Test t = new Test();
    }

}
Run Code Online (Sandbox Code Playgroud)

我得到输出为:

Here x is 0                                                                                      
Here x is 7                                                                                     
const called
Run Code Online (Sandbox Code Playgroud)

任何人都可以解释这种行为..

sp0*_*00m 61

http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html,"初始化实例成员"一章:

Java编译器将初始化程序块复制到每个构造函数中.

也就是说:

{
    printX();
}

Test() {
    System.out.println("const called");
}
Run Code Online (Sandbox Code Playgroud)

表现如下:

Test() {
    printX();
    System.out.println("const called");
}
Run Code Online (Sandbox Code Playgroud)

正如你可以由此看出,一旦实例被创建,最后一个字段没有被明确赋值,而(从http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html #jls-8.3.1.2):

必须在声明它的类的每个构造函数的末尾明确赋值空白的最终实例变量; 否则会发生编译时错误.

虽然似乎在文档explitely说(至少我一直没能找到它)没有,最后的字段必须暂时采取它的默认值的构造年底前,使之具有可预测值,如果你在分配之前阅读它.

默认值:http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5

在第二个片段中,x在实例创建时初始化,因此编译器不会抱怨:

Test() {
    printX();
    x = 7;
    printX();
    System.out.println("const called");
}
Run Code Online (Sandbox Code Playgroud)

另请注意,以下方法不起作用.只允许通过方法使用final变量的默认值.

Test() {
    System.out.println("Here x is " + x); // Compile time error : variable 'x' might not be initialized
    x = 7;
    System.out.println("Here x is " + x);
    System.out.println("const called");
}
Run Code Online (Sandbox Code Playgroud)

  • 这不能回答为什么不初始化final字段会导致编译错误. (2认同)
  • @justhalf的答案缺少一个关键点.您可以在默认状态下(通过方法)访问final,但如果在构造过程结束之前没有初始化它,编译器会抱怨.这就是为什么第二次尝试有效(它实际上是初始化x),但不是第一次尝试.如果您尝试直接访问空白决赛,编译器也会抱怨. (2认同)

uda*_*mik 27

JLS,你必须在构造函数中的默认值赋给空白最终实例变量(或初始化块,这是非常相同).这就是你在第一种情况下得到错误的原因.但是它并没有说你之前无法在构造函数中访问它.看起来很奇怪,但您可以在分配之前访问它并查看int - 0的默认值.

UPD.正如@ I4mpi所提到的,JLS 定义了在任何访问之前应该明确分配每个值的规则:

Each local variable (§14.4) and every blank final field (§4.12.4, §8.3.1.2) must have a definitely assigned value when any access of its value occurs.

但是,它在构造函数和字段方面也有一个有趣的规则:

If C has at least one instance initializer or instance variable initializer then V is [un]assigned after an explicit or implicit superclass constructor invocation if V is [un]assigned after the rightmost instance initializer or instance variable initializer of C.

因此,在第二种情况下,价值x明确赋值在构造函数的开头,因为它包含在它的最终分配.


alf*_*sin 7

如果你没有初始化,x你将得到编译时错误,因为x从未初始化.

声明x为final意味着它只能在构造函数或初始化块初始化(因为此块将由编译器复制到每个构造函数中).

0在变量初始化之前打印出来的原因是由于手册中定义的行为(请参阅:"默认值"部分):

默认值

声明字段时并不总是需要分配值.声明但未初始化的字段将由编译器设置为合理的默认值.一般来说,此默认值将为零或null,具体取决于数据类型.然而,依赖于这样的默认值通常被认为是糟糕的编程风格.

下表总结了上述数据类型的默认值.

Data Type   Default Value (for fields)
--------------------------------------
byte        0
short       0
int         0
long        0L
float       0.0f
double      0.0d
char        '\u0000'
String (or any object)      null
boolean     false
Run Code Online (Sandbox Code Playgroud)