此问题来自我的Java test
学习指南。有人可以解释为什么会这样吗?
此代码打印出数字5而不是12.您能解释原因吗?如果第二个变量也是final
,你可以解释为什么它会打印12,如果它们都不是final
,你能解释为0吗?
public class Question26 {
public static void main(String[] args) {
System.out.println(Q26.q26.ans);
}
}
class Q26 {
public static Q26 q26 = new Q26();
public int ans;
private static final int var1 = 5;
private static int var2 = 7;
public Q26() {
ans = var1 + var2;
}
}
答案 0 :(得分:21)
要知道您声明static
字段的位置的一件事是按顺序初始化它们;你不能写:
public class DoesNotCompile
{
private static final int foo = 1 + bar; // ERROR: bar is not defined
private static final int bar = 1;
然而,在你的情况下,事情会有所不同:
class Q26 {
// Declared first, but NOT first to be initialized...
public static Q26 q26 = new Q26();
public int ans;
// The honor befalls to this one, since it is declared `final`
private static final int var1 = 5;
private static int var2 = 7; // zero until initialized
public Q26() {
ans = var1 + var2;
}
}
非初始化int
的默认值为0;由于Q26
实例在var1
和var2
之前声明,但是,因为var1
首先被初始化(因为它是final
) ,结果是你看到的:ans
是5。
此代码的等效内容可能是:
class Q26 {
public static Q26 q26;
private static final int var1;
private static int var2;
static {
var1 = 5;
q26 = new Q26();
var2 = 7;
}
public int ans;
public Q26() {
ans = var1 + var2;
}
}
进一步说明:还有静态初始化块;并为这些事项订购事宜。你做不到:
public class DoesNotCompileEither
{
static {
foo = 3; // ERROR: what is foo?
}
private static final int foo;
如果你将静态初始值设定项放在下面的foo
声明,那么这将编译:
public class ThisOneCompiles
{
private static final int foo; // declared
static {
foo = 3; // initialized
}
答案 1 :(得分:6)
您班上的静态成员不会同时神奇地初始化。在幕后,Java必须设置它们 - 它按照你声明它们的顺序进行设置,但final
除外。
让我们重写一下,这样问题就会变得更加清晰:
public class Question26 {
public static void main(String[] args) {
System.out.println(Q26.q26.ans);
}
}
class Q26 {
public static Q26 q26;
public int ans;
private static final int var1 = 5;
private static int var2;
static {
q26 = new Q26();
var2 = 7;
}
public Q26() {
ans = var1 + var2;
}
}
此处非final static
成员的声明及其初始化已分开。将初始化放在静态块中会使得这些成员的初始化与其他任何成员的初始化更加明显,并且具有执行顺序。
但为什么final
变量在q26
之前初始化?查看this answer和Java specification,似乎var1
可能会被视为compile-time constant expression。即使它没有被这样处理,只要它被声明并在一个语句中初始化,var1
应该在非final
变量之前由运行时初始化:< / p>
在运行时,首先初始化使用常量表达式初始化的静态字段。这也适用于接口中的这些字段。这些字段是&#34;常量&#34;即使是狡猾的程序,也永远不会被观察到它们的默认初始值。
引文:http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.2.1
那么,如果我们发疯,将static final
成员var1
的声明和初始化分开怎么办?在这种情况下,我们实际上可以使您的程序编译并执行,只输出0
- 这与您在问题中所做的一些断言相矛盾。
public class Question26 {
public static void main(String[] args) {
System.out.println(Q26.q26.ans);
}
}
class Q26 {
public static Q26 q26;
public int ans;
private static final int var1;
private static int var2;
static {
q26 = new Q26();
var1 = 5;
var2 = 7;
}
public Q26() {
ans = var1 + var2;
}
}
所以不要误以为你用来声明变量的关键字确保一些特定的执行顺序!
答案 2 :(得分:3)
这是因为静态成员的初始化顺序,它们将初始化它们声明的文本顺序。在该对象变量var1
之前定义int变量var2
和q26
,如下所示
class Q26 {
private static final int var1 = 5;
private static int var2 = 7;
public static Q26 q26 = new Q26();
public int ans;
public Q26() {
ans = var1 + var2;
}
}
现在输出为
在您的情况下,对象q26
在变量var2
之前初始化,但var1
是最终变量,在编译时知道该值。所以你得到了答案
答案 3 :(得分:1)
来自http://docs.oracle.com/javase/tutorial/java/javaOO/classvars.html
如果原始类型或字符串被定义为常量而且 值在编译时已知,编译器替换常量 在代码中随处可以命名其值。这被称为a 编译时常量。如果在外面常量的值 世界的变化(例如,如果它实际上是立法的话 应该是3.975),你需要重新编译任何使用它的类 常数以获得当前值。
因此,尽管在静态q26
之后定义了该变量,但编译器已将每个地点var1
替换为5。