【问题标题】:Static final member initialization on private class私有类的静态最终成员初始化
【发布时间】:2014-05-11 04:48:46
【问题描述】:

考虑以下代码:

public final class Foo {
    private static final Random random = new Random();
    private Foo() {}
}

这个类不能被实例化所以,什么时候初始化random?

【问题讨论】:

    标签: java static initialization final


    【解决方案1】:

    静态字段在类初始化期间被初始化,例如这里

        ...
        Class.forName("test.Foo");
        ...
    

    加载JVM后会初始化random字段,不会创建Foo实例。为了测试它,我们可以像这样改变 Foo

    class Foo {
        private static final Random random = new Random() {
            {
                System.out.println("random initialized");
            }
        };
    ...
    

    【讨论】:

    • 这会导致加载 Foo 的类对象,而不是 Foo 本身吗?
    • 好的,所以反射起作用了。但除了使用它之外,没有办法......另外,forName() 显式初始化了一个新对象。所以这仍然是在技术上创建一个新的Foo。使用Class.forName("test.Foo", false, Class.class.getClassLoader()) 不会初始化Foo
    • Class.forName("test.Foo") 不会创建 test.Foo 的新实例。
    • 我同意,我没这么说
    【解决方案2】:

    根据Java Language Specification section 12.4,静态成员和块的初始化发生在类初始化期间。初始化类的确切时间和这样做的步骤可以在链接中找到;一般来说,类在第一次以任何方式被访问时都会被加载/初始化,而加载/初始化包括静态成员的创建。

    但是,如果这就是 Foo 的全部内容,我不相信 random 会被初始化,因为它不能被实例化,它的静态成员不能被访问,也不能被子类化。根据 JLS 第 12.4.1 节,在这些情况下会发生初始化:

    1. T 是一个类,并创建了一个 T 的实例。

    2. T是一个类,调用了T声明的静态方法。

    3. 分配了一个由 T 声明的静态字段。

    4. 使用了由 T 声明的静态字段,并且该字段不是常量变量(第 4.12.4 节)。

    5. T 是一个顶级类(第 7.6 节),并执行一个词法嵌套在 T(第 8.1.3 节)中的断言语句(第 14.10 节)。

      1. 不能发生,因为构造函数是私有的。 2. 由于Foo 没有静态方法,所以不能发生。 3. 不能发生,因为静态字段是私有的。 4. 不能发生,因为静态字段是私有的,所以不能使用。 5. 不能发生,因为没有断言语句。

    所以我认为random 永远不会被初始化,因为 OP 的代码就是全部(编辑:除非你使用反射)

    【讨论】:

    • 我使用上面的代码和像public int getRandomInt(){ return random.nextInt(); }这样的getter
    • 如果你使用 getter,那么你满足条件 2,所以是的,你会加载 Foo。但是发布的 OP 代码永远不会导致 random 被初始化。
    猜你喜欢
    • 1970-01-01
    • 2014-03-31
    • 1970-01-01
    • 2012-02-18
    • 1970-01-01
    • 2012-07-22
    • 2011-07-18
    • 2015-02-18
    • 2015-05-18
    相关资源
    最近更新 更多