Board Formulas
β˜• JavaπŸ“… Day 12

Constructors in Java

Go deeper into constructors: your own no-arg constructor, overloading, chaining with this(), copy constructors and validation.

🎯

Learning objectives

  • β†’Write no-arg and parameterised constructors and know when Java adds a default one
  • β†’Overload constructors to give callers several ways to create an object
  • β†’Chain constructors with this() and predict the order in which they run
  • β†’Write a copy constructor and validate data inside a constructor

πŸ’‘ Key points

  • A constructor runs exactly once for each object, right after new reserves memory for it. You cannot call it again on the same object.
  • The default constructor is added by the compiler only when a class has no constructor at all. A no-arg constructor is one you write yourself.
  • Parameterised constructors take values, so every object starts with its own data.
  • Overloaded constructors differ in the number, type or order of parameters, the same rule as method overloading.
  • this(...) calls another constructor of the same class. It must be the first statement, so a constructor can make only one such call.
  • Constructor chaining keeps the real set-up code in one constructor. The others pass their values along to it.
  • A constructor has no return type, not even void. Adding void turns it into an ordinary method.

πŸ’» Code examples(5)

#1Your own no-arg constructor
java
class Account {
    String holder;
    double balance;

    Account() {
        holder = "Unknown";
        balance = 500.0;       // minimum balance
        System.out.println("No-arg constructor ran");
    }

    Account(String holder, double balance) {
        this.holder = holder;
        this.balance = balance;
        System.out.println("Parameterised ran");
    }
}

public class Bank {
    public static void main(String[] args) {
        Account a1 = new Account();
        Account a2 = new Account("Priya", 2500.0);
        System.out.println(a1.holder + " Rs." + a1.balance);
        System.out.println(a2.holder + " Rs." + a2.balance);
    }
}
Output
No-arg constructor ran
Parameterised ran
Unknown Rs.500.0
Priya Rs.2500.0
Writing Account() yourself lets you pick better starting values than 0.0 and null. Because both constructors exist, new Account() and new Account("Priya", 2500.0) both compile.
#2Overloading and chaining β€” who runs first?
java
class Student {
    String name;
    int rollNo;
    String school;

    Student() {
        this("New student", 0);
        System.out.println("3. Student()");
    }

    Student(String name, int rollNo) {
        this(name, rollNo, "KV Pune");
        System.out.println("2. Student(name, rollNo)");
    }

    Student(String name, int rollNo, String school) {
        this.name = name;
        this.rollNo = rollNo;
        this.school = school;
        System.out.println("1. Student(all three)");
    }
}

public class ChainDemo {
    public static void main(String[] args) {
        Student s = new Student();
        System.out.println(s.name + ", " + s.rollNo
            + ", " + s.school);
    }
}
Output
1. Student(all three)
2. Student(name, rollNo)
3. Student()
New student, 0, KV Pune
new Student() jumps to the 2-argument constructor, which jumps to the 3-argument one. The innermost body finishes first, then control returns outward. That is why the messages print in the order 1, 2, 3.
#3Copy constructor
java
class Student {
    String name;
    double marks;

    Student(String name, double marks) {
        this.name = name;
        this.marks = marks;
    }

    Student(Student other) {        // copy constructor
        this(other.name, other.marks);
    }
}

public class CopyDemo {
    public static void main(String[] args) {
        Student s1 = new Student("Amit", 78.0);
        Student s2 = new Student(s1);   // a real copy
        Student s3 = s1;                // same object!

        s2.marks = 85.0;
        s3.marks = 90.0;

        System.out.println(s1.marks);
        System.out.println(s2.marks);
        System.out.println(s1 == s3);
    }
}
Output
90.0
85.0
true
new Student(s1) builds a separate object with the same values, so changing s2 leaves s1 alone. s3 = s1 copies only the reference: s3 and s1 are the same object, which is why s1.marks became 90.0.
#4Validating data in a constructor
java
class Result {
    String name;
    int marks;

    Result(String name, int marks) {
        this.name = name;
        if (marks < 0 || marks > 100) {
            System.out.println("Invalid marks for "
                + name + ", using 0");
            marks = 0;
        }
        this.marks = marks;
    }

    String status() {
        return marks >= 33 ? "Pass" : "Fail";
    }
}

public class ResultDemo {
    public static void main(String[] args) {
        Result r1 = new Result("Priya", 88);
        Result r2 = new Result("Ravi", 120);
        System.out.println(r1.name + ": " + r1.status());
        System.out.println(r2.name + ": " + r2.marks);
    }
}
Output
Invalid marks for Ravi, using 0
Priya: Pass
Ravi: 0
Every object passes through the constructor, so checking the values here means no Result can ever hold impossible marks. Day 18 shows a stronger option: throwing an exception.
#5Trap: a constructor with void
java
class Car {
    String model;

    void Car(String model) {   // has a return type!
        this.model = model;
    }
}

public class TrapDemo {
    public static void main(String[] args) {
        Car c = new Car();     // compiler's default
        System.out.println(c.model);
    }
}
Output
null
Because of void, Car(String) is just a method that happens to share the class name. The class has no real constructor, so Java adds the default one and model stays null. Delete void to fix it.

🎯 Practice

Q1. Will this compile? class Pen { Pen(String colour) { } } and then new Pen();+

No. Once Pen has any constructor, Java stops adding the default one, so the error is 'constructor Pen in class Pen cannot be applied to given types'. Add Pen() { } or call new Pen("blue").

Q2. What does new A() print? class A { A() { this(5); System.out.print("X"); } A(int n) { System.out.print("Y"); } }+

YX. this(5) runs A(int) first, which prints Y. Then control returns to A(), which prints X.

Q3. Why must this(...) be the first statement in a constructor?+

So that the other constructor fully sets up the object before any more code uses it. The compiler reports 'call to this must be first statement in constructor' anywhere else, which also means only one this(...) per constructor.

Q4. Write a copy constructor for class Point { int x, y; Point(int x, int y) { this.x = x; this.y = y; } }.+

Point(Point p) { this(p.x, p.y); }

Q5. Give two differences between a constructor and a method.+

A constructor has the same name as the class and no return type, and it runs automatically once per new. A method has a return type (or void), can have any name, and can be called as many times as you like.

πŸ“ Notes

Constructor vs method

  • Name: a constructor's name must be the class name. A method can have any name.
  • Return type: a constructor has none. A method has one, or void.
  • When it runs: a constructor runs automatically, once, when you write new. You call a method yourself, as often as you like.
  • Inherited? Constructors are not inherited. Methods are (Day 14).

The order things happen in new

When you write new Student("Amit", 1), Java:

  1. reserves memory and gives every field its default value (0, null, false)
  2. runs any field initialisers, such as String school = "KV Pune";
  3. runs the constructor body

So a field can safely have a default written next to it, and a constructor can then override that default.

class Fees {
    double amount = 1200.0;        // step 2
    Fees(double amount) {
        this.amount = amount;      // step 3 wins
    }
}

Common mistakes

  • Expecting the default constructor to still exist. Once you write Student(String name), new Student() fails with constructor Student in class Student cannot be applied to given types. Write a no-arg one yourself if you need it.
  • Putting this(...) second. Any statement before it gives call to this must be first statement in constructor.
  • Chaining in a circle. A() calls this(1) and A(int) calls this(). Java refuses this with recursive constructor invocation.
  • Writing name = name;. When the parameter has the same name as the field, this assigns the parameter to itself and the field stays null. Use this.name = name;.
  • Adding void. As example 5 shows, the "constructor" quietly becomes a method.

Next: Day 13, static and final. You will meet members that belong to the class itself, and values that can never change.