下面的代码中,我有两个关于静态块和常量的问题。
- 常量(甚至是简单的静态变量)不能直接从静态块引用。它给出的错误是"在定义字段之前不能引用字段"。但是,当通过静态方法进行访问时,这是可以的
- 如果我在静态块的catch中为一个常量赋值,如下所述,它会给出一个错误,说"最终字段NAME可能已经被赋值了"。但如果在catch中执行asigning,则会出现错误,称"空白的最终字段NAME可能尚未初始化"
我想知道为什么会这样?
代码:
public class TestStaticblock {
static{
try {
// NAME = dummyStringValue() + NAME_APPENDER; // Cannot reference a field before it is defined
// NAME = dummyStringValue() + getNameAppender(); // This is OK
NAME = dummyStringValue();
} catch (Exception e) {
NAME = null; // The final field NAME may already have been assigned
}
}
private static String dummyStringValue() throws Exception{
return "dummy";
}
private static String getNameAppender() throws Exception{
return NAME_APPENDER;
}
private static final String NAME; // If I comment Catch it says "The blank final field NAME may not have been initialized"
private static String NAME_APPENDER = "appender";
}
只能分配给NAME
一次(因为它是final
)。将结果分配给一个临时变量,然后分配给NAME
(不要默默地吞下Exception
s)。类似
static {
String temp = null;
try {
temp = dummyStringValue();
} catch (Exception e) {
e.printStackTrace();
}
NAME = temp;
}
不能以当前方式分配NAME
的原因是编译器执行静态程序分析(特别是数据流分析),并检测到可能存在未分配NAME的代码路径。因为NAME是final
,所以这是一个编译错误。
在分配static final
字段之前,不能在静态块中使用它,但您可以通过调用方法来访问它。
例如,此代码打印null FOO
:
public class Main {
static final String FOO;
static {
foo();
FOO = "FOOFOO".substring(0, 3);
foo();
}
static void foo() {
System.out.println(FOO);
}
public static void main(String[] args) {}
}
不可否认,这很奇怪,但我想,如果让这样的事情变得不可能,语言会变得更加复杂。
至于你的第二个问题,这并不能编译。
static{
try {
NAME = dummyStringValue();
} catch (Exception e) {
NAME = null; // The final field NAME may already have been assigned
}
}
这也很奇怪。如果抛出异常,则它只能发生在方法dummyStringValue()
内部。由于不能在方法内为final
字段赋值,因此完全不可能在catch
块中已经为NAME
变量赋值。因此,不存在不分配NAME
的可能的代码路径。你会认为它应该以与相同的方式工作
static{
if (someCondition()) {
NAME = dummyStringValue();
} else {
NAME = null;
}
}
它编译得很好。
我想原因再次是,如果允许这样做,语言会变得更加复杂。允许它没有太大的好处,因为你可以只使用一个方法或temp
变量,如其他答案所示。异常只是比if
语句更复杂——它们的行为几乎就像goto
。@ElliotRisch在评论中提出了一个很好的观点。像这样的东西怎么样:
static{
try {
NAME1 = dummyStringValue1();
NAME2 = dummyStringValue2();
} catch (Exception e) {
// Has NAME1 been assigned here?
}
}
也许这对那些寻找类似东西的人会有所帮助。
Java有一个鲜为人知的功能(在JavaSpecialists从字段抛出异常中讨论过),如果您希望将final
实例变量(即NOTstatic
)初始化为抛出异常的方法调用的结果,则可以通过添加抛出异常的构造函数来避免明显的错误。请注意,此解决方案仅适用于非静态(而不是您所观察到的)。
public class TestStaticblock {
private final String NAME = dummyStringValue();
// Adding this removes the "unreported Exception" above.
public TestStaticblock() throws Exception {
}
private static String dummyStringValue() throws Exception {
return "dummy";
}
}
我个人最喜欢使用方法,而不是初始化单个变量的静态初始化器块:
private static final String NAME = getName();
private static String getName() {
try {
return something();
} catch (Exception e) {
return null;
}
}
- 你不会遇到像你所描述的那样的问题
- 您只能计算一个字段值,所以您不想在同一块中放入很多东西
- 您可以重新调用一个方法来测试它