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

时间:2022-09-25 14:08:12

I have a program like this:

我有一个这样的程序:

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

}

If I try to execute it, i am getting compiler error as : variable x might not have been initialized based on java default values i should get the below output right??

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

"Here x is 0".

Will final variables have dafault values?

最终变量是否具有dafault值?

if I change my code like this,

如果我改变我的代码,

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

}

I am getting output as:

我得到输出为:

Here x is 0                                                                                      
Here x is 7                                                                                     
const called

Can anyone please explain this behavior..

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

8 个解决方案

#1


57  

http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html, chapter "Initializing Instance Members":

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

The Java compiler copies initializer blocks into every constructor.

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

That is to say:

也就是说:

{
    printX();
}

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

behaves exactly like:

表现如下:

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

As you can thus see, once an instance has been created, the final field has not been definitely assigned, while (from http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.2):

正如您所看到的,一旦创建了一个实例,最终字段就没有明确赋值,而(来自http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html #JLS-8.3.1.2):

A blank final instance variable must be definitely assigned at the end of every constructor of the class in which it is declared; otherwise a compile-time error occurs.

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

While it does not seem to be stated explitely in the docs (at least I have not been able to find it), a final field must temporary take its default value before the end of the constructor, so that it has a predictable value if you read it before its assignment.

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

Default values: http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5

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

On your second snippet, x is initialized on instance creation, so the compiler does not complain:

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

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

Also note that the following approach doesn't work. Using default value of final variable is only allowed through a method.

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

#2


27  

JLS is saying that you must assign the default value to blank final instance variable in constructor (or in initialization block which is pretty the same). That is why you get the error in the first case. However it doesn't say that you can not access it in constructor before. Looks weird a little bit, but you can access it before assignment and see default value for int - 0.

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

UPD. As mentioned by @I4mpi, JLS defines the rule that each value should be definitely assigned before any access:

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.

当对其值进行任何访问时,每个局部变量(第14.4节)和每个空白最终字段(第4.12.4节,第8.3.1.2节)必须具有明确赋值。

However, it also has an interesting rule in regards to constructors and fields:

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

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.

如果C具有至少一个实例初始化器或实例变量初始化器,则在显式或隐式超类构造函数调用之后,如果在最右边的实例初始化器或C的实例变量初始化器之后对V进行[un]分配,则对其进行[un]赋值。

So in second case the value x is definitely assigned at the beginning of the constructor, because it contains the assignment at the end of it.

所以在第二种情况下,值x在构造函数的开头是明确赋值的,因为它包含在它结尾的赋值。

#3


7  

If you don't initialize x you'll get a compile-time error since x is never initialized.

如果你没有初始化x,你将得到一个编译时错误,因为x永远不会被初始化。

Declaring x as final means that it can be initialized only in the constructor or in initializer-block (since this block will be copied by the compiler into every constructor).

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

The reason that you get 0 printed out before the variable is initialized is due to the behavior defined in the manual (see: "Default Values" section):

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

Default Values

It's not always necessary to assign a value when a field is declared. Fields that are declared but not initialized will be set to a reasonable default by the compiler. Generally speaking, this default will be zero or null, depending on the data type. Relying on such default values, however, is generally considered bad programming style.

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

The following chart summarizes the default values for the above data types.

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

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

#4


4  

The first error is the compiler complaining that you have a final field, but no code to initialize it - simple enough.

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

In the second example, you have code to assign it a value, but the sequence of execution means you reference the field both before and after assigning it.

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

The pre-assigned value of any field is the default value.

任何字段的预分配值都是默认值。

#5


2  

All non-final fields of a class initialize to a default value (0 for nummeric data types, false for boolean and null for reference types, sometimes called complex objects). These fields initialize before a constructor (or instance initialization block) executes independent of whether or not the fields was declared before or after the constructor.

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

Final fields of a class has no default value and must be explicitly initialized just once before a class constructor has finished his job.

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

Local variables on the inside of an execution block (for example, a method) has no default value. These fields must be explicitly initialized before their first use and it doesn't matter whether or not the local variable is marked as final.

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

#6


1  

Let me put it in the simplest words I can.

让我用最简单的话来说。

final variables need to be initialized, this is mandated by the Language Specification. Having said that, please note that it is not necessary to initialize it at the time of declaration.

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

It is required to initialize that before the object is initialized.

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

We can use initializer blocks to initialize the final variables. Now, initializer blocks are of two types static and non-static

我们可以使用初始化块来初始化最终变量。现在,初始化块有静态和非静态两种类型

The block you used is a non-static initializer block. So, when you create an object, Runtime will invoke constructor and which in turn will invoke the constructor of the parent class.

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

After that, it will invoke all the initializers (in your case the non-static initializer).

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

In your question, case 1: Even after the completion of initializer block the final variable remains un-initialized, which is an error compiler will detect.

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

In case 2: The initializer will initialize the final variable, hence the compiler knows that before the object is initialized, the final is already initialized. Hence, it will not complain.

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

Now the question is, why does x takes a zero. The reason here is that compiler already knows that there is no error and so upon invocation of init method all the finals will be initialized to defaults, and a flag set that they can change upon actual assignment statement similar to x=7. See the init invocation below:

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

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

#7


1  

As far as I'm aware, the compiler will always initialize class variables to default values (even final variables). For example, if you were to initialize an int to itself, the int would be set to its default of 0. See below:

据我所知,编译器总是将类变量初始化为默认值(甚至是最终变量)。例如,如果要将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();
    }
}

