在Java中初始化静态final字段

时间:2011-05-13 08:58:01

标签: java initialization field final

public class Main {
 static final int alex=getc();
 static final int alex1=Integer.parseInt("10");
 static final int alex2=getc();

public static int getc(){
    return alex1;
}

public static void main(String[] args) {
    final Main m = new Main();
    System.out.println(alex+" "+alex1 +" "+alex2);
  } 
}

有人可以告诉我为什么要打印:0 10 10?我知道它是一个静态的最终变量,它的值不应该改变,但是理解编译器如何初始化字段有点困难。

5 个答案:

答案 0 :(得分:6)

这是一个订购问题。静态字段按照遇到的顺序初始化,因此当您调用getc()以初始化alex变量时,尚未设置alex1。你需要首先放置alex1的初始化,然后你才能获得预期的结果。

答案 1 :(得分:4)

其值不是编译时常量表达式的静态final字段按声明顺序初始化。因此,在初始化alex时,alex1尚未初始化,因此getc()会返回默认值alex10)。

请注意,在以下情况中,结果会有所不同(10 10 10):

static final int alex1 = 10;

在这种情况下,alex1由编译时常量表达式初始化,因此它从一开始就被初始化。

答案 2 :(得分:4)

JLS 8.3.2.3“初始化期间使用字段的限制”涵盖了这种情况。

JLS规则允许在您的问题中使用,并声明第一次调用getc()将返回alex的默认值(未初始化)。

但是,规则不允许使用未初始化的变量; e.g。

int i = j + 1;
int j = i + 1;

是不允许的。


重新提出其他一些答案。这不是Java编译器“无法弄清楚”的情况。编译器严格执行Java语言规范指定的内容。 (或者换句话说,编译器可以编写以检测示例中的循环,并将其称为编译错误。但是,如果它这样做,它将拒绝有效的Java程序,并且因此,它不是一个符合Java的编译器。)


在评论中你说明了这一点:

  

...在创建对象之前,必须在编译时或运行时初始化最终字段。

这不正确。

实际上有两种final字段:

  • 确实在编译时评估了所谓的“常量变量”。 (常量变量是原始类型或类型String的变量“,它是final,并使用编译时常量表达式”初始化 - 请参阅JLS 4.12.4。)。这样的字段将始终在您访问它时进行初始化...模拟某些与此无关的并发症。

  • 其他final字段按照JLS指定的顺序进行初始化, 可以在初始化之前查看字段的值。对final变量的限制是它们必须在类初始化期间初始化一次(对于static)或对象初始化期间


最后,这个东西是非常“极端情况”的行为。一个典型的写得好的课程不需要 在初始化之前访问final字段。

答案 3 :(得分:2)

静态字段没有什么特别之处,只是编译器无法使用可以在初始化之前访问字段的方法进行锻炼。

e.g。

public class Main {
    private final int a;

    public Main() {
        System.out.println("Before a=10, a="+getA());
        this.a = 10;
        System.out.println("After a=10, a="+getA());
    }

    public int getA() {
        return a;
    }

    public static void main(String... args) {
        new Main();
    }
}

打印

Before a=10, a=0
After a=10, a=10

答案 4 :(得分:0)

初始化不需要类变量,它们会自动设置为默认值。如果是原语(比如int,short ...),那么对象的{0}(0)为null。 因此alex1设置为0。 必须初始化方法变量,否则会出现编译错误。

要获得更好的解释,请阅读http://download.oracle.com/javase/tutorial/java/javaOO/classvars.html