Java Instance Initializer Block: Order, Rules and Examples

Instance initializer block in Java with examples, covering execution order with fields and constructors, this() chaining, exceptions and double braces.

Order of parent constructor, field initializers, instance initializer blocks and constructor body when creating a Java subclass object

A Java instance initializer block is a block of code in braces, written at class level outside any method, that runs every time an object of the class is created, after the superclass constructor and before the rest of the constructor body.

We use it to share setup code between several constructors and to initialize fields in anonymous classes, which cannot declare a constructor. The following example gives every recipe the same first step, whichever constructor creates it.

class Recipe {
    final List<String> steps = new ArrayList<>();
    final String title;

    {
        steps.add("wash hands");            // instance initializer block
    }

    Recipe() { this.title = "untitled"; }

    Recipe(String title) { this.title = title; }
}

Neither constructor mentions steps, yet both objects start with the same step. In the following sections, we explain when the block runs relative to field initializers and parent constructors, what happens with this(…), the rules for exceptions, anonymous classes and double brace initialization, and when a constructor is the better choice.

1. Syntax and Basic Behavior

An instance initializer is a pair of braces at class level, without a name, a return type or the static keyword. A class can have any number of them, and they can read and assign instance fields and call instance methods like a constructor body can.

Recipe pasta = new Recipe("Pasta");
List<String> pastaSteps = pasta.steps;      // [wash hands]
Recipe blank = new Recipe();
String blankTitle = blank.title;            // "untitled"
List<String> blankSteps = blank.steps;      // [wash hands]

The block runs once per object, never when the class loads. That is the main difference from a static initializer block, which we compare in section 6.

2. Execution Order With Fields and Parent Classes

Field initializers and instance initializer blocks run together, in the order they appear in the source, right after the superclass constructor returns. The constructor body that follows runs last, which is why a constructor can rely on everything the blocks prepared.

Order of parent constructor, field initializers, instance initializer blocks and constructor body when creating a Java subclass object
The parent is fully built first, then the child’s fields and blocks run top to bottom, and the constructor body runs last

The following example is a Dish parent and a Dessert subclass. Each step adds its name to a shared log, and Dessert places a field initializer, two blocks and its constructor in an unusual order on purpose.

class Dish {
    final List<String> log = new ArrayList<>();

    {
        log.add("Dish block");
    }

    Dish() {
        log.add("Dish()");
    }
}
class Dessert extends Dish {
    String topping = note("topping field");

    {
        log.add("Dessert block 1");
    }

    Dessert() {
        log.add("Dessert()");
    }

    {
        log.add("Dessert block 2");
    }

    String note(String step) {
        log.add(step);
        return step;
    }
}
List<String> order = new Dessert().log;   // [Dish block, Dish(), topping field, Dessert block 1, Dessert block 2, Dessert()]

We can read the creation of a Dessert as four steps, following JLS section 12.5.

  • The Dessert() constructor starts and calls super(), which the compiler inserted.
  • The parent runs completely, first its field initializer and block, and the Dish() body after them.
  • Back in Dessert, the field initializer and both blocks run top to bottom, even block 2, which sits below the constructor.
  • The Dessert() body runs last.

Since Java 25, a constructor can also run statements before an explicit super(…) call, a feature called flexible constructor bodies (JEP 513). Those statements run before the parent constructor, so they also run before every instance initializer block of the class.

Textual order also limits what a block can see. A block may assign a field declared further down, but reading that field by its simple name is a compile error, illegal forward reference, so we declare fields above the blocks that use them.

3. Initializer Blocks and this() Constructor Calls

The compiler copies the code of all field initializers and blocks into each constructor that starts with a super(…) call, written or implicit. A constructor that starts with this(…) does not get the copy, because the constructor it calls already runs the blocks. The block therefore runs once per object even when constructors chain.

class Batch {
    int blockRuns;
    final int servings;

    {
        blockRuns++;
    }

    Batch() {
        this(4);
    }

    Batch(int servings) {
        this.servings = servings;
    }
}
Batch defaultBatch = new Batch();
int runs = defaultBatch.blockRuns;          // 1
int servings = defaultBatch.servings;       // 4

If the block were copied into both constructors, blockRuns would be 2 for new Batch(). The count of 1 confirms that only the constructor ending the this(…) chain runs it.

4. Exceptions and Other Restrictions

An instance initializer can throw unchecked exceptions, such as IllegalStateException, and the new expression fails with that exception. A checked exception is allowed only if every constructor of the class declares it in its throws clause, because a constructor is where the caller sees the exception.

// does not compile: unreported exception IOException; must be caught or declared to be thrown
class ImportedRecipe {
    {
        if (Files.notExists(Path.of("recipes.csv"))) {
            throw new IOException("recipes.csv missing");
        }
    }

    ImportedRecipe() { }                    // no throws IOException
}

