我可以保证在Java中运行静态初始化器的顺序吗?

时间:2009-04-27 13:10:11

标签: java inheritance static initialization static-initializer

我有一个Set类(这是J2ME,所以我对标准API的访问权限有限;只是为了解释我明显的轮子改造)。我正在使用我的set类在类和子类中创建常量集。它有点像......

class ParentClass
{
    protected final static Set THE_SET = new Set() {{
        add("one");
        add("two");
        add("three");
    }};
}


class SubClass extends ParentClass
{
    protected final static Set THE_SET = new Set() {{
        add("four");
        add("five");
        add("six");
        union(ParentClass.THE_SET); /* [1] */
    }};
}

所有看起来都很好,除了[1]处的行导致空指针异常。据推测,这意味着子类中的静态初始化程序在父类的运行之前运行。这让我感到惊讶,因为我认为它会先在任何新的导入中运行静态块,然后再在instatiated子类中运行。

我在这个假设中是对的吗?有没有办法控制或解决这种行为?

更新

事情甚至更奇怪。我尝试了这个(注意'new ParentClass()'行):

class ParentClass
{
    public ParentClass()
    {
        System.out.println(THE_SET);
    }

    protected final static Set THE_SET = new Set() {{
        add("one");
        add("two");
        add("three");
    }};
}


class SubClass extends ParentClass
{
    protected final static Set THE_SET = new Set() {{
        System.out.println("a");
        new ParentClass();
        System.out.println("b");
        add("four");
        System.out.println("c");
        add("five");
        System.out.println("d");
        add("six");
        System.out.println("e");
        union(ParentClass.THE_SET); /* [1] */
        System.out.println("f");
    }};
}

输出很奇怪:

a
["one", "two", "three"]
b
c
d
e
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.NullPointerException

所以ParentClass已初始化,但子类在其静态初始化程序中无法访问它。

6 个答案:

答案 0 :(得分:7)

这是你想要完成的吗?或者您是否需要本地实现Set接口?

class ParentClass
{
    protected final static Set THE_SET;

    static {
        THE_SET = new HashSet();
        THE_SET.add("one");
        THE_SET.add("two");
        THE_SET.add("three");
    }
}


class SubClass extends ParentClass
{
    protected final static Set THE_SECOND_SET;

    static {
        THE_SECOND_SET = new HashSet();
        THE_SECOND_SET.add("four");
        THE_SECOND_SET.add("five");
        THE_SECOND_SET.add("six");
        union(ParentClass.THE_SET); /* [1] */
    }
}

答案 1 :(得分:3)

不保证类之间的静态初始化顺序。在一个类中,它们按源代码的顺序运行。

如果您考虑一下,那么类之间的命令实际上不可能是因为您无法控制何时加载类;您可以动态加载类,或者JVM可以优化加载顺序。

答案 2 :(得分:2)

即使您没有extends ParentClass,使用ParentClass也会导致它被初始化。

当事情变得棘手时,就是你有周期。使用循环,可以在完全初始化之前访问类。由于Java是一个多线程系统,因此您也可能遇到死锁和竞争问题。

可能是你的Java ME实现是错误的(并非闻所未闻)。完整ParentClass部分引用您的ChildClass。或许还有一些其他的应用程序/库错误。

在相关的说明中,如果您没有-target 1.4或更高版本,则内部类的外部不会在您预期的时候立即初始化。如图所示,您的代码在静态上下文中使用(技术上)内部类,因此这不应该是一个问题。

同样有趣的是指出静态初始化在这种情况下略有混淆,因为你实际上有四个类。

答案 3 :(得分:2)

简单地停止滥用匿名类的概念进行实例初始化(所谓的“双支撑习语”)。

答案 4 :(得分:0)

给定[“one”,“two”,“three”]的输出行,ParentClass.THE_SET基本上不可能从未初始化。

当然,可能不仅涉及一个类加载器,但查看空指针发生的方法和行号肯定会有所帮助。

答案 5 :(得分:-1)

我认为类似的东西是用Java拼图书或google关于java技巧的YouTube视频写的。