Initialization Blocks
Initialization blocks are code blocks that run during object or class creation, supplementing constructors and field initializers. Java provides two kinds: instance initialization blocks, which run every time an object is created before the constructor body executes, and static initialization blocks, which run exactly once when the class is first loaded by the JVM. Together with field initializers, they form a complete picture of how Java objects and classes come to their initial state. This entry covers the execution order of all initialization mechanisms, the purpose and use cases for each type of block, interaction with constructors and inheritance, and the practical scenarios where initialization blocks solve problems that field initializers and constructors cannot.
Instance Initialization Blocks
// ── Instance initialization block ────────────────────────────────────
public class AuditedEntity {
private final String entityId;
private final List<String> changeLog;
private final Instant createdAt;
// ── Instance initializer — runs for EVERY constructor ─────────────
// Runs after super() but BEFORE each constructor body
{
this.changeLog = new ArrayList<>();
this.createdAt = Instant.now();
changeLog.add("Entity created at " + createdAt);
System.out.println("Instance initializer ran");
}
// ── Constructor 1 — receives entityId ─────────────────────────────
public AuditedEntity(String entityId) {
// Instance initializer already ran — changeLog and createdAt set
this.entityId = entityId;
changeLog.add("Assigned id: " + entityId);
}
// ── Constructor 2 — generates a random entityId ───────────────────
public AuditedEntity() {
// Same instance initializer ran here too
this.entityId = UUID.randomUUID().toString();
changeLog.add("Auto-assigned id: " + entityId);
}
public List<String> getChangeLog() {
return Collections.unmodifiableList(changeLog);
}
}
// ── Multiple instance initializers — run in order ─────────────────────
public class MultiInitDemo {
private int x;
private int y;
{ x = 1; System.out.println("First block: x=" + x); } // runs first
{ y = 2; System.out.println("Second block: y=" + y); } // runs second
public MultiInitDemo() {
System.out.println("Constructor: x=" + x + " y=" + y);
}
}
// Output when new MultiInitDemo():
// First block: x=1
// Second block: y=2
// Constructor: x=1 y=2
// ── Exact execution order ─────────────────────────────────────────────
public class Parent {
{ System.out.println("Parent instance initializer"); }
public Parent() { System.out.println("Parent constructor"); }
}
public class Child extends Parent {
{ System.out.println("Child instance initializer"); }
public Child() {
// super() implicit — runs Parent chain first
System.out.println("Child constructor");
}
}
// new Child() output:
// Parent instance initializer ← parent's initializer
// Parent constructor ← parent's constructor body
// Child instance initializer ← child's initializer
// Child constructor ← child's constructor bodyStatic Initialization Blocks
// ── Static initialization block ──────────────────────────────────────
public class CountryCodeLookup {
// ── Static field requiring complex initialization ──────────────────
private static final Map<String, String> DIAL_CODES;
private static final Set<String> VALID_CODES;
// ── Static initializer — runs ONCE when class is loaded ───────────
static {
System.out.println("CountryCodeLookup class loading...");
Map<String, String> codes = new HashMap<>();
codes.put("US", "+1");
codes.put("GB", "+44");
codes.put("DE", "+49");
codes.put("FR", "+33");
codes.put("JP", "+81");
codes.put("CN", "+86");
codes.put("AU", "+61");
codes.put("IN", "+91");
DIAL_CODES = Collections.unmodifiableMap(codes);
VALID_CODES = Collections.unmodifiableSet(codes.keySet());
System.out.println("Loaded " + DIAL_CODES.size() + " country codes");
}
public static String getDialCode(String countryCode) {
return DIAL_CODES.getOrDefault(countryCode.toUpperCase(), "unknown");
}
public static boolean isValidCode(String code) {
return VALID_CODES.contains(code.toUpperCase());
}
}
// ── Static block with error handling ─────────────────────────────────
public class DatabaseConfig {
private static final Properties DB_PROPS;
static {
DB_PROPS = new Properties();
try (InputStream in = DatabaseConfig.class
.getResourceAsStream("/db.properties")) {
if (in == null) {
throw new ExceptionInInitializerError(
"Cannot find /db.properties on classpath");
}
DB_PROPS.load(in);
} catch (IOException e) {
// Wrap checked exception — static block cannot throw checked
throw new ExceptionInInitializerError(e);
}
System.out.println("Database config loaded: " +
DB_PROPS.getProperty("db.url"));
}
public static String getUrl() { return DB_PROPS.getProperty("db.url"); }
public static String getUsername() { return DB_PROPS.getProperty("db.username"); }
}
// ── Static initialization holder — lazy, thread-safe singleton ────────
public class ExpensiveSingleton {
private ExpensiveSingleton() {
System.out.println("Singleton created — expensive setup...");
}
// Holder class NOT loaded until getInstance() is first called
private static class Holder {
// Static block in Holder runs when Holder is first loaded
static final ExpensiveSingleton INSTANCE = new ExpensiveSingleton();
}
// No synchronization needed — JVM class loading is thread-safe
public static ExpensiveSingleton getInstance() {
return Holder.INSTANCE; // triggers Holder class loading
}
}Initialization Order — The Complete Picture
// ── Complete initialization order demonstration ────────────────────────
public class InitOrderDemo {
// ── 1. Static field initializer ───────────────────────────────────
private static int staticField = initStatic("static field");
// ── 2. Static block ───────────────────────────────────────────────
static {
System.out.println("Static block: staticField=" + staticField);
staticField = 100;
}
// ── 3. Another static field after the block ───────────────────────
private static int staticField2 = initStatic("static field 2");
// ── 4. Instance field initializer ────────────────────────────────
private int instanceField = initInstance("instance field");
// ── 5. Instance block ────────────────────────────────────────────
{
System.out.println("Instance block: instanceField=" + instanceField);
instanceField = 200;
}
// ── 6. Another instance field after the block ─────────────────────
private int instanceField2 = initInstance("instance field 2");
// ── 7. Constructor body ───────────────────────────────────────────
public InitOrderDemo() {
System.out.println("Constructor: instanceField=" +
instanceField + " instanceField2=" + instanceField2);
}
private static int initStatic(String name) {
System.out.println("Static initializer: " + name);
return 1;
}
private int initInstance(String name) {
System.out.println("Instance initializer: " + name);
return 2;
}
}
// Output of: new InitOrderDemo(); new InitOrderDemo();
//
// (First time class is loaded):
// Static initializer: static field ← step 1
// Static block: staticField=1 ← step 2
// Static initializer: static field 2 ← step 3
//
// (First object construction):
// Instance initializer: instance field ← step 4
// Instance block: instanceField=2 ← step 5
// Instance initializer: instance field 2← step 6
// Constructor: instanceField=200 instanceField2=2 ← step 7
//
// (Second object construction — NO static steps again):
// Instance initializer: instance field ← step 4 repeats
// Instance block: instanceField=2 ← step 5 repeats
// Instance initializer: instance field 2← step 6 repeats
// Constructor: instanceField=200 instanceField2=2 ← step 7 repeats
// ── Zeroing guarantee ─────────────────────────────────────────────────
public class ZeroingDemo {
static int staticInt; // guaranteed 0
static boolean staticBool; // guaranteed false
static String staticRef; // guaranteed null
int instanceInt; // guaranteed 0 before initializer
boolean instanceBool; // guaranteed false before initializer
Object instanceRef; // guaranteed null before initializer
{
System.out.println("Before assignment: " +
instanceInt + " " + instanceBool + " " + instanceRef);
// prints: 0 false null
}
}Practical Use Cases and Best Practices
// ── Use case 1: Loading native library with error handling ───────────
public class NativeEncryption {
private static final boolean NATIVE_AVAILABLE;
static {
boolean available;
try {
System.loadLibrary("encryption-native");
available = true;
System.out.println("Native encryption library loaded");
} catch (UnsatisfiedLinkError e) {
available = false;
System.err.println("Native library unavailable — using Java fallback");
}
NATIVE_AVAILABLE = available;
}
public byte[] encrypt(byte[] data) {
if (NATIVE_AVAILABLE) return nativeEncrypt(data);
else return javaEncrypt(data);
}
private native byte[] nativeEncrypt(byte[] data);
private byte[] javaEncrypt(byte[] data) { /* pure Java impl */ return data; }
}
// ── Use case 2: Registering handlers at class load time ──────────────
public class EventHandlerRegistry {
private static final Map<String, EventHandler> HANDLERS;
static {
Map<String, EventHandler> h = new LinkedHashMap<>();
// Register in priority order — LinkedHashMap preserves insertion order
h.put("auth", new AuthEventHandler());
h.put("payment", new PaymentEventHandler());
h.put("audit", new AuditEventHandler());
h.put("notify", new NotificationEventHandler());
HANDLERS = Collections.unmodifiableMap(h);
}
public static EventHandler getHandler(String type) {
EventHandler handler = HANDLERS.get(type);
if (handler == null) throw new IllegalArgumentException(
"No handler for event type: " + type);
return handler;
}
}
// ── Use case 3: Instance block for anonymous class initialization ──────
// Anonymous classes cannot have named constructors — use instance blocks
Comparator<String> caseInsensitive = new Comparator<String>() {
private final Collator collator;
// Instance block initializes the anonymous class
{
collator = Collator.getInstance(Locale.ENGLISH);
collator.setStrength(Collator.SECONDARY);
}
@Override
public int compare(String a, String b) {
return collator.compare(a, b);
}
};
// ── Anti-pattern: calling overridable method in initializer ─────────
public class UnsafeParent {
{
// NEVER call overridable methods from initializers or constructors
// This calls Child.init() BEFORE Child's fields are initialized
init(); // ← DANGEROUS if overridden
}
protected void init() {
System.out.println("Parent init");
}
}
public class UnsafeChild extends UnsafeParent {
private String name = "initialized";
@Override
protected void init() {
// Runs BEFORE "name = initialized" — name is still null here!
System.out.println("Child init, name=" + name); // null!
}
}