简体   繁体   English

在Java中初始化静态final字段

[英]Initialization of static final fields in Java

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

Can someone tell me why this prints: 0 10 10 ? 有人能告诉我为什么会这样打印: 0 10 10 I understand that it's a static final variable and its value shouldn't change but it`sa little difficult to understand how the compiler initializes the fields. 我知道它是一个静态的最终变量,它的值不应该改变,但是理解编译器如何初始化字段有点困难。

It's an ordering problem. 这是一个订购问题。 Static fields are initialized in the order that they are encountered, so when you call getc() to inititalize the alex variable, alex1 hasn't been set yet. 静态字段按照遇到的顺序初始化,因此当您调用getc()以初始化alex变量时,尚未设置alex1。 You need to put initialization of alex1 first, then you'll get the expected result. 你需要先放置alex1的初始化,然后你才能获得预期的结果。

Static final fields whose values are not compile-time constant expressions are initialized in order of declaration. 其值不是编译时常量表达式的静态最终字段按声明顺序初始化。 Thus when alex in being initialized, alex1 is not initialized yet, so that getc() returns default values of alex1 ( 0 ). 因此,当初始化alex时, alex1尚未初始化,因此getc()返回默认值alex10 )。

Note that result will be different ( 10 10 10 ) in the following case: 请注意,在以下情况下,结果将有所不同( 10 10 10 ):

static final int alex1 = 10;

In this case alex1 is initialized by a compile-time constant expression, therefore it's initialized from the very beginning. 在这种情况下, alex1由编译时常量表达式初始化,因此它从一开始就被初始化。

This situation is covered by JLS 8.3.2.3 "Restrictions on the use of Fields during Initialization". JLS 8.3.2.3 “初始化期间使用字段的限制”涵盖了这种情况。

The JLS rules allows the usage in your Question, and state that the first call to getc() will return default (uninitialized) value of alex . JLS规则允许在您的问题中使用,并声明第一次调用getc()将返回alex默认(未初始化)值。

However, the rules disallow some uses of uninitialized variables; 但是,规则不允许使用未初始化的变量; eg 例如

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

is disallowed. 是不被允许的。


Re some of the other answers. 重新回答其他一些问题。 This is not a case where the Java compiler "can't figure it out". 这不是Java编译器“无法弄清楚”的情况。 The compiler is strictly implementing what the Java Language Specification specifies. 编译器严格执行Java语言规范指定的内容。 (Or to put it another way, a compiler could be written to detect the circularity in your example and call it a compilation error. However, if it did this, it would be rejecting valid Java programs, and therefore wouldn't be a conformant Java compiler.) (或者换一种说法,编译器被写入到检测圆你的榜样,并把它编译错误。但是,如果这样做,将被拒绝有效的Java程序,因此不会成为符合Java编译器。)


In a comment you state this: 在评论中你说明了这一点:

... final fields always must be initialized at compile or at runtime before the object creation. ...在创建对象之前,必须在编译时或运行时初始化final字段。

This is not correct. 这是不正确的。

There are actually two kinds of final fields: 实际上有两种final字段:

  • A so-called "constant variable" is indeed evaluated at compile time. 确实在编译时评估了所谓的“常量变量”。 (A constant variable is a variable "of primitive type or type String, that is final and initialized with a compile-time constant expression" - see JLS 4.12.4.). (常量变量是原始类型或类型String的变量,是最终的并使用编译时常量表达式初始化“ - 参见JLS 4.12.4。)。 Such a field will always have been initialized by the time you access it ... modulo certain complications that are not relevant here. 这样的字段将始终在您访问它时进行初始化...模拟某些与此无关的并发症。

  • Other final fields are initialized in the order specified by the JLS, and it is possible to see the field's value before it has been initialized. 其他final字段由JLS指定的顺序初始化,并且可以看到该字段的值已初始化之前。 The restriction on final variables is that they must be initialized once and only once during class initialization (for a static ) or during object initialization. final变量的限制是它们必须类初始化期间 (对于static )或对象初始化期间初始化一次且仅初始化一次。


Finally, this stuff is very much "corner case" behavior. 最后,这个东西是非常“极端情况”的行为。 A typical well-written class won't need to access a final field before it has been initialized. 一个典型的编写良好的类在初始化之前不需要访问final字段。

There is nothing special about static fields, it just that the compiler cannot workout that you are using a method which can access a field before its initialised. 静态字段没有什么特别之处,只是编译器无法使用可以在初始化之前访问字段的方法进行锻炼。

eg 例如

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

prints 版画

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

Class variables are not necessary to initialize, they are automatically set to their default values. 初始化不需要类变量,它们会自动设置为默认值。 If primitives (like int, short...) it's 0 (zero) for Objects it's null . 如果是原语(比如int,short ...),那么对象的0(零)是null Therefore alex1 is set to 0. Method variables must be initialized, otherwise you will get an compiliation error. 因此,alex1设置为0.必须初始化方法变量,否则您将收到编译错误。

For a better explanation read http://download.oracle.com/javase/tutorial/java/javaOO/classvars.html 有关更好的解释,请阅读http://download.oracle.com/javase/tutorial/java/javaOO/classvars.html

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM