Constructors
Runs once, when the object is created, to put it into a valid state.
A constructor has the class’s name and no return type:
public class Point {
private final int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
this.x is the field; x alone is the parameter. Without this. you would assign the parameter to itself, which compiles and does nothing: a genuinely confusing bug the first time.
The default constructor
Write no constructor and you get a no-argument one for free, which leaves fields at their defaults (0, false, null).
Write any constructor and the free one disappears. This surprises people:
Point p = new Point(); // no longer compiles once Point(int,int) exists
If you want both, declare both.
Overloading
Several constructors differing in parameters:
public Point() { this(0, 0); }
public Point(int x, int y) { this.x = x; this.y = y; }
this(...) chains to another constructor and must be the first statement. Keeping the real work in one constructor and having the others delegate to it means the initialisation logic exists once.
What belongs in one
Put the object into a valid state and stop. Validate arguments and fail immediately:
public Account(String id, long balance) {
if (balance < 0) throw new IllegalArgumentException("negative balance");
...
}
Rejecting bad input at construction means every method afterwards can assume the object is sound.
What does not belong: heavy work, I/O, network calls, or anything that can leave a half-built object lying around. If construction needs to do real work, use a static factory method that does the work and then constructs.
Do not call overridable methods
Calling a method that a subclass can override, from a constructor, runs the subclass version before the subclass’s own fields are initialised. The override then sees nulls and zeros. Make such methods private or final.
Records
For a plain value carrier, a record gives you the constructor, the accessors, equals, hashCode and toString:
record Point(int x, int y) { }
Validation goes in a compact constructor:
record Point(int x, int y) {
Point {
if (x < 0) throw new IllegalArgumentException();
}
}