Java 静态初始值设定项在定义字段之前无法引用该字段
我有以下注释错误的代码Java 静态初始值设定项在定义字段之前无法引用该字段,java,static-initialization,static-initializer,Java,Static Initialization,Static Initializer,我有以下注释错误的代码 public final class MyStaticClass { private MyStaticClass() {} static { a = new A(); b = new B(a); // Cannot access a field before it is defined } private static final A a; private static final B
public final class MyStaticClass {
private MyStaticClass() {}
static {
a = new A();
b = new B(a); // Cannot access a field before it is defined
}
private static final A a;
private static final B b;
}
我对使用静态初始值设定项相当陌生,但我不知道为什么它不能编译。我看过一些关于这个主题的帖子,看到了初始化的顺序,但这似乎并没有违反规则。在初始化b时,a应该已经被初始化了
我有一个解决办法,就是将这个类设置为单例,但是这样做会使代码的可读性降低一点。我很想知道这里出了什么问题。如果静态块还没有声明,就不能在静态块中使用
a
。所以在静态块之前声明它:
public final class MyStaticClass {
private MyStaticClass() {}
private static final A a;
private static final B b;
static {
a = new A();
b = new B(a);
}
}
(我假设调用
A
的实例“b”和b
的实例“A”是一个输入错误。)静态初始化是按照写入代码的顺序完成的。因此,在您的代码中,它将首先进入静态块,然后进入变量声明
在声明变量之前不能使用它,这就是为什么它不能编译
public final class MyStaticClass {
private static final A a;
private static final B b;
private MyStaticClass() {}
static {
a = new A();
b = new B(a); // Cannot access a field before it is defined
}
}
}
这一点在本文中进行了解释。事实上,有几种方法可以解决这个问题
使用限定名a
:
// #1
public final class MyStaticClass {
static {
a = new A();
b = new B(MyStaticClass.a);
}
private static final A a;
private static final B b;
}
如果a
和b
是在实例初始值设定项中初始化的实例字段,a
可以限定为this.a
将前向引用置于作业左侧的a
:
// #2
public final class MyStaticClass {
static {
b = new B(a = new A());
}
private static final A a;
private static final B b;
}
当然,将声明文本置于引用之前:
// #3
public final class MyStaticClass {
private static final A a;
private static final B b;
static {
a = new A();
b = new B(a);
}
}
根据JLS,#3在技术上不是必需的(“这些类变量在范围内”),而是设计用于捕捉字段初始化顺序错误的特定类型的错误:
public final class MyStaticClass {
private static final B b = new B(a); // a is null
private static final A a = new A();
}
(尽管我刚才向您展示了两种方法来阻止它,并且无论如何都会犯错误。)
我推荐1号或3号,因为2号有点深奥。您似乎没有犯此规则旨在捕捉的错误。查看您的变量名,我想起了
public static final int ONE=2代码>:P哎呀。很抱歉,我对这个评论更感到困惑。那么在类中物理上更高的位置声明字段意味着它可以编译?为什么?我以为整个文件在编译之前都被标记了。为什么A必须是B的超类?B有一个采用a类型的构造函数(B内部有一个对a的引用)。你在写变量时把事情弄糟了,这是我误解的。。。现在检查一下编辑,这确实有效。我刚才在另一篇评论中说,你知道这为什么有效吗?我认为编译器会在编译之前标记整个文件,因此它应该知道声明的字段是什么,而不考虑顺序。还有一个问题提到在调用初始值设定项之前初始化字段。你知道为什么更改顺序会影响编译器的结果吗?这是因为静态初始化是按照代码中写入的顺序进行的。您好,是的,这是一个片段,很抱歉,实际的代码是10行初始化。为什么移动声明会产生影响?我明白你的意思,但我认为java编译就像C#一样,首先对整个文件进行标记,然后进行编译。我认为init顺序是方法、字段,然后是初始值设定项,所以字段在文件后面的物理位置声明肯定不重要吗?我认为这是为了避免循环依赖,尽管我不确定在这种情况下它做了什么有益的事情。