3

我有以下代码,错误注释

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 应该已经被初始化了。

我有一个解决方法,就是将这个类设置为单例,但这样做会使代码的可读性降低。我很想知道这里出了什么问题。

4

3 回答 3

2

这在JLS 8.3.3中进行了解释。事实上,有几种方法可以解决它。

使用限定名称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 有点深奥。您似乎没有犯此规则旨在捕获的错误。

于 2014-11-06T17:32:11.293 回答
2

您不能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”实例是一个错字。)

于 2014-11-06T16:54:08.467 回答
1

静态初始化是按照它在代码中编写的顺序完成的。因此,在您的代码中,它将首先进入静态块,然后进入变量声明......

您不能在声明变量之前使用它,这就是它无法编译的原因...

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
    }


}



    }
于 2014-11-06T16:58:20.037 回答