Adding throws IOException to every constructor makes the class compile. The block also cannot contain a return statement and cannot call this(…) or super(…), which are allowed only as the explicit constructor call inside a constructor. Interfaces cannot declare initializer blocks at all, because they have no instances to initialize.

5. Anonymous Classes and Double Brace Initialization

An anonymous class has no name, so it cannot declare a constructor. An instance initializer block is the only place for setup code that needs more than a field initializer. The following example creates a one-off recipe without writing a named subclass.

Recipe toast = new Recipe("Toast") {
    {
        steps.add("toast the bread");
    }
};
List<String> toastSteps = toast.steps;      // [wash hands, toast the bread]

Double brace initialization applies the same trick to collections. The outer braces create an anonymous subclass of HashMap, and the inner braces are its initializer block.

Map<String, Integer> spices = new HashMap<>() {{
    put("salt", 1);
    put("pepper", 2);
}};
boolean extraClass = spices.getClass() != HashMap.class;   // true

The pattern looks compact, but each use creates a new class file. Inside an instance method, the anonymous map also keeps a reference to the enclosing object, which can keep a large object in memory as long as the map lives. Since Java 9, the factory methods Map.of() and List.of() are the better choice, and immutable collection factories covers them.

Map<String, Integer> fixedSpices = Map.of("salt", 1);     // {salt=1}
Map<String, Integer> editable = new HashMap<>(Map.of("salt", 1));
editable.put("pepper", 2);
int spiceCount = editable.size();                         // 2

6. Static vs Instance Initializer Blocks

Both kinds of block are anonymous code at class level, but they run at different times and see different members. A static block runs once when the JVM initializes the class, while an instance block runs for every object.

PointInstance initializer blockStatic initializer block
Syntax{ … }static { … }
RunsOnce per new, after the superclass constructorOnce per class, when the class is initialized
Can use this and instance fieldsYesNo, only static members
Checked exceptionsAllowed if every constructor declares themNot allowed
Exception at runtimeThe new expression throws itThe JVM wraps it in ExceptionInInitializerError
Typical useShared constructor code, anonymous classesFilling static lookup tables, loading native libraries

7. When to Use an Instance Initializer Block

In application code, constructor chaining with this(…) solves the same problem more visibly, because a reader of the constructor sees the shared code. Field initializers with a helper method cover most of the remaining cases.

Say a recipe app has three Recipe constructors, for a blank recipe, a recipe with a title and a recipe imported from a URL. All three must set a creation timestamp and an empty ingredient list. Delegating to one main constructor keeps that setup in one place and lets the fields stay final with no block at all.

class ImportedDish {
    final String title;
    final Instant createdAt;
    final List<String> ingredients;

    ImportedDish(String title, List<String> ingredients) {
        this.title = title;
        this.createdAt = Instant.now();
        this.ingredients = new ArrayList<>(ingredients);
    }

    ImportedDish(String title) {
        this(title, List.of());
    }
}
ImportedDish soup = new ImportedDish("Soup");
int ingredientCount = soup.ingredients.size();    // 0

Instance initializer blocks remain the correct option in anonymous classes, and in classes whose constructors cannot delegate to each other because they call different parent constructors. The block fits the second case because it runs after any super(…) call. More on how constructors chain is in the guide to Java constructors, and the OOP concepts overview links the related topics.

8. Instance Initializer FAQs

Most of these questions come from interview lists and from developers who read an unnamed pair of braces in someone else’s class.

8.1. Which runs first, the instance initializer block or the constructor?

The constructor starts first, but its body runs last. The constructor calls the superclass constructor, the instance initializers run next, and the remaining constructor statements execute last.

8.2. Can a class have more than one instance initializer block?

Yes. All blocks and field initializers run in the order they appear in the class, regardless of where the constructors are placed.

8.3. Can an instance initializer block use constructor parameters?

No. The block is not part of any single constructor, so it cannot see their parameters. Code that needs a parameter belongs in the constructor.

8.4. Is double brace initialization bad practice?

Yes, in most code. It creates an extra anonymous class per use, may keep a reference to the enclosing object, and the result’s class differs from HashMap. Map.of(), List.of() or a plain constructor followed by put() calls are clearer.

8.5. Does a record allow instance initializer blocks?

No. A record cannot declare instance initializers or instance fields beyond its components. Setup code goes into the compact canonical constructor instead.

9. Conclusion

An instance initializer block runs for every new object, after the superclass constructor and together with the field initializers in source order, and before the rest of the constructor body. It runs once per object, because constructors that start with this(…) skip it and leave the work to the constructor they call.

Checked exceptions in a block must be declared by every constructor, and a record cannot have one at all. For named classes, constructor chaining is the clearer option, while anonymous classes still need the block. Double brace initialization is a misuse of the feature that Map.of() and List.of() replace.

10. References

Happy Learning !!

Source Code on Github

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.