Constructors & this
Default and parameterised constructors, overloading, this(...) chaining and initialisation order.
In the previous lesson we created objects and then filled in their fields one by one. That is clumsy, and it is easy to forget a field and leave an object half-built. A constructor is special code that runs when an object is created, so every object starts life in a valid state.
Writing a constructor#
A constructor looks like a method, but it has the same name as the class and no return type (not even void):
new Student("Kiran", 9) does three things:
- Allocates memory for a new
Studentobject, with fields set to default values. - Runs the constructor with the arguments you passed.
- Returns a reference to the finished object.
The this keyword#
Inside a constructor or instance method, this is a reference to the current object. It is needed when a parameter has the same name as a field:
Without this., the name name refers to the parameter, which shadows the field. Writing name = name; just assigns the parameter to itself, and the field stays null. The compiler will not stop you, so this bug is surprisingly common.
this can also be passed as an argument (register(this)) or returned from a method (useful for chaining calls).
The default constructor#
If you write no constructor at all, Java quietly adds a no-argument constructor that does nothing. That is why new Book() worked in the last lesson.
As soon as you write any constructor, that free default disappears:
If you still want a no-arg constructor, declare it yourself.
Overloaded constructors#
Like methods, constructors can be overloaded with different parameter lists, giving callers several convenient ways to create an object:
Constructor chaining with this(...)#
Notice how the shorter constructors above call the full one with this(...). This is constructor chaining: all the real initialisation lives in one place, and the other constructors just supply defaults. The rules:
this(...)must be the first statement in the constructor.- A constructor can call either
this(...)orsuper(...)(the parent's constructor, covered in the inheritance lesson), not both. - Chains cannot be circular.
Validating in the constructor#
A constructor is the perfect place to reject bad data, so an invalid object can never exist:
throw new IllegalArgumentException(...) stops construction with an error; try/catch handles it. Both are explained fully in the exceptions lesson.
final fields#
A field marked final must be assigned exactly once, either where it is declared or in every constructor, and can never change afterwards:
Making fields final whenever possible makes objects easier to reason about. Objects whose fields are all final (and hold immutable values) are immutable, like String.
Initialisation order#
When you call new, things happen in a well-defined order:
- Fields get default values (
0,false,null). - Field initialisers and instance initialiser blocks
{ ... }run, top to bottom. - The constructor body runs.
(If a parent class is involved, the parent's part is initialised first.) Instance initialiser blocks are rare in practice. Prefer putting logic in constructors.
Copy constructors#
A constructor that takes another object of the same class creates an independent copy:
If the copy constructor had written this.songs = other.songs;, both playlists would share one array, and changing one would change the other.
Too many parameters? The builder idea#
A constructor with seven parameters, such as new User("a", "b", null, true, false, 3, null), is hard to read. Common solutions:
- Provide a few overloaded constructors for common cases.
- Use a builder object with named setter-style methods:
Each builder method returns this, which enables the fluent chain. You will see builders everywhere in Java libraries (HttpRequest.newBuilder(), StringBuilder, Lombok's @Builder). It uses private and a static nested class, which are covered in the next two lessons.
Common mistakes#
- Adding a return type:
void Student(...)is a method named Student, not a constructor, so your fields never get set. - Forgetting
this.when parameter and field share a name. - Expecting the default constructor after declaring another constructor.
- Calling
this(...)anywhere other than the first line. - Doing heavy work (network calls, file reads) in constructors. Keep them focused on initialisation.
What's next#
Constructors set up each object's own data. But some data belongs to the class as a whole, such as a counter of how many objects exist. That is the job of the static keyword, up next.
Check your understanding
Quick quiz
1.A class declares only
Person(String name) { ... }. What doesnew Person()do?2.Where must a
this(...)call appear inside a constructor?3.Inside a constructor with parameter
name, what doesname = name;do?
Finished reading?
Mark this lesson complete to track your progress.