Reputation: 3244
The program below prints:
my name is:null
my name is:null
Someclass static init
AFAIK when a class is first loaded static blocks and fields are always initialized first, instance blocks and fields second. Therefore variables "objectName1" and "objectName2" should be initialized first, instance variable "list" second...but the ouput obviously contradicts this theory... Can anyone explain the program behavior (I'm not looking for a critique of the design in itself btw) ?
import java.util.ArrayList;
import java.util.List;
public class Main2{
public static void main (String[] args){
SomeClass.getInstance();
}
}
class SomeClass {
private static final SomeClass instance = new SomeClass();
public static SomeClass getInstance(){
return instance;
}
static {
System.out.println ("Someclass static init");
}
private static String objectName1 ="test1";
private static String objectName2 ="test2";
@SuppressWarnings("serial")
private List<SomeObject> list=
new ArrayList<SomeObject> () { {
add (new SomeObject(objectName1));
add (new SomeObject(objectName2));
}};
}
class SomeObject {
String name;
SomeObject (String name){
this.name = name;
System.out.println ("my name is:" +name);
}
}
Upvotes: 7
Views: 2624
Reputation: 36096
On first look I was pretty surprised about the behavior myself, but on second thought, it is quite trivial to explain:
private static final SomeClass instance = new SomeClass();
is part of the static initialization of SomeClass
. As you create an instance before initialization has completed, the class is not yet completely initialized. When you replace the System.out.println(...);
with something like new Exception().printStackTrace();
you get this (note that I put all classes as static nested classes into Main)
at Main$SomeObject.<init>(Main.java:37) // new Exception().printStackTrace();
at Main$SomeClass$1.<init>(Main.java:26) // add(new SomeObject(...))
at Main$SomeClass.<init>(Main.java:23) // list = new ArrayList()
at Main$SomeClass.<clinit>(Main.java:10) // instance = new SomeClass()
at Main.main(Main.java:6) // SomeClass.getInstance();
As you see, execution still is inside Main$SomeClass.<clinit>
(the class initialization), hence SomeClass is not completely initialized.
As a side note: the best way to implement Singleton pattern is to avoid it completely. The second best most likely is using enum
(at least it's Josh-Bloch-approved)
class enum SomeClass {
instance;
// snip
}
Upvotes: 1
Reputation: 1074198
Static blocks are initialized in order (so you can rely on the ones above in the ones below). By creating an instance of SomeClass
as your first static initializer in SomeClass
, you're forcing an instance init during the static init phase.
So the logical order of execution of your code is:
SomeClass
, all static fields initially defaults (0
, null
, etc.)SomeClass
SomeClass
instance, using current values for static fields (so objectName1
and objectName2
are null
)SomeObject
class, all static fields initially default (you don't have any)SomeObject
static inits (you don't have any)SomeObject
using the passed-in null
valuesSomeClass
, setting objectName1
and objectName2
To make this work as you may expect, simply put the inits for objectName1
and objectName2
above the init for instance
.
Upvotes: 10
Reputation: 4934
As suggested moving this line:
private static final SomeClass instance = new SomeClass();
after these:
private static String objectName1 ="test1";
private static String objectName2 ="test2";
should fix the problem.
Upvotes: 1
Reputation: 6229
The first thing that executes is probably the static initializer of the instance
variable. This causes the list to be initialized using the (uninitialized) objectName1
and objectName2
variables. After that, it proceeds to initialize objectName1
and objectName2
.
If you move the declaration of instance
to the end of SomeClass
it will probably do what you're expecting.
Upvotes: 0