Exemplify the initialization-on-demand holder design pattern – Objects, Immutability, Switch Expressions, and Pattern Matching
By Adenike Adekola / May 26, 2021 / No Comments / Checking sub-range in the range from 0 to length, Exams of Java, Filling a long array with pseudo-random numbers, Getting integral and fractional parts from a double, Java Certifications, Returning an identity string
40. Exemplify the initialization-on-demand holder design pattern
Before we tackle the solution of implementing the initialization-on-demand holder design pattern let’s quickly resume a few ingredients of this solution.
Static vs. non-static blocks
In Java, we can have initialization non-static blocks and static blocks. An initialization non-static block (or simply, a non-static block) is automatically called every single time we instantiate the class. On the other hand, an initialization static block (or simply, a static block) is called a single time when the class itself is initialized. No matter how many subsequent instances of that class we create, the static block will never get executed again. In code lines:
public class A {
{
System.out.println(“Non-static initializer …”);
}
static {
System.out.println(“Static initializer …”);
}
}
Next, let’s run the following test code to create 3 instances of A:
A a1 = new A();
A a2 = new A();
A a3 = new A();
The output reveals that the static initializer is called only once, while the non-static initializer is called three times:
Static initializer …
Non-static initializer …
Non-static initializer …
Non-static initializer …
Moreover, the static initializer is called before the non-static one. Next, let’s talk about nested classes.
Nested classes
Quick example:
public class A {
private static class B { … }
}
Nested classes can be static or non-static. A non-static nested class is referred to as an inner class (further, it can be a local inner class (declared in a method) or an anonymous inner class (class with no name)). On the other hand, a nested class that is declared static is referred to as a static nested class. The following figure clarifies these statements:

Figure 2.28 – Java nested classes
Since B is a static class declared in A, we say that B is a static nested class.
Tackling initialization-on-demand holder design pattern
The initialization-on-demand holder design pattern refers to a thread-safe lazy-loaded singleton (single instance) implementation. Before JDK 16, we can exemplify this design pattern in code as follows (we want a single thread-safe instance of Connection):
public class Connection { // singleton
private Connection() {
}
private static class LazyConnection { // holder
static final Connection INSTANCE = new Connection();
static {
System.out.println(“Initializing connection …”
+ INSTANCE);
}
}
public static Connection get() {
return LazyConnection.INSTANCE;
}
}
No matter how many times a thread (multiple threads) are calling Connection.get(), we are always getting the same instance of Connection. This is the instance created when we called get() for the first time (first thread) and Java has initialized the LazyConnection class and its statics. In other words, if we never call get() then the LazyConnection class and its statics are never initialized (this is why we name it lazy initialization). And, this is thread-safe because static initializers can be constructed (here, INSTANCE) and referenced without explicit synchronization since they are run before any thread can use the class (here, LazyConnection).
JDK 16+
Until JDK 16, an inner class could contain static members as constant variables but it couldn’t contain static initializers. In other words, the following code will not compile because of the static initializer:
public class A {
public class B {
{
System.out.println(“Non-static initializer …”);
}
static {
System.out.println(“Static initializer …”);
}
}
}
But, starting with JDK 16, the previous code is compiled without issues. In other words, starting with JDK 16, Java inner classes can have static members and static initializers.This allows us to tackle the initialization-on-demand holder design pattern from another angle. We can replace the static nested class, LazyConnection, with a local inner class as follows:
public class Connection { // singleton
private Connection() {
}
public static Connection get() {
class LazyConnection { // holder
static final Connection INSTANCE = new Connection();
static {
System.out.println(“Initializing connection …”
+ INSTANCE);
}
}
return LazyConnection.INSTANCE;
}
}
Now, the LazyConnection is visible only in its containing method, get(). As long as we don’t call the get() method, the connection will not be initialized.