Page Content

Tutorials

What are Constructors in Java? & Why Constructors Are Used

Constructors in Java

In Java, a constructor is a unique kind of method that is essential to an object’s existence. Setting an object’s initialisation right after creation is its main purpose. A constructor, in contrast to conventional methods, has no return type and is named after its class. It is automatically called anytime the new keyword is used to create an object of its class.

Why Constructors Are Used

A constructor’s primary function is to guarantee that an object is legitimate and useable when it is first formed. Constructors give the programmer a quick and easy way to complete all setup tasks without having to manually configure each instance variable after constructing an object. Unpredictable program behaviour could result from fields being left uninitialised, which is something that our automatic initialisation helps avoid. As the “building instructions” for an object, constructors establish its basic properties from the very beginning.

Types of Constructors in Java
Types of Constructors in Java

Default Constructor

Java classes all have constructors. The Java compiler automatically offers a default constructor for your class if you don’t provide any explicitly. This default constructor does not carry out any special initialisation and is parameter-less, often known as a “no-argument constructor.” Instead, it automatically initialises all instance variables to their default values: 0 or 0.0 for numeric types, '\u0000' for char, false for boolean, and null for all reference types.

Consider a basic class without a defined constructor:

class MySimpleClass {
    int value;       // Will default to 0
    String name;     // Will default to null
    boolean active;  // Will default to false
}
class DefaultConstructorDemo {
    public static void main(String args[]) {
        MySimpleClass obj = new MySimpleClass(); // Default constructor is implicitly called
        System.out.println("obj.value: " + obj.value);
        System.out.println("obj.name: " + obj.name);
        System.out.println("obj.active: " + obj.active);
    }
}

Code Output:

obj.value: 0
obj.name: null
obj.active: false

It is vital to remember that the compiler will no longer automatically offer the default constructor after you declare any constructor of your own. You must specify a no-argument constructor explicitly if you still require one after declaring others.

Parameterised Constructors

Although the default constructor is adequate for straightforward situations, the majority of real-world classes require initialisation with particular values. This is done by using parameterised constructors, which take one or more arguments to set the initial state of the object.

Here’s an example using a MyClass with a parameterised constructor:

class MyClass {
    int x;
    // Parameterised constructor
    MyClass(int i) {
        x = i;
    }
}
class ConsDemo {
    public static void main(String args[]) {
        MyClass t1 = new MyClass(10); // Calls constructor with 10
        MyClass t2 = new MyClass(88); // Calls constructor with 88
        System.out.println("t1.x: " + t1.x);
        System.out.println("t2.x: " + t2.x);
    }
}

Code Output:

t1.x: 10
t2.x: 88

This illustrates how different values can be supplied to each object upon creation, enabling a variety of object instances.

Constructor Overloading

Like methods, constructors are susceptible to overloading. This implies that several constructors with the same name can exist in a class as long as their parameter lists (also known as “signatures”) differ. The number, kind, and arrangement of the arguments of an overloaded constructor determine how the Java compiler distinguishes between them.

Flexibility in object formation is achieved via overloading constructors. You can provide an object a constructor that requires no arguments for default settings, one that requires numerous arguments for complete customisation, or one that requires minimal setup.

Let’s illustrate constructor overloading using a MyClass example with several constructors:

class MyClass {
    int x;
    // 1. No-argument constructor
    MyClass() {
        System.out.println("Inside MyClass().");
        x = 0;
    }
    // 2. Constructor with one int parameter
    MyClass(int i) {
        System.out.println("Inside MyClass(int).");
        x = i;
    }
    // 3. Constructor with one double parameter
    MyClass(double d) {
        System.out.println("Inside MyClass(double).");
        x = (int) d; // Casts double to int
    }
    // 4. Constructor with two int parameters
    MyClass(int i, int j) {
        System.out.println("Inside MyClass(int, int).");
        x = i * j;
    }
}
class OverloadConsDemo {
    public static void main(String args[]) {
        // Create objects using various constructors
        MyClass t1 = new MyClass();            // Calls MyClass()
        MyClass t2 = new MyClass(88);         // Calls MyClass(int)
        MyClass t3 = new MyClass(17.23);      // Calls MyClass(double)
        MyClass t4 = new MyClass(2, 4);       // Calls MyClass(int, int)
        System.out.println("t1.x: " + t1.x);
        System.out.println("t2.x: " + t2.x);
        System.out.println("t3.x: " + t3.x);
        System.out.println("t4.x: " + t4.x);
    }
}

