1. What are initialization blocks
In Java, there are two types of initialization blocks:
- Non-static (instance) initialization blocks — executed every time a new object is created, immediately after field initialization and before the constructor is called.
- Static initialization blocks — executed once when the class is loaded into memory (before the first object is created or any static fields/methods are accessed).
Non-static initialization block
Declared directly in the body of the class, without any keyword like static:
public class User {
private String name;
// Non-static initialization block
{
System.out.println("Executing non-static block!");
name = "Default name";
}
public User() {
System.out.println("Executing constructor!");
}
}
Static initialization block
Declared with the static keyword:
public class Config {
public static String appName;
static {
System.out.println("Executing static block!");
appName = "My super application";
}
}
2. Initialization order: who’s in charge?
In Java, the initialization order of class elements isn’t “random” but a strictly defined sequence. If you have ever tried to assemble IKEA furniture without the manual, you’ll understand why order matters: if you mix up the steps, you’ll get an art object instead of a wardrobe.
Initialization order:
- Static fields and static blocks — in the order they are declared in the class. Executed once when the class is loaded.
- Non-static fields and non-static blocks — in the order they are declared, for each new object created.
- Constructor — runs after all non-static initializations.
Schematic
+-------------------------------+
| Class loading into the JVM |
+-------------------------------+
| 1. Static fields |
| 2. Static blocks |
| ↓ |
| Object creation |
| ↓ |
| 3. Non-static fields |
| 4. Non-static blocks |
| 5. Constructor |
+-------------------------------+
Example with output
Let’s write a class that shows us the order in which everything happens:
public class Demo {
static String staticField = print("1. static field");
static {
print("2. static block");
}
String field = print("3. non-static field");
{
print("4. non-static block");
}
public Demo() {
print("5. constructor");
}
static String print(String msg) {
System.out.println(msg);
return msg;
}
public static void main(String[] args) {
System.out.println("Creating the first Demo object:");
Demo d1 = new Demo();
System.out.println("\nCreating the second Demo object:");
Demo d2 = new Demo();
}
}
What will appear on the screen?
1. static field
2. static block
Creating the first Demo object:
3. non-static field
4. non-static block
5. constructor
Creating the second Demo object:
3. non-static field
4. non-static block
5. constructor
Note: static parts (static) are executed only once—on the first access to the class. Everything that is not static runs every time an object is created.
3. Code examples: why initialization blocks are useful
When constructors aren’t enough
Sometimes part of the initialization needs to be shared by all constructors. For example, if a class has several constructors and you don’t want to duplicate the same initialization in each of them. In this case, it’s convenient to move it into a non-static block:
public class Person {
private String id;
private String name;
{
// This code will run before any constructor
id = java.util.UUID.randomUUID().toString();
System.out.println("Generating a unique id: " + id);
}
public Person() {
System.out.println("Person() without parameters");
}
public Person(String name) {
this.name = name;
System.out.println("Person(String name)");
}
}
Result: when any Person object is created, the id is generated first, then the appropriate constructor runs.
Initializing complex static data
A static block is often used to initialize “heavy” or complex static fields, for example, reading configuration from a file, creating collections, connecting to a database, etc.
public class Settings {
public static final java.util.Map<String, String> DEFAULTS;
static {
DEFAULTS = new java.util.HashMap<>();
DEFAULTS.put("theme", "light");
DEFAULTS.put("language", "ru");
System.out.println("Settings static block: default settings");
}
}
4. Useful nuances
When to use initialization blocks
When to use
- For common initialization required by all constructors.
- For complex static data that cannot be expressed with a simple assignment.
- For initializing static resources (for example, reading a config file at application startup).
When NOT to use
- If a simple assignment or a constructor will do — use those.
- Don’t “hide” business logic in initialization blocks — it makes the code harder to read and maintain.
- If initialization depends on constructor parameters, use the constructor itself.
Don’t overuse initialization blocks
Initialization blocks are powerful but not the most commonly used tool. In most cases, a simple assignment or a constructor is enough. If a class has too many initialization blocks, the code becomes unreadable and hard to maintain.
Don’t use a non-static block for logic that depends on constructor parameters
You cannot use constructor parameters in a non-static block, because it runs BEFORE the constructor. If you need to initialize something based on parameters, do it in the constructor itself.
Static blocks and inheritance
Static initialization blocks are not inherited. Each class has its own static block. When a child class is loaded, the base class’s static block runs first, followed by the child class’s static block.
5. Common mistakes when working with initialization blocks
Error #1: Expecting a non-static block to see constructor parameters.
Many beginners try to use constructor parameters in a non-static block but get a compilation error or unexpected result. Remember: the non-static block runs BEFORE the constructor, which means there are no parameters yet.
Error #2: Too much logic in initialization blocks.
If complex logic appears in initialization blocks, the code becomes confusing. It’s better to do the main work in the constructor or separate methods.
Error #3: Multiple static blocks with different order.
If a class has several static blocks, they run in the order they are declared in the code, along with static fields. Sometimes this leads to unexpected results if, for example, one block depends on another’s result.
Error #4: Expecting the static block to run on each object creation.
static blocks run only once — when the class is loaded. If you expect re-initialization, it will not happen.
Error #5: Trying to access non-static fields from a static block.
Only static variables and methods are available in a static block. Attempting to access non-static (regular) fields will cause a compilation error.
GO TO FULL VERSION