Week 2 Day 3 — Constructors, this, and Initialization
Goal
Today I want construction as the moment an object becomes valid.
Main questions:
- What does a constructor do?
- What is the default constructor?
- How do
this()andthis.fielddiffer? - In what order does Java initialize?
- Why does Spring prefer constructors for injection?
1. Constructors
A constructor’s job is to establish invariants and assign fields.
public class OrderService {
private final OrderRepository orders;
private final Clock clock;
public OrderService(OrderRepository orders, Clock clock) {
this.orders = Objects.requireNonNull(orders);
this.clock = Objects.requireNonNull(clock);
}
}
this.orders is the field. The parameter orders would shadow the field without this..
After the constructor returns, a well-designed object is ready. No init() the caller might forget.
Memory sentence:
If the object is not valid, the constructor should throw. After
new, it is ready.
2. Default and overloaded constructors
If I write no constructor, Java gives a public no-arg constructor.
If I write any constructor, that default disappears.
public class User {
private final String name;
public User(String name) {
this.name = name;
}
}
// new User(); // compile error — no no-arg constructor
JPA and some proxies want a no-arg constructor. Frameworks then set fields. That is a framework constraint, not a good domain default.
I can overload constructors. I chain with this(...) as the first statement:
public User(String name) {
this(name, true);
}
public User(String name, boolean active) {
this.name = Objects.requireNonNull(name);
this.active = active;
}
this(...) calls another constructor of this class. super(...) calls the superclass (Week 2 Day 4).
3. Initialization order
For new Child(...) the JVM roughly does:
- Allocate heap memory, default fields (
0,null,false) - Superclass constructor chain, starting at
Object - Superclass instance initializers and field initializers
- Superclass constructor body
- Then the same for the child: field initializers, then constructor body
class Demo {
private final int n = compute(); // runs before constructor body
private final int extra;
Demo(int extra) {
this.extra = extra;
}
private int compute() { return 1; }
}
Calling overridable instance methods from a constructor is a trap: the subclass fields are not initialized yet, but the overridden method already runs.
4. Factory methods
Sometimes a constructor is not the clearest API:
public static Money eur(long cents) {
return new Money("EUR", cents);
}
Factories can name the intent, cache instances, or return a subtype. The constructor stays private if I want all creation to go through the factory.
Spring’s @Bean methods are factories the container calls.
5. Spring connection
Constructor injection is the default for a reason:
- all dependencies are visible
- fields can be
final - the object cannot exist half-wired
- unit tests call the same constructor
@Service
public class OrderService {
private final OrderRepository orders;
public OrderService(OrderRepository orders) {
this.orders = orders;
}
}
With a single constructor, Spring injects it without @Autowired on Spring Boot 2.2+.
Setter injection allows optional dependencies and circular references (usually a design smell). The object exists before the setter runs.
Field injection skips the constructor API entirely.
JPA entities: a protected or package no-arg constructor for the provider, plus a public constructor for my code that sets required state. I do not call the no-arg constructor in application code.
Memory sentence:
Spring calls a constructor. I should write the constructor I want tests to call.
6. Common traps
Trap 1: Business logic in setters that must run “after construction.”
Callers and frameworks can skip them. Put required work in the constructor or in an explicit factory.
Trap 2: Doing I/O in a constructor (open a DB connection, read files).
Construction should be cheap and local. Spring beans that need startup I/O use ApplicationRunner or lazy methods. Constructors that throw for missing arguments are fine.
Trap 3: Overridable methods in constructors.
The subclass is not initialized yet.
Trap 4: Assuming JPA’s no-arg constructor is my domain API.
It exists for the provider. My application uses the constructor that takes real data.
Practice Questions and Answers
Question 1
What happens if I write one constructor that takes arguments?
Answer:
Java no longer provides the public no-arg constructor. Callers must pass the arguments, or I add another constructor myself.
Question 2
Why is constructor injection preferred in Spring?
Answer:
Dependencies are required and visible, fields can be final, and the bean is complete when constructed. Tests instantiate the same way. I do not need @Autowired if there is only one constructor.
Question 3
What is the difference between this.orders and this(orders)?
Answer:
this.orders is the field on the current instance. this(orders) is a call to another constructor of the same class, and it must be the first statement.
Question 4
Why is calling an overridable method from a constructor dangerous?
Answer:
The overridden method in the subclass runs before the subclass constructor body and field initializers finish. It can read null or 0 and publish this too early.
Question 5
How do I handle a JPA entity that also needs a real constructor?
Answer:
I keep a no-arg constructor for the provider (protected/package) and a public constructor that sets required fields for application code. I do not leave the entity valid only after setters.
Memory sentences
After
new, the object is valid — or the constructor threw.
One constructor is the Spring injection point and the test entry point.
this.fieldis a field.this(...)is another constructor.
Next: Week 2 Day 4 — Inheritance, super, Override vs Overload