Code Output:

Inside MyClass().
Inside MyClass(int).
Inside MyClass(double).
Inside MyClass(int, int).
t1.x: 0
t2.x: 88
t3.x: 17
t4.x: 8

With the help of the arguments supplied during object instantiation, the compiler selects the right constructor, giving users a variety of alternatives for creating objects.

The keyword

The this keyword in Java is a reference to the current object specifically, the object upon which a method or constructor was invoked. It can be used anywhere a reference to an object of the current class’s type is permitted.

One significant use of this is to disambiguate instance variables from local variables or parameters when they share the same name. This enhances code clarity and prevents confusion.

Consider a constructor for an Employee class:

public class Employee {
    String name;
    int age;
    public Employee(String name, int age) {
        // 'this.name' refers to the instance variable, 'name' refers to the parameter
        this.name = name;
        this.age = age;
        System.out.println("Employee created: " + this.name + ", " + this.age + " years old.");
    }
    public static void main(String[] args) {
        Employee emp1 = new Employee("Alice", 30);
    }
}

Code Output:

Employee created: Alice, 30 years old.

To differentiate it from the name parameter supplied to the constructor, this.name here explicitly corresponds to the name instance variable.

Constructor chaining is another strong use of the this keyword, which is used to invoke another constructor within the same class using the syntax this(arg-list).

It is imperative that this(arg-list) always be the first line executed inside the constructor; hence, a constructor cannot call both this() and super() (to call a parent class constructor) since both of these methods need to be the first statement.

A useful technique for minimising code duplication is constructor chaining, which enables a constructor to reuse initialisation logic written in a more general constructor of the same type.

Let’s modify our MyClass example to demonstrate this() for chaining constructors:

class MyClass {
    int a;
    int b;
    // 1. Full constructor: initializes 'a' and 'b' individually
    MyClass(int i, int j) {
        a = i;
        b = j;
        System.out.println("Two-argument constructor: a=" + a + ", b=" + b);
    }
    // 2. Chained constructor: initializes 'a' and 'b' to the same value by calling the full constructor
    MyClass(int i) {
        this(i, i); // Calls MyClass(i, i)
        System.out.println("One-argument constructor, called via chaining.");
    }
    // 3. Chained default constructor: gives 'a' and 'b' default values of 0 by calling the one-argument constructor
    MyClass() {
        this(0); // Calls MyClass(0)
        System.out.println("No-argument constructor, called via chaining.");
    }
}
class ThisConstructorDemo {
    public static void main(String args[]) {
        System.out.println("Creating mc1 (two args):");
        MyClass mc1 = new MyClass(8, 12);
        System.out.println("mc1.a: " + mc1.a + ", mc1.b: " + mc1.b);
        System.out.println();
        System.out.println("Creating mc2 (one arg):");
        MyClass mc2 = new MyClass(5);
        System.out.println("mc2.a: " + mc2.a + ", mc2.b: " + mc2.b);
        System.out.println();
        System.out.println("Creating mc3 (no args):");
        MyClass mc3 = new MyClass();
        System.out.println("mc3.a: " + mc3.a + ", mc3.b: " + mc3.b);
    }
}

Code Output:

Creating mc1 (two args):
Two-argument constructor: a=8, b=12
mc1.a: 8, mc1.b: 12
Creating mc2 (one arg):
Two-argument constructor: a=5, b=5
One-argument constructor, called via chaining.
mc2.a: 5, mc2.b: 5
Creating mc3 (no args):
Two-argument constructor: a=0, b=0
One-argument constructor, called via chaining.
No-argument constructor, called via chaining.
mc3.a: 0, mc3.b: 0

The constructor call chain is evident in this output. Although this() eliminates redundant code, it should be noted that constructors that use it may run a little more slowly because of the overhead of the call and return mechanism. Nevertheless, for complex initialisation logic, the advantages of cleaner, more manageable code frequently exceed this small performance issue.

Index