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

时间:2014-07-28 07:49:34

标签: 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();
    }

}

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

"Here x is 0".

最终变量是否具有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();
    }

}

我的输出为:

Here x is 0                                                                                      
Here x is 7                                                                                     
const called

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

8 个答案:

答案 0 :(得分:60)

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

  

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

也就是说:

{
    printX();
}

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

的行为完全如下:

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

正如您可以看到的那样,创建实例后,最终字段不是definitely assigned,而(来自http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.2):

  

必须明确指定空白的最终实例变量   它所在类的每个构造函数的结尾   声明;否则会发生编译时错误。

虽然在文档中似乎没有明确说明(至少我无法找到它),但是最终字段必须在构造函数结束之前临时获取其默认值,以便它具有{ {3}}如果你在分配之前阅读它。

默认值:predictable value

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

Test() {
    printX();
    x = 7;
    printX();
    System.out.println("const called");
}

另请注意,以下方法不起作用。使用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");
}

答案 1 :(得分:27)

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

UPD。正如@ I4mpi, JLS defines所述,在任何访问权限之前,每个值应明确分配的规则:

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.

但是,它在构造函数和字段方面也有interesting rule

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在构造函数的开头是明确赋值,因为它包含最后的赋值。

答案 2 :(得分:7)

如果您没有初始化x,那么您将收到编译时错误,因为x永远不会被初始化。

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

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

默认值

  

声明字段时并不总是需要指定值。   声明但未初始化的字段将设置为a   编译器合理默认。一般来说,这是默认的   将为零或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

答案 3 :(得分:4)

第一个错误是编译器抱怨你有一个final字段,但没有代码来初始化它 - 很简单。

在第二个示例中,您有代码为其分配值,但执行顺序意味着您在分配之前和之后引用该字段。

任何字段的预先指定的值都是默认值。

答案 4 :(得分:2)

类的所有非final字段初始化为默认值(0表示数字数据类型,false表示布尔值,null表示引用类型,有时称为复杂对象。这些字段在构造函数(或实例初始化块)执行之前初始化,而不管是否在构造函数之前或之后声明了字段。

类的最终字段具有无默认值,并且必须在类构造函数完成其作业之前显式初始化一次。

执行块内部的局部变量(例如,方法)没有默认值。这些字段必须在首次使用之前显式初始化,并且局部变量是否标记为最终字段并不重要。

答案 5 :(得分:1)

据我所知,编译器总是将类变量初始化为默认值(甚至是最终变量)。例如,如果要将int初始化为自身,则int将设置为其默认值0.参见下文:

class Test {
    final int x;

    {
        printX();
        x = this.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();
    }
}

以上将打印以下内容:

Here x is 0
Here x is 0
const called

答案 6 :(得分:1)

让我用最简单的话来说明。

需要初始化

final个变量,这是语言规范要求的。 话虽如此,但请注意,在声明时没有必要对其进行初始化。

需要在初始化对象之前初始化它。

我们可以使用初始化块来初始化最终变量。现在,初始化块有两种类型 staticnon-static

您使用的块是非静态初始化块。因此,当您创建一个对象时,Runtime将调用构造函数,而后者将调用父类的构造函数。

之后,它将调用所有初始值设定项(在您的情况下是非静态初始值设定项)。

在您的问题中,案例1 :即使在初始化程序块完成后,最终变量仍然未初始化,这是编译器将检测到的错误。

case 2 中:初始化器将初始化最终变量,因此编译器知道在初始化对象之前,final已经初始化。因此,它不会抱怨。

现在问题是,为什么x取零。这里的原因是编译器已经知道没有错误,因此在调用init方法时,所有的决定都将初始化为默认值,并设置一个标志,它们可以在类似于x=7的实际赋值语句中更改。 请参阅下面的init调用:

enter image description here

答案 7 :(得分:1)

  

如果我尝试执行它,我收到编译器错误:变量x可能没有基于java默认值初始化我应该得到以下输出吗?

     

“这里x是0”。

没有。您没有看到该输出,因为您首先得到编译时错误。最终变量确实得到一个默认值,但是Java语言规范(JLS)要求你在构造函数的末尾初始化它们(LE:我在这里包括初始化块),否则你会得到一个编译时错误将阻止您的代码被编译和执行。

您的第二个示例尊重要求,这就是为什么(1)您的代码编译和(2)您获得预期的行为。

将来尝试熟悉JLS。关于Java语言没有更好的信息来源。