Java编译器允许使用'this'关键字访问未初始化的空白最终字段?这是一个错误吗?

时间:2014-06-16 09:14:17

标签: java initialization javac final

我写了这段代码,似乎编译器允许在使用'这个'访问时访问未初始化的空白最终字段。关键字:

public class TestClass
{
    public final int value1;
    public int value2;

    TestClass(int value) {
        value2 = 2 + this.value1; // access final field using 'this' before initialization gives no compiler error
        //value2 = 2 + value1;      // uncomment it gives compile time error - variable value1 might not have been initialized
        value1 = value;
    }

    public static void main(String args[]) {
    TestClass tc = new TestClass(10);
    System.out.println("\nTestClass Values : value1 =  " + tc.value1 + " , value2 =  " + tc.value2);
    }
}

我尝试在1.5,1.6和&amp ;;上编译它。 1.7并且在所有这三个中得到了相同的结果。

对我来说,它看起来像编译器错误,因为编译器必须在这种情况下抛出错误,但是使用'这个'关键字它不会因此而产生编码错误的范围,因为它不会被程序员忽视,因为不会抛出编译时或运行时错误。

为什么不是重复 的少数几点   - 所有答案都解释了它是如何工作的以及JLS所说的,很好,但我的真正意图应该放在第一位吗?
  - 我的问题更多来自程序员的观点,而不是语言语义

3 个答案:

答案 0 :(得分:1)

在Java中,默认值为0,所以即使你没有初始化它,编译器仍然将0指定为值。

  

声明但未初始化的字段将由编译器设置为合理的默认值。一般来说,此默认值为零或null,具体取决于数据类型。

来源:http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html

请注意,这不适用于局部变量,只有字段变量具有默认值。

答案 1 :(得分:1)

这不是错误。这是Java Specification 1.6及更低版本的一个特性。

可以在代码的任何部分访问最终字段。它没有任何限制。

最终的唯一限制是必须在创建类实例之前初始化它。

使用this时,表示它是正在构造的对象的元素。

由于规范1.7 because of change,我们可能会说这是编译器的某些实现中的错误。

但是从1.8开始,代码会为this.value1value1生成相同的错误。

答案 2 :(得分:0)

我使用了以下代码

public class HelloWorld {

    public final int value1;
    public int value2;
    public HelloWorld(int value){
        System.out.println(this.value1);
        System.out.println(this.value2);
        value1 = value;
    }

    public static void main(String args[]) {
    }
}

它生成的字节码是

Compiled from "HelloWorld.java"
public class test.HelloWorld extends java.lang.Object{
public final int value1;

public int value2;

public test.HelloWorld(int);
  Code:
   0:   aload_0
   1:   invokespecial   #11; //Method java/lang/Object."<init>":()V
   4:   getstatic       #14; //Field java/lang/System.out:Ljava/io/PrintStream;
   7:   aload_0
   8:   getfield        #20; //Field value1:I
   11:  invokevirtual   #22; //Method java/io/PrintStream.println:(I)V
   14:  aload_0
   15:  iload_1
   16:  putfield        #20; //Field value1:I
   19:  return

public static void main(java.lang.String[]);
  Code:
   0:   return

}

如果您注意到JVM在最终的情况下进行getstatic调用,在正常实例变量的情况下进行getfield。现在,如果你看一下它所说的specs for getstatic

  

成功解析字段后,如果尚未初始化该类或接口,则初始化声明已解析字段的类或接口(第5.5节)。

因此,如果您将此函数与最终变量一起使用,则将其初始化。不是它会初始化this.value1而不是value1这是最终的。在使用之前,您仍需要对其进行初始化。