Can I guarantee the order in which static initializers are run in Java?

Can I guarantee the order in which static initializers are run in Java?

猫九 发布于 2021-11-30 字数 2189 浏览 797 回复 6 原文

I have a Set class (This is J2ME, so I have limited access to the standard API; just to explain my apparent wheel-reinvention). I am using my set class to create constant sets of things in classes and subclasses. It sort of looks like this...

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] */
    }};
}

All looks fine, except the line at [1] causes a null pointer exception. Presumably this means that the static initialiser in the subclass is being run before that of the parent class. This surprised me because I'd have thought it would run the static blocks in any new imports first, before running any in the instatiated subclass.

Am I right in this assumption? Is there any way to control or work around this behaviour?

Update:

Things are even stranger. I tried this instead (Note the 'new ParentClass()' line):

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");
    }};
}

And the output is strange:

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

So ParentClass is initialised, but the subclass doesn't have access to it in its static initializer.

如果你对这篇文章有疑问,欢迎到本站 社区 发帖提问或使用手Q扫描下方二维码加群参与讨论,获取更多帮助。

扫码加入群聊

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

执着的年纪 2022-06-07 6 楼

I think something similar was written in Java puzzles book or google's YouTube video about java tricks.

踏雪无痕 2022-06-07 5 楼

Given the output line of ["one", "two", "three"], it is basically impossible that ParentClass.THE_SET has never been initialized.

Of course, it is possible that there isn't only one classloader involved, but it would certainly be helpful to see the method and line number where the null pointer happens.

も星光 2022-06-07 4 楼

Simply stop abusing the concept of anonymous classes for instance initialization (the so-called "double brace idiom").

む无字情书 2022-06-07 3 楼

Even if you didn't have the extends ParentClass in there, using ParentClass should cause it to become initialised.

Where things become tricky is when you have cycles. With a cycle it is possible to access a class before it has been fully initialised. As Java is a multithreaded system, you can also have problems with deadlocks and races.

May be your Java ME implementation is buggy (not unheard of). Part your full ParentClass references your ChildClass. Or perhaps there is some other application/library bug.

On a related note, if you don't -target 1.4 or later, inner classes' outer this is not initialised as soon as you might expect. As presented your code uses (technically) inner classes in a static context, so that shouldn't be a problem.

Also it's interesting to point out that static initialisation is slightly confused in situations like this, because you actually have four classes there.

为人所爱 2022-06-07 2 楼

There is no guarantee for static initializer order among classes. Within a class, they run in the order of the source code.

If you think about it, therre really couldn't be an order among classes, because you don't control when the classes are loaded either; you might dynamically load a class, or the JVM might optimize the load order.

来日方长 2022-06-07 1 楼

Is this what you are trying to accomplish? Or do you need a local implementation of the Set interface?

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] */
    }
}