The ID card that said “null”
The fest ID cards are printed. Riya's program created each Student with new Student() and then set the name, roll and course in three separate lines. For one volunteer she forgot the name line, and his card proudly says null · 0 · null.
Sir, I keep forgetting one of the lines after new Student(). Can Java force me to give the name?
Yes: write a constructor. It's a special block that runs automatically inside new. If it takes a name and a roll number, nobody can create a Student without them.
And the course? Most students are PG-DAC.
Write a second constructor without the course that passes "PG-DAC" to the first one using this(...). That's constructor chaining: one place does the real work, the others just fill in defaults.
What is a constructor?
A constructor is a special member of a class that is invoked automatically when an object is created with new, to initialize that object. It has the same name as the class and no return type, not even void. If a class declares no constructor, the compiler supplies a default constructor with no parameters. Constructors can be overloaded, and one constructor can call another of the same class with this(...), which is called constructor chaining.
The admission desk for new objects. new builds an empty object; the constructor fills in its details before anyone gets to use it.
Words you'll meet in this lesson
Student(String name, int roll).{ … } in the class that runs for every new object.Constructor facts at a glance
| Name | Exactly the class name: Student(…) |
|---|---|
| Return type | None. void Student() compiles, but it is an ordinary method, not a constructor. |
| Called by | new, automatically, once per object. You can't call it like a method. |
| Default constructor | Added by javac only if the class has no constructor at all |
this(...) | Must be the first statement (Java 8 to 21); constructors can't call each other in a cycle |
| Inherited? | No. Each class declares its own (parents are called with super(...), Lesson 7.3). |
Exam tip: "Does a constructor return anything?" It has no return type and can't return a value (a bare return; is allowed). new is what produces the reference to the new object.
Three kinds of constructor, and how they share work
Default
You wrote none, so javac adds Student() { }. Fields keep their default values.
No-arg
You write Student() { … } to set your own starting values.
Parameterized
Student(String name, int roll): the caller must supply the values.
Overloading plus chaining: one constructor does the real work
class Pizza {
String size;
String crust;
int price;
Pizza() { // no-arg
this("Medium");
}
Pizza(String size) { // 1 parameter
this(size, "Classic");
}
Pizza(String size, String crust) { // 2 parameters: does the real work
this.size = size;
this.crust = crust;
price = size.equals("Large") ? 499 : 299;
}
void show() {
System.out.println(size + " | " + crust + " | Rs " + price);
}
}
public class Main {
public static void main(String[] args) {
new Pizza().show();
new Pizza("Large").show();
new Pizza("Small", "Thin").show();
}
}new Pizza() → this("Medium") → this("Medium", "Classic"). Only the 2-parameter constructor contains the logic, so a price change is made in one place.
Constructor vs method
| Constructor | Method | |
|---|---|---|
| Name | Same as the class | Any name (camelCase verb) |
| Return type | None | Required (void if nothing) |
| Called | Automatically by new, once per object | Explicitly, any number of times |
| Default provided? | Yes, if you write none | No |
| Inherited? | No | Yes (Module 7) |
| Purpose | Set up a new object | Behaviour of an existing object |
The void trap: adding a return type turns a constructor into a method with the same name as the class. It compiles, javac quietly adds a default constructor, and your setup code never runs:
class Student {
String name;
void Student(String name) { // looks like a constructor...
this.name = name;
}
}
public class Main {
public static void main(String[] args) {
Student s = new Student(); // works: javac added a default constructor
System.out.println(s.name);
}
}What happens inside new, in order
Memory and default values
The JVM reserves heap memory for one Account and sets every field to its default:Account a = new Account("Riya");null,0.0,false. This happens before any of your code runs.The matching constructor is chosen
Exactly like overloaded methods (Lesson 5.4), javac picks the constructor whose parameter list matches the arguments. In bytecode every constructor is a special method namedAccount(String holder) { … } // chosen: one String Account(String holder, double balance) { … }<init>; that's the name you see in stack traces.this(...) runs first
A new frame for the 2-parameter constructor is pushed on top, working on the same object. Only when it finishes does the rest of the 1-parameter body run.Account(String holder) { this(holder, 0.0); // must be the first statement System.out.println("1-arg body"); }Field initializers and instance blocks
javac copies these, in the order they are written, into the start of every constructor that doesn't begin withString type = "SAVINGS"; { System.out.println("instance block"); }this(...)(right after the hidden call to the parent constructor). So they run once per object, before the constructor body.javap -cshows the copiedputfieldinstructions.Bodies finish, reference handed back
The 2-parameter body runs, its frame pops, then the 1-parameter body runs and pops. Finallythis.holder = holder; this.balance = balance;newgives the reference of the finished object toa. Nobody ever sees a half-built Account.
Chained constructors: both views
Left: the code being run and what it prints. Right: constructor frames stacking up for one object, and that object's fields filling in.
new Account("Riya") 6 steps- new: before any of your code runs, an Account exists with default values.
- Choose: the call passes one String, so the 1-parameter constructor runs. Its first line is
this(h, 0.0). - Chain:
this(...)calls the 2-parameter constructor for the same object. The 1-arg body waits. - Initializer:
type = "SAVINGS"runs now, before the 2-arg body. Nothing is printed yet. - 2-arg body: it prints
type=SAVINGS(already set), then stores the holder and balance. - Finish: control returns to the 1-arg body, which prints its line. Only now does
aget the object.
- The JVM allocates object #1 on the heap and zeroes it:
null,0.0,null.adoesn't have it yet. - The 1-parameter constructor's frame is pushed. Its hidden
thispoints to #1. this(h, 0.0)pushes a second constructor frame on top. Both frames work on the same object #1.- The initializer for
typewas copied by javac into the 2-arg constructor (the one that doesn't start withthis(...)), so it runs here, once. - The 2-arg body copies the parameters into #1's fields. Then its frame is popped.
- The 1-arg body finishes and its frame pops too. Only now does
newhand the reference of the complete object toa.
Constructors in real systems
Good constructors make it impossible to create a broken object. That single idea prevents a large share of production bugs.
Watch it with an example: opening a bank account (KYC)
A bank won't open an account without a name and PAN. The constructor makes these details compulsory, and constructor chaining fills in sensible defaults.
- Opening:
new Account("Riya", "ABCDE1234F"). The constructor's parameters are exactly what the bank requires. - Memory first: the JVM creates object #1 with default values:
nullfor references, 0 for numbers. - Chaining: the 2-argument constructor calls
this(n, p, 1000), passing a default minimum balance.this(…)must be the first line. - Main constructor: sets name, PAN and balance. All the real work is in one place.
- Ready: only now is the reference stored in
a. Nobody can ever see a half-built account. - Safety: because the class defines constructors with parameters, Java gives no default no-arg constructor.
new Account()doesn't compile, so required details can't be skipped.
1. Mandatory details (KYC)
A bank's Customer class takes name, PAN and date of birth in its only constructor. There is simply no way to create a customer without them.
2. Sensible defaults with chaining
An Order created from the app gets status PLACED and payment mode UPI unless told otherwise. Shorter constructors pass defaults to one full constructor.
3. The JDK itself
You have already used overloaded constructors: new Scanner(System.in), new StringBuilder() and new StringBuilder(64), new String(charArray). Different arguments, different setup.
4. Frameworks and dependency injection
Frameworks such as Spring create your service objects for you and pass the parts they need (a repository, a payment client) into the constructor. This is called constructor injection.
Examples are simplified. Real constructors usually also validate their inputs, and some libraries need a no-arg constructor to create objects from database rows or JSON.
Fest registration with constructors
class Student {
String name;
int roll;
String course;
Student(String name, int roll) {
this(name, roll, "PG-DAC"); // default course
}
Student(String name, int roll, String course) {
this.name = name;
this.roll = roll;
this.course = course;
System.out.println("Registered: " + name);
}
void printCard() {
System.out.println(roll + " | " + name + " | " + course);
}
}
public class Main {
public static void main(String[] args) {
Student a = new Student("Riya", 101);
Student b = new Student("Kabir", 102, "PG-DBDA");
a.printCard();
b.printCard();
}
}Student(String name, int roll)The short form for most students: it chains to the full constructor with the default course.this(name, roll, "PG-DAC");Must be the first statement. All the real work happens in one place.this.name = name;The parameter has the same name as the field;this.namemeans the field (Lesson 6.3).Registered: …Printed by the 3-parameter constructor for both students, because both paths end there.
Two tickets, two ways in
Watch the constructor frames: the second ticket goes through two constructors, both working on the same object. The field initializer runs once per object.
Worked examples: real problems, solved and traced in memory
Three business objects that must be set up correctly from the very first moment. Run each solution, then step through it: watch new create the object with default values, constructor frames being pushed (sometimes two or three for one object), and the fields filling in.
Open an account with or without a deposit
Problem statement
A bank lets customers open a savings account with an initial deposit, or with zero balance. Write two constructors so that both ways work and the setup code is written only once.
new Account("Riya", 2000)
new Account("Kabir")Riya: Rs 2000.0 Kabir: Rs 0.0
Approach
- Write the full constructor
Account(String holder, double balance)that stores both values. - Write
Account(String holder)that callsthis(holder, 0): zero balance is the default. - Print both accounts to check that each object got the right values.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Delivery fee decided by the constructor
Problem statement
A food delivery app charges Rs 30 delivery on orders below Rs 199 and nothing above that. Every FoodOrder must know its delivery fee and total as soon as it is created.
new FoodOrder(149) new FoodOrder(260)
Order Rs 149 -> pay Rs 179 Order Rs 260 -> pay Rs 260
Approach
- The constructor receives the item total and stores it.
- It decides the delivery fee with one
if, using a field initializer for the minimum order value. - It stores
payable = itemTotal + deliveryFee, so callers never have to calculate it.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Three constructors, one chain
Problem statement
New joiners are added by HR with just a name (department "Trainee", salary Rs 25,000), sometimes with a department (salary Rs 25,000), and sometimes with all three details. Write the constructors so that only one of them assigns the fields.
new Employee("Sneha")
new Employee("Rahul", "Finance", 52000)Sneha | Trainee | Rs 25000 Rahul | Finance | Rs 52000
Approach
- The 3-parameter constructor is the only one that assigns fields.
Employee(name, dept)callsthis(name, dept, 25000).Employee(name)callsthis(name, "Trainee"), which in turn chains to the full one: three frames for one object.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Check yourself
Quick revision
Lesson 6.2 in one screen
- Constructor: same name as the class, no return type, runs automatically inside
new. - No constructor written → javac adds a default no-arg one. Write any constructor → it's gone.
- Kinds: default, no-arg, parameterized. Constructors can be overloaded.
this(...)calls another constructor of the same class: first statement, no cycles.- Order: defaults → field initializers and instance blocks (in written order) → constructor body.
- With chaining, initializers run once, in the constructor that doesn't start with
this(...). void ClassName()is a method, not a constructor. Constructors are not inherited.
Interview and viva questions
What is the difference between a default constructor and a no-arg constructor?
Both take no parameters. The default constructor is generated by javac only when the class declares no constructor; it just calls the parent constructor. A no-arg constructor is one you write yourself, usually to set starting values.
Why doesn't a constructor have a return type?
It doesn't produce a value; it initializes an object that new has already created. The new expression is what evaluates to the reference. If you add a return type, Java treats it as a normal method.
Why must this(...) be the first statement?
So the object is fully set up by the other constructor before the current one uses it, and so initialization runs exactly once. javac enforces it with "call to this must be first statement in constructor" (Java 8 to 21; Java 25 relaxes the rule slightly with flexible constructor bodies).
Can a constructor be private?
Yes. Then only code inside the class can create objects. It's used for utility classes that should never be instantiated, and for patterns like Singleton or factory methods (LocalDate.of(…) is a factory method rather than a public constructor).
In what order does Java initialize an object?
Memory is allocated and fields get default values; the parent constructor runs; then field initializers and instance initializer blocks run in the order written; then the rest of the constructor body. With this(...), the called constructor finishes before the caller's body continues.
Practice problems
Every problem here is about setting objects up correctly the moment they are created. Decide which values are mandatory and which can have defaults before you write the constructors. The last three are real business tasks.
Circle with a constructor
Write a Circle class whose constructor takes the radius. Add area() and print the area of circles with radius 7 and 2.5, rounded to 2 decimals.
radius = 7, radius = 2.5
Area 1: 153.94 Area 2: 19.63
Show solution and explanation
class Circle {
double radius;
Circle(double radius) {
this.radius = radius;
}
double area() {
return Math.PI * radius * radius;
}
}
public class Main {
public static void main(String[] args) {
Circle c1 = new Circle(7);
Circle c2 = new Circle(2.5);
System.out.printf("Area 1: %.2f%n", c1.area());
System.out.printf("Area 2: %.2f%n", c2.area());
}
}How it works: the radius is required, so it is a constructor parameter: a Circle without a radius can't be created. new Circle(7) passes an int, which is widened to double automatically.
No-arg and parameterized together
A Book can be created with a title and price, or with nothing (title "Untitled", price 0). Write both constructors and print one book of each kind.
new Book()
new Book("Java Basics", 450)Untitled - Rs 0 Java Basics - Rs 450
Show solution and explanation
class Book {
String title;
int price;
Book() {
title = "Untitled";
price = 0;
}
Book(String title, int price) {
this.title = title;
this.price = price;
}
void show() {
System.out.println(title + " - Rs " + price);
}
}
public class Main {
public static void main(String[] args) {
new Book().show();
new Book("Java Basics", 450).show();
}
}How it works: two overloaded constructors; javac picks one by the argument list. Because we wrote constructors, javac adds no default one; that's why we had to write Book() ourselves.
Copy constructor
Write a Point class with a normal constructor and a copy constructor Point(Point other). Copy a point, move the copy, and show the original is unchanged.
p1 = (3, 4); p2 = copy of p1; p2.x = 10
p1 = (3, 4) p2 = (10, 4) Same object? false
Show solution and explanation
class Point {
int x;
int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
Point(Point other) {
this(other.x, other.y);
}
}
public class Main {
public static void main(String[] args) {
Point p1 = new Point(3, 4);
Point p2 = new Point(p1);
p2.x = 10;
System.out.println("p1 = (" + p1.x + ", " + p1.y + ")");
System.out.println("p2 = (" + p2.x + ", " + p2.y + ")");
System.out.println("Same object? " + (p1 == p2));
}
}How it works: p2 = p1 would only copy the reference (Lesson 6.1). The copy constructor creates a new object and copies the field values, then chains to the main constructor so the setup code isn't repeated.
Square as a special rectangle
A Rectangle has a constructor for length and width, and a second constructor taking one side for a square that chains to the first. Print the area of a 6 × 4 rectangle and a 5 × 5 square.
new Rectangle(6, 4) new Rectangle(5)
Rectangle: 6 x 4 = 24 Square: 5 x 5 = 25
Show solution and explanation
class Rectangle {
int length;
int width;
Rectangle(int length, int width) {
this.length = length;
this.width = width;
}
Rectangle(int side) {
this(side, side);
}
int area() {
return length * width;
}
}
public class Main {
public static void main(String[] args) {
Rectangle r = new Rectangle(6, 4);
Rectangle sq = new Rectangle(5);
System.out.println("Rectangle: " + r.length + " x " + r.width + " = " + r.area());
System.out.println("Square: " + sq.length + " x " + sq.width + " = " + sq.area());
}
}How it works: Rectangle(int side) does no work of its own; it passes the side twice to the full constructor. If you later add validation (no negative sides), you add it once and both paths get it.
Normalising time in the constructor
A Duration is created from hours and minutes, but callers sometimes pass minutes over 59 (like 2 h 135 min). Make the constructor convert extra minutes into hours.
new Duration(2, 135) new Duration(0, 45)
4 h 15 min 0 h 45 min
Show solution and explanation
class Duration {
int hours;
int minutes;
Duration(int hours, int minutes) {
this.hours = hours + minutes / 60;
this.minutes = minutes % 60;
}
void show() {
System.out.println(hours + " h " + minutes + " min");
}
}
public class Main {
public static void main(String[] args) {
new Duration(2, 135).show();
new Duration(0, 45).show();
}
}How it works: the constructor guarantees every Duration is stored in a clean form: 135 minutes = 2 hours (135 / 60) and 15 minutes (135 % 60). Code that uses Duration never has to handle "2 h 135 min".
Watch the initialization order
Predict the output, then run. The class has a field initializer, an instance block and two chained constructors.
new Lamp()
block: OFF Lamp(int): READY, 60W Lamp(): ON final: ON
Show solution and explanation
class Lamp {
String state = "OFF";
{
System.out.println("block: " + state);
state = "READY";
}
Lamp() {
this(60);
System.out.println("Lamp(): " + state);
}
Lamp(int watts) {
System.out.println("Lamp(int): " + state + ", " + watts + "W");
state = "ON";
}
}
public class Main {
public static void main(String[] args) {
Lamp l = new Lamp();
System.out.println("final: " + l.state);
}
}How it works: Lamp() starts with this(60), so Lamp(int) runs first. Inside it, before its body, the field initializer (OFF) and the instance block (prints OFF, sets READY) run in the order written. Then the Lamp(int) body prints READY and sets ON, and finally the rest of Lamp() prints ON.
Rejecting bad values
A Product constructor should never store a negative price. If the price is negative, store 0 and print a warning. Create one valid and one invalid product.
new Product("Notebook", 60)
new Product("Pen", -10)Warning: negative price for Pen, using 0 Notebook: Rs 60 Pen: Rs 0
Show solution and explanation
class Product {
String name;
int price;
Product(String name, int price) {
this.name = name;
if (price < 0) {
System.out.println("Warning: negative price for " + name + ", using 0");
this.price = 0;
} else {
this.price = price;
}
}
}
public class Main {
public static void main(String[] args) {
Product a = new Product("Notebook", 60);
Product b = new Product("Pen", -10);
System.out.println(a.name + ": Rs " + a.price);
System.out.println(b.name + ": Rs " + b.price);
}
}How it works: the constructor is the gatekeeper: every Product passes through it, so this one check protects every object. In real code you would usually throw an exception instead of silently fixing the value (Module 9), so the caller knows the input was wrong.
Hotel booking bill
A Booking takes guest name, nights and room rate. If the rate is not given, use the standard room rate of Rs 2,500. The constructor calculates the bill with 12% GST. Print two bookings.
new Booking("Sharma", 3)
new Booking("Iyer", 2, 4200)Sharma: 3 nights x Rs 2500 + 12% GST = Rs 8400.0 Iyer: 2 nights x Rs 4200 + 12% GST = Rs 9408.0
Show solution and explanation
class Booking {
String guest;
int nights;
int rate;
double total;
Booking(String guest, int nights) {
this(guest, nights, 2500);
}
Booking(String guest, int nights, int rate) {
this.guest = guest;
this.nights = nights;
this.rate = rate;
double base = nights * rate;
total = base + base * 12 / 100;
}
void print() {
System.out.println(guest + ": " + nights + " nights x Rs " + rate + " + 12% GST = Rs " + total);
}
}
public class Main {
public static void main(String[] args) {
new Booking("Sharma", 3).print();
new Booking("Iyer", 2, 4200).print();
}
}How it works:
- The short constructor supplies the default rate and chains to the full one, so the GST calculation exists only once.
- Sharma: 3 × 2500 = 7500, plus 900 GST = 8400.0. Iyer: 2 × 4200 = 8400, plus 1008 GST = 9408.0.
- GST rates on hotel rooms depend on the tariff and change over time; 12% is used here only as an example.
Loan EMI calculated at creation
A Loan object is created with principal, annual interest rate and tenure in months. The constructor computes the monthly EMI using EMI = P × r × (1 + r)n / ((1 + r)n − 1), where r is the monthly rate. Show the EMI, total payment and total interest for a Rs 5 lakh loan at 9% for 5 years.
principal = 500000, rate = 9% a year, months = 60
EMI: Rs 10379.18 Total paid: Rs 622750.66 Total interest: Rs 122750.66
Show solution and explanation
class Loan {
double principal;
double annualRate;
int months;
double emi;
Loan(double principal, double annualRate, int months) {
this.principal = principal;
this.annualRate = annualRate;
this.months = months;
double r = annualRate / 12 / 100;
double factor = Math.pow(1 + r, months);
emi = principal * r * factor / (factor - 1);
}
}
public class Main {
public static void main(String[] args) {
Loan home = new Loan(500000, 9, 60);
double totalPaid = home.emi * home.months;
System.out.printf("EMI: Rs %.2f%n", home.emi);
System.out.printf("Total paid: Rs %.2f%n", totalPaid);
System.out.printf("Total interest: Rs %.2f%n", totalPaid - home.principal);
}
}How it works:
- The EMI depends only on values given at creation, so the constructor works it out once and stores it.
- The yearly rate 9% becomes a monthly rate of 0.0075;
Math.powcomputes (1 + r)60. - This is the standard reducing-balance EMI formula used by banks; actual EMIs may differ slightly because of rounding rules and fees.
Train tickets: general and Tatkal
A TrainTicket has passenger, distance in km and quota. A general ticket costs Rs 0.60 per km. Tatkal adds 30% of the base fare, with a minimum surcharge of Rs 100. A constructor without the quota means GENERAL. Print one ticket of each.
new TrainTicket("Neha", 350)
new TrainTicket("Arif", 350, "TATKAL")Neha | 350 km | GENERAL | Rs 210.00 Arif | 350 km | TATKAL | Rs 310.00
Show solution and explanation
class TrainTicket {
String passenger;
int km;
String quota;
double fare;
TrainTicket(String passenger, int km) {
this(passenger, km, "GENERAL");
}
TrainTicket(String passenger, int km, String quota) {
this.passenger = passenger;
this.km = km;
this.quota = quota;
fare = km * 0.60;
if (quota.equals("TATKAL")) {
fare += Math.max(fare * 0.30, 100);
}
}
void print() {
System.out.printf("%s | %d km | %s | Rs %.2f%n", passenger, km, quota, fare);
}
}
public class Main {
public static void main(String[] args) {
new TrainTicket("Neha", 350).print();
new TrainTicket("Arif", 350, "TATKAL").print();
}
}How it works:
- Most bookings are general, so the 2-parameter constructor supplies the quota and chains to the full one.
- Base fare 350 × 0.60 = 210. Tatkal: 30% of 210 is 63, less than the minimum, so Rs 100 is added: 310.
Math.maxapplies the minimum in one line. The fare rules here are invented for practice, not real railway rules.
Real-world scenario questions (asked in company interviews)
Constructors decide whether a broken object can ever exist. Decide your answer first, then open the model answer.
“I added one constructor and 40 files broke”
You add Invoice(String customerId) to the Invoice class so new code must pass a customer. Suddenly the build fails in 40 places that use new Invoice(), and a library that reads invoices from JSON starts failing too. Explain and fix.
The interviewer is checking: do you know when the default constructor disappears?
Show a strong answer
- Before, the class had no constructor, so javac supplied a default no-arg one. Writing any constructor removes it, so every
new Invoice()stops compiling. - Many JSON and database libraries create objects through a no-arg constructor and then fill the fields, so they break too.
- Fix: if no-arg creation is still valid, add
Invoice()back explicitly (it can chain to the other with a default). If it isn't valid, update the 40 callers so each passes a customer.
Avoid: deleting the new constructor to make the build green. Decide what a valid Invoice needs.
Accounts saved without a PAN
Audit finds 3,000 account records with an empty PAN. The Account class has only the default constructor, and each screen sets fields one by one. How would you redesign the class so this can't happen again?
The interviewer is checking: can you use constructors to enforce valid objects?
Show a strong answer
- Give
Accounta constructor that takes the mandatory details (holder, PAN, branch) and remove the no-arg one, so an account can't be created without them. - Validate inside the constructor: if the PAN is null or not in the 10-character format, throw an exception (Module 9) instead of creating a bad object.
- Later, make the fields private (Lesson 7.1) so nobody can blank them out after creation.
- Clean up the 3,000 existing records separately; the code change only protects new ones.
Avoid: adding a null check on every screen. Put the rule in one place: the constructor.
Four constructors, one forgotten status
An Order class has four overloaded constructors, each copy-pasting the same ten lines of setup. Someone added a status = "PLACED" line to three of them, and orders created through the fourth show a blank status in the tracking page. How would you restructure it?
The interviewer is checking: do you know how to use constructor chaining to remove duplication?
Show a strong answer
- Keep one "full" constructor that takes every value and contains all the setup.
- Make the other three one-liners that call it with
this(...), passing defaults for the missing values. - Values that never depend on the caller (like the starting status) can be a field initializer, which runs for every constructor path.
- Add a unit test that creates an order through each constructor and checks the status.
Avoid: fixing just the fourth constructor. The duplication will cause the next bug.
Creating a Shipment takes two seconds
The Shipment constructor calls a courier partner's web service to fetch the delivery estimate. Bulk uploads of 10,000 shipments take hours, and unit tests fail whenever the partner's test server is down. What's wrong with this design?
The interviewer is checking: do you know what belongs in a constructor and what doesn't?
Show a strong answer
- A constructor should only put the object into a valid starting state: copy and check values. Slow network calls make every
newslow and impossible to test without the network. - Create the Shipment with its own data only. Fetch estimates in a separate service method, and for bulk uploads call the partner in batches or in the background.
- Pass collaborators (like the courier client) in from outside so tests can supply a fake one.
Avoid: "Add a cache inside the constructor." Keep constructors simple and fast.