2011-05-13 70 views
5
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?我知道它是一个静态的最终变量,它的值不应该改变,但它有点难以理解编译器如何初始化这些字段。初始化Java中的静态最终字段

回答

3

这种情况是由JLS 8.3.2.3 “关于利用领域的限制初始化期间” 覆盖。

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

但是,规则不允许未初始化变量的某些用途;例如

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

被禁止。


回复其他答案。这不是Java编译器“无法解决”的情况。编译器严格执行Java语言规范指定的内容。 (换言之,编译器可能会编写来检测示例中的循环性,并将其称为编译错误。但是,如果这样做,它将拒绝有效的Java程序,因此不会是一个兼容的Java编译器。)


在评论你的国家,这一点:

...最终场总是必须在编译或在对象创建之前运行时进行初始化。

这是不正确的。

实际上有2种final领域:

  • 所谓的“恒变量”在编译时确实评估。 (常量变量是原始类型或类型字符串的变量“,它是最终的,并且使用编译时常量表达式” - 参见JLS 4.12.4“进行初始化)。当你访问它时,这样的字段总是会被初始化......模仿某些在这里不相关的并发症。

  • 其他final字段由JLS指定的顺序初始化,因此,可以看到该字段的值已初始化之前。 final变量的限制是它们必须初始化期间期间的类初始化(对于static)或期间对象初始化。


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

6

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

+0

这是一个面试问题不是一个真正的问题,我试图理解为什么编译器分配0只是因为alex1字段还没有initlialized呢??因为alex1是最终的,而alex代替alex1 = >> alex1是0? – Alexx 2011-05-13 09:02:20

+0

静态分配与成员变量不同,并且没有这种保证。评估每个静态分配,以便类加载器找到它。所以,就你而言,它首先通过调用getc()来指定alex。此时,alex1还没有初始化,所以返回0。 – stevevls 2011-05-13 09:10:00

+0

好吧,thx.另一个问题..如果它是在静态块中初始化的alex1字段,值为10,其他人打印什么?哪一个先执行?静态块还是初始化? – Alexx 2011-05-13 09:12:07

0

类变量不需要初始化,它们会自动设置为默认值。如果基元(如int,short ...)对象为0(零),则它是null。 因此,alex1设置为0. 方法变量必须被初始化,否则您将收到一个编译错误。

为了更好地解释阅读http://download.oracle.com/javase/tutorial/java/javaOO/classvars.html

+0

是的,但最终字段总是必须在编译或运行时在对象创建之前初始化。你sayid适用于普通字段而不是最终ones.ps它作品,我说在打印之前'0 10 10' – Alexx 2011-05-13 09:08:08

4

静态最终字段其值不被编译时间常数表达式中声明的顺序进行初始化。因此当alex初始化时,alex1尚未初始化,所以getc()返回默认值alex10)。

注意,结果将是不同的(10 10 10)在以下情况下:

​​

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

+0

+1对于编译时常量表达式。 (为什么'Integer.parseInt'?) – 2011-05-13 09:18:53

2

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

例如

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