Constructors and constructor chaining

Creating an object and then setting five fields by hand is slow and easy to get wrong: forget one and you get null on a receipt. A constructor sets up a new object the moment it is created. You'll see when Java gives you a default constructor and when it takes it away, how overloaded constructors share work with this(...), the exact order in which initialization happens, and then practise with 10 problems.

Module 6 · OOP Basics⏱ 24 minBeginner10 practice problems
1 The story

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.

new Student() + forgotten lines FEST ID CARD name: nullroll: 0course: null easy to forget a field new Student("Riya", 101) Constructor needs name + roll, fills course default FEST ID CARD name: Riyaroll: 101course: PG-DAC complete from the first moment myphatshala.in
Riya

Sir, I keep forgetting one of the lines after new Student(). Can Java force me to give the name?

Arjun Sir

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.

Riya

And the course? Most students are PG-DAC.

Arjun Sir

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.

2 Definition

What is a constructor?

Formal definition (write this in exams)

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.

In simple words

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

Default constructorThe no-parameter constructor javac adds when you write none.
No-arg constructorA constructor you write that takes no parameters.
Parameterized constructorTakes values: Student(String name, int roll).
OverloadingSeveral constructors with different parameter lists (Lesson 5.4).
this(...)Calls another constructor of the same class.
Initializer blockA bare { … } in the class that runs for every new object.

Constructor facts at a glance

NameExactly the class name: Student(…)
Return typeNone. void Student() compiles, but it is an ordinary method, not a constructor.
Called bynew, automatically, once per object. You can't call it like a method.
Default constructorAdded 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.

3 Types and ways to use it

Three kinds of constructor, and how they share work

Default

You wrote none, so javac adds Student() { }. Fields keep their default values.

Added by javac

No-arg

You write Student() { … } to set your own starting values.

Student()

Parameterized

Student(String name, int roll): the caller must supply the values.

Student(name, roll)

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();
    }
}
Medium | Classic | Rs 299 Large | Classic | Rs 499 Small | Thin | Rs 299

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

ConstructorMethod
NameSame as the classAny name (camelCase verb)
Return typeNoneRequired (void if nothing)
CalledAutomatically by new, once per objectExplicitly, any number of times
Default provided?Yes, if you write noneNo
Inherited?NoYes (Module 7)
PurposeSet up a new objectBehaviour 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);
    }
}
null
4 Step by step

What happens inside new, in order

  1. Memory and default values

    Account a = new Account("Riya");
    The JVM reserves heap memory for one Account and sets every field to its default: null, 0.0, false. This happens before any of your code runs.
  2. The matching constructor is chosen

    Account(String holder) { … }           // chosen: one String
    Account(String holder, double balance) { … }
    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 named <init>; that's the name you see in stack traces.
  3. this(...) runs first

    Account(String holder) {
        this(holder, 0.0);   // must be the first statement
        System.out.println("1-arg body");
    }
    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.
  4. Field initializers and instance blocks

    String type = "SAVINGS";
    { System.out.println("instance block"); }
    javac copies these, in the order they are written, into the start of every constructor that doesn't begin with this(...) (right after the hidden call to the parent constructor). So they run once per object, before the constructor body. javap -c shows the copied putfield instructions.
  5. Bodies finish, reference handed back

    this.holder = holder;
    this.balance = balance;
    The 2-parameter body runs, its frame pops, then the 1-parameter body runs and pops. Finally new gives the reference of the finished object to a. Nobody ever sees a half-built Account.
5 See it happen

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.

Inside new Account("Riya") 6 steps
What you see Account a = new Account("Riya"); Account(String h) { this(h, 0.0); … } Account(String h, double b) { String type = "SAVINGS"; // initializer this.holder = h; this.balance = b; println(a.holder + " | " + a.type …); // nothing printed yet 2-arg body, type=SAVINGS 1-arg body Riya | SAVINGS | 0.0 new: an empty Account appears first. One String argument: the 1-arg constructor. this(...) jumps to the 2-arg constructor. The field initializer runs before the body. The 2-arg body prints, then fills the fields. Back in the 1-arg body, then a is ready. myphatshala.in
Behind the screen Stack Heap Account(String, double) this→#1 b=0.0 Account(String) this→#1 h→"Riya" main a: not set yet a → #1 #1 Account holder = null holder = "Riya" balance = 0.0 type = null type = "SAVINGS" Heap memory first, every field at its default. A constructor frame: this → the new object. this(...) pushes a 2nd frame for the same #1. javac copied the initializer into this constructor. The body writes the parameters into #1. Both frames popped; new returns #1 to a. myphatshala.in
6 Real-world use

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.

new Account("Riya", "ABCDE1234F") 6 steps
Animated example Account a = new Account("Riya", "ABCDE1234F"); // JVM: memory with default values Account(String n, String p) { this(n, p, 1000); } Account(String n, String p, int min) { name = n; … } // constructor done → reference stored in a new Account(); // compile error #1 Account name = null name = "Riya" pan = null pan = "ABCDE1234F" balance = 0 balance = 1000 STACK Account(n, p) Account(n, p, min) a → #1 constructor Account() is undefined: KYC can't be skipped myphatshala.in

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.

Onboarding form→new Customer(name, pan, dob)→Valid record

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.

new Order(cart)→this(cart, "UPI", "PLACED")→Consistent orders
new Scanner(System.in)new StringBuilder(64)new int[5]

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.

Arguments→matching constructor→Ready object

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.

App starts→new OrderService(repo)→Wired-up app

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.

7 Run the code

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();
    }
}
Registered: Riya Registered: Kabir 101 | Riya | PG-DAC 102 | Kabir | PG-DBDA
  • 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.name means the field (Lesson 6.3).
  • Registered: …Printed by the 3-parameter constructor for both students, because both paths end there.
