On this page
Description
When does the code in a static block actually run? In what order do constructors and field initializers fire when one class extends another? And how do you make sure an object is created exactly once, even when many threads ask for it at the same time? These questions sound academic, but they explain real behavior: why one way of writing a singleton is safe and another isn't, why a startup error can break a class until the server restarts, and what a puzzling "what does this print?" interview question is really testing.
The good news is that the JVM follows a few simple, strict rules. Once you know them, the rest follows.
Here's the path, from easiest to most technical:
- When a class gets set up (initialized), and the one big exception: constants.
- The order things run in when you create an object.
- What happens when a static initializer throws.
- Four ways to write a singleton, and when each one is right.
- How a "single" instance can end up with a twin.
When a class gets set up
Java doesn't prepare every class when your program starts. A class is initialized (its static fields get their values and its static {} blocks run, top to bottom) the first time it's really used:
- creating an object with
new, - calling one of its static methods,
- reading one of its static fields (with one exception, below).
A subclass also initializes its parent class first.
The exception is a compile-time constant: a static final field of a primitive type or String, set to a value the compiler can work out on its own. The compiler copies the value straight into the code that uses it, so reading it never touches the class:
class Metrics {
static final String VERSION = "1.4"; // a constant: copied into callers
static final String BUILT_AT = String.valueOf(System.nanoTime()); // computed when the program runs
static { System.out.println("Metrics initialized"); }
}
Metrics.VERSION // prints nothing: the class isn't initialized
Metrics.BUILT_AT // prints "Metrics initialized" first
Two promises come with initialization, and the rest of this lesson builds on them:
- It happens exactly once per class.
- It happens under a lock. If another thread needs the class while it's being set up, that thread waits, and then sees all the static fields fully set.
The order things run in
Creating an object runs code from several places. Here's a parent and a child class that announce each step:
class Base {
static { System.out.println("Base static"); }
{ System.out.println("Base instance"); } // an instance initializer block
Base() { System.out.println("Base constructor"); }
}
class Child extends Base {
static { System.out.println("Child static"); }
{ System.out.println("Child instance"); }
Child() { System.out.println("Child constructor"); }
}
new Child(); // Base static, Child static, Base instance, Base constructor, Child instance, Child constructor
new Child(); // Base instance, Base constructor, Child instance, Child constructor
Two rules explain it:
- Static setup happens once per class, parent first. That's why the second
new Child()skips it. - Every new object is built from the top of the family down. For each class, starting with the parent, Java runs its field initializers and instance blocks, then its constructor body.
Rule 2 is why a parent constructor that calls an overridable method is dangerous: the child's version runs before the child's own fields have been set.
When a static initializer throws
class Config {
static final int PORT = Integer.parseInt(System.getProperty("port", "eighty"));
}
"eighty" isn't a number, so the first use of Config throws an ExceptionInInitializerError that wraps the real NumberFormatException. Here's the part that surprises people: Java marks the class as broken and never tries again. Every later use throws NoClassDefFoundError: Could not initialize class Config, even after someone fixes the setting, until the JVM restarts.
The lesson: keep static initializers simple. Load configuration in a normal method that you can call, retry and test.
Singletons
A singleton is a class that should have exactly one instance, such as a shared cache or a connection registry. There are four common ways to write one in Java.
1. Eager. Create the instance when the class initializes. Thread-safe for free, thanks to the class-initialization lock:
class Eager {
private static final Eager INSTANCE = new Eager();
private Eager() {}
static Eager getInstance() { return INSTANCE; }
}
The catch: calling any static method on Eager creates the instance, even one that doesn't need it.
2. The holder idiom. Lazy (created only when first asked for) and still lock-free:
class Lazy {
private Lazy() {}
private static class Holder { static final Lazy INSTANCE = new Lazy(); }
static Lazy getInstance() { return Holder.INSTANCE; }
}
Holder isn't initialized until getInstance() first touches it, and from then on getInstance() is a plain field read.
3. An enum. The JVM creates each enum constant exactly once, and it also blocks the two tricks in the next section:
enum Registry { INSTANCE; }
4. Double-checked locking. Lazy, and it locks only while creating the instance:
class Checked {
private static volatile Checked instance; // volatile is required
private Checked() {}
static Checked getInstance() {
Checked local = instance;
if (local == null) { // first check, without the lock
synchronized (Checked.class) {
local = instance;
if (local == null) instance = local = new Checked(); // second check, with it
}
}
return local;
}
}
Without volatile, another thread can see the reference before the constructor's work is visible, and use a half-built object. (The Java Multithreading module explains why volatile fixes this.) A simpler alternative is to make the whole getInstance() method synchronized: correct, but it takes a lock on every call.
Which one should you pick?
| You need | Use |
|---|---|
| Simplest, and it's cheap to create | eager |
| Lazy, and creating it can't fail | holder idiom |
| Strongest protection against extra instances | enum |
| Lazy, and creating it might fail and need a retry | double-checked locking (or a synchronized method) |
The last row matters because of the previous section: if a holder's initializer throws, the holder class is broken until restart, so a failed first attempt can never be retried.
How a singleton gets a twin
A private constructor keeps honest code out, but three things can still create a second instance of a class-based singleton:
- Reflection can call a private constructor:
constructor.setAccessible(true); constructor.newInstance(). - Deserialization creates a new object without calling any constructor. A
readResolve()method returningINSTANCEfixes it. - Separate class loaders. A class is unique per class loader, so two web applications in one server that each bundle the same library each get their own copy of the class, and of its "single" instance. Enums don't help here.
An enum handles the first two automatically. In everyday application code, most "singletons" are simply one object that a dependency-injection framework (like Spring) creates and shares; these idioms matter for libraries, for code without a framework, and for interviews.
Common mistakes
| Mistake | What happens | Do this instead |
|---|---|---|
| Heavy work or I/O in a static initializer | one failure breaks the class until restart | do it in a method you can retry |
| Expecting a constant to trigger initialization | nothing runs: the value was copied in | use a method, or a non-constant field |
| An eager singleton with unrelated static methods | the instance is built too early | holder idiom or enum |
Double-checked locking without volatile | another thread can see a half-built object | add volatile |
| A holder whose constructor can fail | the failure is permanent | double-checked locking, or synchronized |
| Trusting a private constructor | reflection and deserialization make twins | enum, or readResolve |
| A parent constructor calling an overridable method | the child's code runs before its fields are set | call only private/final methods |
Check yourself
Try to answer before opening each one. Recalling beats re-reading.
1. Why is the holder idiom lazy and thread-safe without any lock in getInstance()?
Show answer
Holder initializes only on its first use, which happens inside getInstance(), and the JVM runs a class's initialization exactly once, under a lock. Every thread sees the one finished instance; after that, getInstance() is a plain field read.
2. A static initializer threw on first use. Why does every later call fail with NoClassDefFoundError?
Show answer
The JVM marks a class whose initialization failed as unusable and never retries it. The first use gets ExceptionInInitializerError; every later use gets NoClassDefFoundError, until the JVM restarts.
3. What order do static blocks, instance blocks and constructors run in for the first new Child()?
Show answer
Base's static setup, then Child's; then Base's instance blocks and constructor; then Child's instance blocks and constructor.
4. Reading Metrics.VERSION (a static final String set to "1.4") doesn't run Metrics's static block. Why?
Show answer
It's a compile-time constant, so the compiler copied "1.4" into the calling code. The program never touches the Metrics class at all.
Key takeaways
- A class initializes on first real use, exactly once, under a lock. Compile-time constants don't count as a use.
- A failed static initializer breaks the class until restart, so keep static setup simple.
- Static setup runs once, parent first; every object is then built from the parent down.
- Eager, holder, enum and double-checked locking each have a place;
volatileis required for double-checked locking. - Reflection, deserialization and separate class loaders can each create a second "single" instance.