The above would print the following:

以上将打印以下内容:

Here x is 0
Here x is 0
const called

#8


1  

If I try to execute it, i am getting compiler error as : variable x might not have been initialized based on java default values i should get the below output right??

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

"Here x is 0".

“这里x是0”。

No. You are not seeing that output because you are getting a compile-time error in the first place. Final variables do get a default value, but the Java Language Specification (JLS) requires you to initialize them by the end of the constructor (LE: I'm including initialization blocks here), otherwise you'll get a compile-time error which will prevent your code to be compiled and executed.

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

Your second example respects the requirement, that's why (1) your code compiles and (2) you get the expected behaviour.

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

In the future try to familiarize yourself with the JLS. There's no better source of information about the Java language.

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

#1


57  

http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html, chapter "Initializing Instance Members":

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

The Java compiler copies initializer blocks into every constructor.

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

That is to say:

也就是说:

{
    printX();
}

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

behaves exactly like:

表现如下:

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

As you can thus see, once an instance has been created, the final field has not been definitely assigned, while (from http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.2):

正如您所看到的,一旦创建了一个实例,最终字段就没有明确赋值,而(来自http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html #JLS-8.3.1.2):

A blank final instance variable must be definitely assigned at the end of every constructor of the class in which it is declared; otherwise a compile-time error occurs.

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

While it does not seem to be stated explitely in the docs (at least I have not been able to find it), a final field must temporary take its default value before the end of the constructor, so that it has a predictable value if you read it before its assignment.

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

Default values: http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5

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

On your second snippet, x is initialized on instance creation, so the compiler does not complain:

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

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

Also note that the following approach doesn't work. Using default value of final variable is only allowed through a method.

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

#2


27  

JLS is saying that you must assign the default value to blank final instance variable in constructor (or in initialization block which is pretty the same). That is why you get the error in the first case. However it doesn't say that you can not access it in constructor before. Looks weird a little bit, but you can access it before assignment and see default value for int - 0.

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

UPD. As mentioned by @I4mpi, JLS defines the rule that each value should be definitely assigned before any access:

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.

当对其值进行任何访问时,每个局部变量(第14.4节)和每个空白最终字段(第4.12.4节,第8.3.1.2节)必须具有明确赋值。

However, it also has an interesting rule in regards to constructors and fields:

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

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.

如果C具有至少一个实例初始化器或实例变量初始化器,则在显式或隐式超类构造函数调用之后,如果在最右边的实例初始化器或C的实例变量初始化器之后对V进行[un]分配,则对其进行[un]赋值。

So in second case the value x is definitely assigned at the beginning of the constructor, because it contains the assignment at the end of it.

所以在第二种情况下,值x在构造函数的开头是明确赋值的,因为它包含在它结尾的赋值。

#3


7  

If you don't initialize x you'll get a compile-time error since x is never initialized.

如果你没有初始化x,你将得到一个编译时错误,因为x永远不会被初始化。

Declaring x as final means that it can be initialized only in the constructor or in initializer-block (since this block will be copied by the compiler into every constructor).

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

The reason that you get 0 printed out before the variable is initialized is due to the behavior defined in the manual (see: "Default Values" section):

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

Default Values

It's not always necessary to assign a value when a field is declared. Fields that are declared but not initialized will be set to a reasonable default by the compiler. Generally speaking, this default will be zero or null, depending on the data type. Relying on such default values, however, is generally considered bad programming style.

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

The following chart summarizes the default values for the above data types.

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

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

#4


4  

The first error is the compiler complaining that you have a final field, but no code to initialize it - simple enough.

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

In the second example, you have code to assign it a value, but the sequence of execution means you reference the field both before and after assigning it.

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

The pre-assigned value of any field is the default value.

任何字段的预分配值都是默认值。

#5


2  

All non-final fields of a class initialize to a default value (0 for nummeric data types, false for boolean and null for reference types, sometimes called complex objects). These fields initialize before a constructor (or instance initialization block) executes independent of whether or not the fields was declared before or after the constructor.

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

Final fields of a class has no default value and must be explicitly initialized just once before a class constructor has finished his job.

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

Local variables on the inside of an execution block (for example, a method) has no default value. These fields must be explicitly initialized before their first use and it doesn't matter whether or not the local variable is marked as final.

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

#6


1  

Let me put it in the simplest words I can.

让我用最简单的话来说。

final variables need to be initialized, this is mandated by the Language Specification. Having said that, please note that it is not necessary to initialize it at the time of declaration.

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

It is required to initialize that before the object is initialized.

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

We can use initializer blocks to initialize the final variables. Now, initializer blocks are of two types static and non-static

我们可以使用初始化块来初始化最终变量。现在,初始化块有静态和非静态两种类型

The block you used is a non-static initializer block. So, when you create an object, Runtime will invoke constructor and which in turn will invoke the constructor of the parent class.

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

After that, it will invoke all the initializers (in your case the non-static initializer).

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

In your question, case 1: Even after the completion of initializer block the final variable remains un-initialized, which is an error compiler will detect.

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

In case 2: The initializer will initialize the final variable, hence the compiler knows that before the object is initialized, the final is already initialized. Hence, it will not complain.

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

Now the question is, why does x takes a zero. The reason here is that compiler already knows that there is no error and so upon invocation of init method all the finals will be initialized to defaults, and a flag set that they can change upon actual assignment statement similar to x=7. See the init invocation below:

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

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

#7


1  

As far as I'm aware, the compiler will always initialize class variables to default values (even final variables). For example, if you were to initialize an int to itself, the int would be set to its default of 0. See below:

据我所知,编译器总是将类变量初始化为默认值(甚至是最终变量)。例如,如果要将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();
    }
}

The above would print the following:

以上将打印以下内容:

Here x is 0
Here x is 0
const called

#8


1  

If I try to execute it, i am getting compiler error as : variable x might not have been initialized based on java default values i should get the below output right??

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

"Here x is 0".

“这里x是0”。

No. You are not seeing that output because you are getting a compile-time error in the first place. Final variables do get a default value, but the Java Language Specification (JLS) requires you to initialize them by the end of the constructor (LE: I'm including initialization blocks here), otherwise you'll get a compile-time error which will prevent your code to be compiled and executed.

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

Your second example respects the requirement, that's why (1) your code compiles and (2) you get the expected behaviour.

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

In the future try to familiarize yourself with the JLS. There's no better source of information about the Java language.

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