Gar*_*son 6 java static-methods static-initializer
我可以从Java中的静态初始化程序调用静态方法吗?以下是否有效并保证按照Java规范工作?
public class Foo {
private final static int bar;
private static int generateValue() {
return 123;
}
static {
bar = generateValue();
}
}
Run Code Online (Sandbox Code Playgroud)
令我惊讶的是,我可能期望bar在里面可用generateValue().我知道静态初始化程序块的顺序很重要,但我没有听说静态方法声明的顺序很重要.但是,在执行静态初始化程序块之前,静态方法是否可用?
正如@Mureinik所说,“总之-是的。此代码完全合法。” 我想提供一个更彻底的答案,因为在将静态初始化程序与类方法结合使用时,您可以轻松地开发出问题-出现的顺序会影响类的状态,这是一个令人讨厌的错误。
初始化类时,将执行在类中声明的静态初始化器。与类变量的任何字段初始化程序一起使用...静态初始化程序可用于初始化类的类变量-Java语言规范(JLS)§8.7
初始化通常按照出现的顺序(称为文本顺序)进行。例如,考虑以下代码:
class Bar {
static int i = 1;
static {i += 1;}
static int j = i;
}
class Foo {
static int i = 1;
static int j = i;
static {i += 1;}
public static void main(String[] args) {
System.out.println("Foo.j = " + Foo.j);
System.out.println("Bar.j = " + Bar.j);
}
}
Run Code Online (Sandbox Code Playgroud)
对于价值Foo.j和Bar.j的,因为在代码中的文本排序的差异是不同的:
Foo.j = 1
Bar.j = 2
Run Code Online (Sandbox Code Playgroud)
OP的示例以文本顺序执行。但是,如果代码以相反的顺序重新排列,该怎么办:
class Foo {
static { bar = generateValue(); } //originally 3rd
private static int generateValue() { return 123; } //originally 2nd
private final static int bar; //originally 1st
public static void main(String[] args) {
System.out.println("Foo.bar = " + Foo.bar);
}
}
Run Code Online (Sandbox Code Playgroud)
事实证明,该编译没有错误。此外,输出为:Foo.bar = 123。因此,bar实际上包含123在运行时。但是,以下代码(来自JLS§8.3.1.1)会产生编译时错误,因为它尝试j在j声明之前进行访问:
//Don't do this!
class Z {
static { i = j + 2; } //Produces a compilation error
static int i, j;
static { j = 4; }
}
Run Code Online (Sandbox Code Playgroud)
有趣的是,没有以这种方式检查方法的访问,因此:
class Foo {
static int peek() { return j; }
static int i = peek();
static int j = 1;
public static void main(String[] args) {
System.out.println("Foo.i = " + Foo.i);
}
}
Run Code Online (Sandbox Code Playgroud)
产生以下输出:
Foo.i = 0
Run Code Online (Sandbox Code Playgroud)
这是因为
变量初始值设定项用于
i使用class方法在变量初始peek值设定项对其进行初始化j之前访问变量的值,此时j该变量初始值仍具有其默认值-JLS§8.3.2.3
如果相反,i则在之后 初始化j,则输出为
Foo.i = 1
Run Code Online (Sandbox Code Playgroud)
当使用对象而不是原始类型时,情况变得更糟,如下所示:
class Foo { //Don't do this
static int peek() { return j.hashCode(); } // NullPointerException here
static int i = peek();
static Object j = new Object();
public static void main(String[] args) {
System.out.println("Foo.i = " + Foo.i);
}
}
Run Code Online (Sandbox Code Playgroud)
这peek需要NullPointerException初始化一段时间i:
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.NullPointerException
at TestGame.Foo.peek(Foo.java:4)
at TestGame.Foo.<clinit>(Foo.java:5)
Run Code Online (Sandbox Code Playgroud)
运行上面的代码时,Eclipse弹出此窗口:
这绑回的OP,如果不是返回123,该generateValue()方法返回一些其他的静态字段(或方法)的值,那么价值bar取决于代码的文本排序。
那么,什么时候文本排序很重要?
并非总是使用文本顺序。有时,JVM会提前执行初始化。重要的是要知道何时可以进行提前查找,以及何时以文本顺序进行初始化。JLS 在第8.3.2.3节(强调我自己)中描述了初始化期间对字段的使用限制:
只有在成员是类或接口C的[a] ...静态字段且满足以下所有条件的情况下,成员的声明才需要使用文本形式出现:
- 用法发生在C的[a] ...静态变量初始值设定项或C的[a] ...静态初始值设定项中。
- 用法不在作业的左侧。
- 用法是通过一个简单的名称。
- C是包含用法的最里面的类或接口。
最后一点:常量首先被初始化(根据JLS§8.3.2.1):
在运行时,将首先初始化final并使用常量表达式初始化的静态字段(第15.28节)(第12.4.2节)。
| 归档时间: |
|
| 查看次数: |
1451 次 |
| 最近记录: |