Creational patterns control how objects get made: exactly one (Singleton), step by step (Builder), or without the caller naming the concrete class (Factory).
Singleton
| Variant | Lazy? | Thread-safe? | Notes |
|---|---|---|---|
Eager static final field | No — created at class init | Yes (class init is thread-safe) | Simplest; fine when creation is cheap |
synchronized getInstance() | Yes | Yes | Lock on every call — slow |
| Double-checked locking | Yes | Yes only with volatile | Verbose; easy to get wrong |
| Holder idiom (static nested class) | Yes | Yes | JVM class-loading guarantees; no locks |
enum with one constant | At enum init | Yes | Recommended (Effective Java): also safe against reflection and serialization |
public class Main {
// 1. Eager
static final class Eager {
private static final Eager INSTANCE = new Eager();
private Eager() {}
static Eager getInstance() { return INSTANCE; }
}
// 2. Double-checked locking — volatile prevents seeing a half-constructed object
static final class Dcl {
private static volatile Dcl instance;
private Dcl() {}
static Dcl getInstance() {
Dcl local = instance;
if (local == null) { // 1st check, no lock
synchronized (Dcl.class) {
local = instance;
if (local == null) { // 2nd check, with lock
instance = local = new Dcl();
}
}
}
return local;
}
}
// 3. Holder idiom — Holder is initialised on first getInstance() call
static final class Lazy {
private Lazy() { System.out.println("Lazy created"); }
private static final class Holder { static final Lazy INSTANCE = new Lazy(); }
static Lazy getInstance() { return Holder.INSTANCE; }
}
// 4. Enum
enum Config {
INSTANCE;
private int timeout = 30;
int timeout() { return timeout; }
}
public static void main(String[] args) {
System.out.println(Eager.getInstance() == Eager.getInstance());
System.out.println(Dcl.getInstance() == Dcl.getInstance());
System.out.println("before first use");
System.out.println(Lazy.getInstance() == Lazy.getInstance());
System.out.println(Config.INSTANCE.timeout());
}
}true
true
before first use
Lazy created
true
30Breaking a singleton — and the enum defence
import java.lang.reflect.Constructor;
public class Main {
static final class Classic {
static final Classic INSTANCE = new Classic();
private Classic() {}
}
enum Single { INSTANCE }
public static void main(String[] args) throws Exception {
Constructor<Classic> c = Classic.class.getDeclaredConstructor();
c.setAccessible(true); // bypass `private`
Classic second = c.newInstance();
System.out.println("classic still single? " + (second == Classic.INSTANCE));
Constructor<Single> e = Single.class.getDeclaredConstructor(String.class, int.class);
e.setAccessible(true);
try {
e.newInstance("INSTANCE", 0);
} catch (IllegalArgumentException ex) {
System.out.println(ex.getMessage());
}
}
}classic still single? false
Cannot reflectively create enum objectsimport java.io.*;
public class Main {
static final class Settings implements Serializable {
private static final long serialVersionUID = 1L;
static final Settings INSTANCE = new Settings();
private Settings() {}
private Object readResolve() { // deserialization returns this instead
return INSTANCE;
}
}
public static void main(String[] args) throws Exception {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
try (ObjectOutputStream out = new ObjectOutputStream(bytes)) {
out.writeObject(Settings.INSTANCE);
}
try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray()))) {
Object copy = in.readObject();
System.out.println(copy == Settings.INSTANCE); // false without readResolve
}
}
}trueBuilder
For classes with many parameters, especially optional ones. Replaces telescoping constructors and avoids the half-built objects of the JavaBeans setter style.
import java.util.ArrayList;
import java.util.List;
public class Main {
static final class Pizza {
private final String size; // required
private final boolean extraCheese; // optional
private final List<String> toppings; // optional
private Pizza(Builder b) {
this.size = b.size;
this.extraCheese = b.extraCheese;
this.toppings = List.copyOf(b.toppings);
}
static Builder builder(String size) { return new Builder(size); }
@Override
public String toString() {
return size + " pizza, cheese=" + extraCheese + ", toppings=" + toppings;
}
static final class Builder {
private final String size;
private boolean extraCheese;
private final List<String> toppings = new ArrayList<>();
private Builder(String size) { this.size = size; }
Builder extraCheese() { this.extraCheese = true; return this; }
Builder topping(String t) { toppings.add(t); return this; }
Pizza build() {
if (toppings.size() > 5) throw new IllegalStateException("too many toppings");
return new Pizza(this);
}
}
}
public static void main(String[] args) {
Pizza p = Pizza.builder("large")
.topping("olives")
.topping("basil")
.extraCheese()
.build();
System.out.println(p);
}
}large pizza, cheese=true, toppings=[olives, basil]StringBuilder · HttpRequest.newBuilder() · Stream.builder() · Locale.Builder · Lombok @Builder
Factories
public class Main {
sealed interface Shape permits Circle, Square {
double area();
static Shape of(String kind, double size) { // simple / static factory
return switch (kind) {
case "circle" -> new Circle(size);
case "square" -> new Square(size);
default -> throw new IllegalArgumentException("unknown shape: " + kind);
};
}
}
record Circle(double r) implements Shape {
public double area() { return Math.PI * r * r; }
}
record Square(double side) implements Shape {
public double area() { return side * side; }
}
public static void main(String[] args) {
Shape s = Shape.of("square", 3);
System.out.println(s + " area=" + s.area());
System.out.printf("%.2f%n", Shape.of("circle", 1).area());
System.out.println(Integer.valueOf(100) == Integer.valueOf(100)); // valueOf caches
}
}Square[side=3.0] area=9.0
3.14
trueWhich singleton implementation would you use and why?
Why must the instance field be volatile in double-checked locking?
When would you choose a builder over constructors?