8 Line by line

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.

Example 1Banking · account opening

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.

Input
new Account("Riya", 2000)
new Account("Kabir")
Expected output
Riya: Rs 2000.0
Kabir: Rs 0.0

Approach

  1. Write the full constructor Account(String holder, double balance) that stores both values.
  2. Write Account(String holder) that calls this(holder, 0): zero balance is the default.
  3. 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.

Example 2Food delivery · checkout

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.

Input
new FoodOrder(149)
new FoodOrder(260)
Expected output
Order Rs 149 -> pay Rs 179
Order Rs 260 -> pay Rs 260

Approach

  1. The constructor receives the item total and stores it.
  2. It decides the delivery fee with one if, using a field initializer for the minimum order value.
  3. 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.

Example 3HR · employee onboarding

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.

Input
new Employee("Sneha")
new Employee("Rahul", "Finance", 52000)
Expected output
Sneha | Trainee | Rs 25000
Rahul | Finance | Rs 52000

Approach

  1. The 3-parameter constructor is the only one that assigns fields.
  2. Employee(name, dept) calls this(name, dept, 25000).
  3. Employee(name) calls this(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.

9 Quiz and revision

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.

P1Easy

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.

Input
radius = 7, radius = 2.5
Expected output
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());
    }
}
Area 1: 153.94 Area 2: 19.63

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.

P2Easy

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.

Input
new Book()
new Book("Java Basics", 450)
Expected output
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();
    }
}
Untitled - Rs 0 Java Basics - Rs 450

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.

P3Easy

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.

Input
p1 = (3, 4); p2 = copy of p1; p2.x = 10
Expected output
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));
    }
}
p1 = (3, 4) p2 = (10, 4) Same object? false

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.

P4Medium

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.

Input
new Rectangle(6, 4)
new Rectangle(5)
Expected output
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());
    }
}
Rectangle: 6 x 4 = 24 Square: 5 x 5 = 25

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.

P5Medium

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.

Input
new Duration(2, 135)
new Duration(0, 45)
Expected output
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();
    }
}
4 h 15 min 0 h 45 min

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".

P6Medium

Watch the initialization order

Predict the output, then run. The class has a field initializer, an instance block and two chained constructors.

Input
new Lamp()
Expected output
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);
    }
}
block: OFF Lamp(int): READY, 60W Lamp(): ON final: ON

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.

P7Medium

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.

Input
new Product("Notebook", 60)
new Product("Pen", -10)
Expected output
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);
    }
}
Warning: negative price for Pen, using 0 Notebook: Rs 60 Pen: Rs 0

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.

P8Business problem

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.

Input
new Booking("Sharma", 3)
new Booking("Iyer", 2, 4200)
Expected output
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();
    }
}
Sharma: 3 nights x Rs 2500 + 12% GST = Rs 8400.0 Iyer: 2 nights x Rs 4200 + 12% GST = Rs 9408.0

How it works:

  1. The short constructor supplies the default rate and chains to the full one, so the GST calculation exists only once.
  2. Sharma: 3 × 2500 = 7500, plus 900 GST = 8400.0. Iyer: 2 × 4200 = 8400, plus 1008 GST = 9408.0.
  3. GST rates on hotel rooms depend on the tariff and change over time; 12% is used here only as an example.
P9Business problem

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.

Input
principal = 500000, rate = 9% a year, months = 60
Expected output
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);
    }
}
EMI: Rs 10379.18 Total paid: Rs 622750.66 Total interest: Rs 122750.66

How it works:

  1. The EMI depends only on values given at creation, so the constructor works it out once and stores it.
  2. The yearly rate 9% becomes a monthly rate of 0.0075; Math.pow computes (1 + r)60.
  3. This is the standard reducing-balance EMI formula used by banks; actual EMIs may differ slightly because of rounding rules and fees.
P10Business problem

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.

Input
new TrainTicket("Neha", 350)
new TrainTicket("Arif", 350, "TATKAL")
Expected output
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();
    }
}
Neha | 350 km | GENERAL | Rs 210.00 Arif | 350 km | TATKAL | Rs 310.00

How it works:

  1. Most bookings are general, so the 2-parameter constructor supplies the quota and chains to the full one.
  2. Base fare 350 × 0.60 = 210. Tatkal: 30% of 210 is 63, less than the minimum, so Rs 100 is added: 310.
  3. Math.max applies 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.

SaaS · refactoringFresher

“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.

Banking · data qualityFresher

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 Account a 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.

E-commerce · code review1–3 years

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.

Logistics · performance1–3 years

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 new slow 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.