Java Constructor Overloading
Table of Contents + β
In the last lesson you learned about Java constructors. So far each class had just one constructor, so there was just one way to build the object. Constructor overloading gives you several ways to create the same kind of object.
π€ Why do we need more than one constructor?
A single constructor forces every caller to pass every value, every time. This Book demands all three:
class Book { String title; String author; double price;
Book(String title, String author, double price) { this.title = title; this.author = author; this.price = price; }}What if you only have the title?
new Book("Java Basics")will not compile. The only constructor demands three arguments.- So you are forced to invent fake values for the author and price just to satisfy it.
- That hides real data behind made-up data.
Think of ordering coffee. The shop does not force you to specify milk, sugar, and an extra shot every time. It offers a few sensible ways to order. Constructor overloading gives your class the same choice.
π§© What is constructor overloading?
More than one constructor in the same class, each with a different parameter list. They all share the class name, so the parameter list is what tells them apart.
Two constructors count as different when their parameter lists differ in:
- Number of parameters. One takes two, another takes three.
- Type of parameter. One takes a
String, another takes anint. - Order of parameter types.
(String, int)versus(int, String)β legal but rare, and easy to misread.
What does not count: renaming parameters. Java only looks at the list of types, not the names, and there is no return type to change.
π’ Defining multiple constructors
Letβs give Book three ways to be created β a no-argument one, a title-only one, and a full one. This program builds one of each and prints it.
public class Main { public static void main(String[] args) { Book b1 = new Book(); // nothing known yet Book b2 = new Book("Java Basics"); // just a title Book b3 = new Book("Deep Java", "Alex", 499.0); // full details
b1.show(); b2.show(); b3.show(); }}
class Book { String title; String author; double price;
Book() { // no-arg constructor this.title = "Untitled"; this.author = "Unknown"; this.price = 0.0; }
Book(String title) { // title-only constructor this.title = title; this.author = "Unknown"; this.price = 0.0; }
Book(String title, String author, double price) { // full constructor this.title = title; this.author = author; this.price = price; }
void show() { System.out.println(title + " | " + author + " | " + price); }}Now the caller has three doors into the same class:
- Pass nothing and you get sensible placeholder values.
- Pass a title and the rest fill in.
- Pass everything and you control every field.
Output
Untitled | Unknown | 0.0Java Basics | Unknown | 0.0Deep Java | Alex | 499.0π― How the compiler picks the right one
Java matches the arguments you pass to a parameter list:
- It counts the arguments and checks their types, then finds the constructor that fits.
new Book()β the no-arg version.new Book("Java Basics")β the one-Stringversion.new Book("Deep Java", "Alex", 499.0)β the three-parameter version.- This happens at compile time, before the program runs β so we say the compiler picks it.
- No match means the code will not compile. Java reports no matching constructor was found.
Here is a mismatch:
Book b = new Book("Java Basics", "Alex"); // β two args: no constructor takes (String, String)No constructor takes (String, String), so this will not compile. Fix it by passing the price too, or add a (String, String) constructor if that makes sense.
π Constructor chaining with this(β¦)
Look back at the three Book constructors. The no-arg and title-only ones both repeat the same "Unknown" and 0.0 setup that the full constructor already does. Why copy it?
- One constructor can call another in the same class using this(β¦). This is constructor chaining.
- One βmainβ constructor does the real setup.
- The smaller ones hand their work to it, supplying default values.
This version keeps the same behaviour, but the real setup now lives in one place.
public class Main { public static void main(String[] args) { Book b1 = new Book(); Book b2 = new Book("Java Basics"); Book b3 = new Book("Deep Java", "Alex", 499.0);
b1.show(); b2.show(); b3.show(); }}
class Book { String title; String author; double price;
Book() { this("Untitled", "Unknown", 0.0); // calls the full constructor with all defaults }
Book(String title) { this(title, "Unknown", 0.0); // calls the full constructor, supplies a title }
Book(String title, String author, double price) { // the one place setup really happens this.title = title; this.author = author; this.price = price; }
void show() { System.out.println(title + " | " + author + " | " + price); }}Read this("Untitled", "Unknown", 0.0) as βrun the constructor that takes a title, an author, and a price, with these valuesβ:
- The smaller constructors no longer touch the fields directly. They feed defaults into the full one.
- Change how a book is set up once, in a single spot.
Output
Untitled | Unknown | 0.0Java Basics | Unknown | 0.0Deep Java | Alex | 499.0One firm rule: a this(...) call must be the very first statement in the constructor. Nothing can come before it. Java wants the core setup to run before any other code in the body.
this(...) must be first
The this(...) call has to be the first statement in the constructor. If you put any line before it, the code will not compile. Java needs the chained setup to happen before the rest of the constructor body runs.
π Worked example: a Rectangle
Letβs tie it together with a Rectangle that has three ways to be made β a default unit square, a square from one side, and a full width-by-height rectangle. All chain to one real constructor. This program builds each and prints its area.
public class Main { public static void main(String[] args) { Rectangle r1 = new Rectangle(); // default 1 by 1 Rectangle r2 = new Rectangle(4); // square, side 4 Rectangle r3 = new Rectangle(3, 5); // full rectangle
r1.showArea(); r2.showArea(); r3.showArea(); }}
class Rectangle { double width; double height;
Rectangle() { this(1, 1); // a default unit square }
Rectangle(double side) { this(side, side); // a square reuses the full constructor }
Rectangle(double width, double height) { // the real setup, with a guard this.width = width < 0 ? 0 : width; this.height = height < 0 ? 0 : height; }
void showArea() { System.out.println("Area: " + (width * height)); }}Walk through it:
new Rectangle()chains tothis(1, 1)β a one-by-one square.new Rectangle(4)chains tothis(4, 4)β a square of side 4.new Rectangle(3, 5)runs the full constructor straight away.- Every path ends in the same constructor β the one that stores the fields and guards against negative sizes. So a negative value can never sneak in, whichever door the caller used.
Output
Area: 1.0Area: 16.0Area: 15.0The payoff: validation lives in one constructor, and the other two get that safety for free by chaining to it. That is why chaining is worth the small effort.
β οΈ Common Mistakes
A few overloading slip-ups catch almost everyone.
Putting code before the this(β¦) call. The chained call must come first. Any statement before it stops the code compiling.
// β Wrong: a line runs before this(...)Rectangle() { System.out.println("Making a rectangle"); // not allowed here this(1, 1);}
// β
Right: this(...) is the very first statementRectangle() { this(1, 1);}Making constructors call each other in a loop. If A calls B and B calls A, the chaining never ends. Java catches this and refuses to compile.
// β Wrong: each one calls the other foreverBook() { this("Untitled");}Book(String title) { this(); // back to the no-arg one, which calls this() again...}
// β
Right: smaller constructors all chain toward one real constructorBook() { this("Untitled", "Unknown", 0.0);}Book(String title) { this(title, "Unknown", 0.0);}Two constructors with the same parameter list. Overloading needs the types to differ. Two constructors with the same types are a duplicate and will not compile. Renaming the parameters does not help β Java only looks at types.
class Book { // β Wrong: both take a single String, so they clash Book(String title) { this.title = title; } Book(String author) { this.author = author; }}β Best Practices
A few habits that make overloaded constructors pleasant to use:
- Pick one main constructor that sets every field, then have the others chain to it with
this(...). - Keep the real setup in one place. Validation and field assignment belong in the main constructor, so every path gets the same safety.
- Offer only constructors that make sense β one for each natural way to build the object, not every possible combination.
- Use sensible defaults when a smaller constructor fills in missing values, like
0or"Unknown". - Avoid relying on parameter order to tell constructors apart.
(String, int)versus(int, String)is legal but easy to misread; prefer different counts or types. - Keep constructors light. Their job is to set the object up, not run heavy work.
π§© What Youβve Learned
Nicely done. Letβs recap.
- β Constructor overloading means several constructors in one class, each with a different parameter list.
- β Parameter lists differ by the number, type, or order of their parameters.
- β It gives callers flexible ways to build an object, like default values versus full details.
- β The compiler picks the matching constructor by counting and type-checking the arguments you pass.
- β
Constructor chaining with
this(...)lets one constructor call another to avoid repeating setup code. - β
A
this(...)call must be the very first statement in the constructor.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What makes two constructors count as overloaded?
Why: Overloaded constructors share the class name but differ in the number, type, or order of their parameters.
- 2
How does the compiler choose which constructor to run?
Why: Java matches the count and types of your arguments to a constructor's parameter list while compiling.
- 3
What does this(...) do inside a constructor?
Why: this(...) chains to another constructor in the same class so setup logic lives in one place.
- 4
Where must a this(...) call appear in a constructor?
Why: A this(...) call has to be the first statement; any line before it stops the code from compiling.
π Whatβs Next?
You leaned on this a lot here, both as this.field and as this(...). It shows up everywhere in object-oriented code, so it is worth understanding fully. Letβs look at the this keyword next.