Avoid Constructor Pitfalls and Cut Code in Half with Builder Pattern
This article explains common constructor mistakes such as starting threads, calling overridable methods, and using too many parameters, demonstrates how the Builder pattern and default-value constructors can dramatically reduce boilerplate, improve readability and performance, and offers practical guidelines for safe constructor design.
The author introduces a common pain point: creating objects with verbose constructors and repetitive setter calls.
1. Avoid Pitfalls! These constructor tricks can explode
Starting a thread in a constructor? Bad idea!
public class Task {
private Thread thread;
public Task() {
thread = new MyThread(); // subclass may not be fully initialized, thread runs early!
thread.start(); // results: data corruption, NPE
}
}Advice: never start asynchronous work in a constructor; if unavoidable, mark the class final .
Calling overridable methods? Trap!
public class Payment {
public Payment() {
validate(); // if a subclass overrides validate(), trouble!
}
public void validate() { /* basic checks */ }
}Advice: make validate private or final to avoid accidental overrides.
Too many parameters? Stacked constructors are a disaster!
// Parameter hell! Adding a field forces changes in three constructors
public User(int id) { this(id, null); }
public User(int id, String name) { this(id, name, null); }
// Adding age? Keyboard smash!Advice: avoid long parameter lists; consider alternative patterns.
2. Code‑saving tricks: halve the lines
1. Builder pattern: tame the parameter monster
User user = new User.Builder()
.id(1001) // chain call, clear
.name("架构君")
.age(30) // add without breaking old code
.build();Effect comparison:
Traditional stacked constructors: 15+ lines, low readability, limited extensibility. Builder pattern: 1 line, ★★★★★ readability, ★★★★★ extensibility.
2. Default values in constructor: avoid repeated work
public class Order {
private Date createTime;
private String status;
public Order() {
this.createTime = new Date(); // auto‑generated creation time
this.status = "UNPAID"; // default status
}
}Original verbose usage required separate setCreateTime and setStatus calls for each new instance; moving fixed initialization into the constructor cuts the calling code roughly in half.
3. Performance tip: constructors are 2.6× faster than setters
Benchmark (10 000 object creations): constructor initialization 72.6 ms vs setter‑by‑setter 192 ms (≈2.64× slower).
Reason: the JVM allocates memory for objects contiguously in the heap, while setters cause scattered writes.
3. Advanced usage: the art of constructor boundaries
Don’t overstep : constructors should only perform essential initialization (default values, required‑parameter validation). Move heavy operations such as DB connections or file I/O to separate methods.
Keep it pure : avoid invoking other class methods from a constructor to prevent “construction‑before‑completion” deadlocks.
Final advice : if a constructor has more than four parameters, switch to the Builder pattern without hesitation.
Conclusion: Using the Builder pattern and disciplined constructor design can dramatically reduce boilerplate, improve code readability, and boost performance.
Java Architect Essentials
Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
