5

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

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] 处的行导致空指针异常。大概这意味着子类中的静态初始化器在父类之前运行。这让我感到惊讶,因为我认为它会先在任何新导入中运行静态块,然后再在实例化子类中运行任何块。

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

更新:

事情就更奇怪了。我尝试了这个(注意'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 已初始化,但子类在其静态初始化程序中无权访问它。

4

6 回答 6

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] */
    }
}
于 2009-04-27T13:32:31.597 回答
3

不能保证类之间的静态初始值设定项顺序。在一个类中,它们按照源代码的顺序运行。

如果你仔细想想,类之间真的不可能有顺序,因为你也无法控制何时加载类;您可能会动态加载一个类,或者 JVM 可能会优化加载顺序。

于 2009-04-27T13:11:28.123 回答
2

即使你extends ParentClass在那里没有,使用ParentClass应该会导致它被初始化。

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

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

在相关说明中,如果您不这样做-target 1.4或稍后,内部类的外部 this 不会像您预期的那样初始化。如前所述,您的代码在静态上下文中使用(技术上)内部类,因此这应该不是问题。

另外有趣的是,静态初始化在这种情况下会有些混乱,因为您实际上有四个类。

于 2009-04-27T13:31:59.210 回答
2

只需停止滥用匿名类的概念来进行实例初始化(所谓的“双括号习语”)。

于 2009-04-27T13:57:13.083 回答
0

鉴于 ["one", "two", "three"] 的输出行,ParentClass.THE_SET 基本上不可能从未初始化过。

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

于 2009-04-27T13:36:22.790 回答
-1

我认为类似的东西写在 Java 谜题书或谷歌关于 Java 技巧的 YouTube 视频中。

于 2009-04-27T18:36:55.577 回答