Constructors are fundamental concepts in OOP. We use it for object creation, initialization and maintaining the integrity and consistency of objects throughout their lifecycle. Other than this, it is a core part of several object-orientated principles and design patterns.
In object-oriented programming, constructors are methods (with the same name as the class) that is used to create and initialize an object of that class. When we create a new object, the constructor is called, and necessary initialization is performed.
In the following example, we have defined the constructor for the class MobilePhone.
public class MobilePhone {
private int imeiNumber;
private String brandName;
// Constructor
public MobilePhone (int number, String name) {
// Initialization code
imeiNumber = number;
brandName = brand;
}
public String getBrandName() {
return brandName;
}
public int getImeiNumber() {
return imeiNumber;
}
}
Here's an example of how we create the object of the MobilePhone class using its constructor.
MobilePhone myPhone = new MobilePhone(12345678, "Samsung");
The new keyword creates a new instance of the MobilePhone class and allocates the required memory. Then the constructor will call itself by passing the arguments in the parameter list.
In Java, a constructor has the following elements:
In Java, there are four types of constructors:
A default constructor is automatically provided by the Java compiler if a class does not have any constructors defined. It is a no-argument constructor because it does not take any arguments.
In Java, a no-argument constructor does not take any parameters. This means: When an object is created without passing any arguments to the constructor, the no-argument constructor is called. This can be useful when we want to perform initialization with some default values or perform certain setup operations every time an object is created.
Example
public class MobilePhone {
......
public MobilePhone() {
//perform some operations
}
.....
}
In Java, a parameterized constructor takes one or more parameters. This will allow developers to initialize an object with specific values.
Example
public class MobilePhone {
private int imeiNumber;
private String brandName;
//parameterized constructor 1
public MobilePhone (int number, String name) {
imeiNumber = number;
brandName = brand;
}
//parameterized constructor 2
public MobilePhone (int number) {
imeiNumber = number;
}
//.....
}
In Java, a copy constructor creates a new object by copying the values of another object of the same class. It takes an object of the same class as a parameter to initialize the properties of the new object.
Example
class MobilePhone {
private int imeiNumber;
private String brandName;
//copy constructor
MobilePhone (MobilePhone object){
this.imeiNumber = object.imeiNumber;
this.brandName = object.brandName;
}
......
}
It is possible to have both private and public constructors in a class. A private constructor restricts the visibility of the constructor to only within the class itself.
The good use case of private constructors is singleton design patterns, where we control the creation of a single instance of a class using the private constructor.
In Java, constructor overloading is the ability of a class to have multiple constructors with different parameter lists. This means: A class can have several constructors with the same name but different types and/or numbers of parameters.
Example 1
class MobilePhone {
private int imeiNumber;
private String brandName;
MobilePhone(){
.....
}
MobilePhone(int number, String name){
.....
}
MobilePhone(MobilePhone mobile){
.....
}
MobilePhone(int number) {
.....
}
...
}
In Java, constructor chaining is the practice of calling one constructor from another constructor within the same class or from a subclass constructor to a superclass constructor.
In the same class, we can achieve constructor chaining by using the "this" keyword followed by the parameters required by the constructor being called.
For example, suppose you have a class called "Person" with constructors that take different sets of arguments. Here second constructor "Person(String name, int age, String address)" is calling the first constructor "Person(String name, int age)" using this(name, age).
class Person {
private String name;
private int age;
private String address;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name, int age, String address) {
this(name, age); // calls the constructor above
this.address = address;
}
//...
}
Person p1 = new Person("Shubham", 30);
Person p2 = new Person("Shubham", 30, "New Delhi");
In the inheritance hierarchy, we use the "super" keyword to invoke a constructor of the superclass from the subclass. Here, super() must also be the first line in the constructor body.
Explore more about constructor chaining in the inheritance blog.
class Vehicle {
private String brand;
public Vehicle(String brand) {
this.brand = brand;
}
//....
}
class Car extends Vehicle {
private int numberOfSeats;
public Car(String brand, int numberOfSeats) {
super(brand); // Invoking superclass constructor using "super"
this.numberOfSeats = numberOfSeats;
}
//...
}
public class Main {
public static void main(String[] args) {
Car car = new Car("Toyota", 5);
car.displayDetails();
}
}
In Java, we use constructors to implement dependency injection by passing in the required dependencies as parameters to the constructor. For example, consider a class Car that has a dependency on an Engine object. Instead of creating an Engine object inside the Car class, the Engine object can be passed in as a parameter to the Car class's constructor. This way, the Car class does not have to worry about creating its own Engine object.
class Engine { ... }
class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
...
}
The singleton pattern ensures that a class has only one instance and provides a global access point to that instance. So to implement the singleton pattern, we use a private constructor to ensure a single instance of the class. Here is an example:
public class Singleton {
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
Inside the getInstance() method, we create an instance of the class using a private constructor if it doesn't already exist. So, one can only call the getInstance() method to access or instantiate the single instance of the class.
We can use a private constructor to enforce security by implementing a user authentication process before creating an object. For example, in the following code, SecureObject class is only instantiated when the user credentials are valid. This will provide an additional layer of security to the application.
public class SecureObject {
private SecureObject() {
// private constructor
}
public static SecureObject createInstance(String username, String password) {
// Verify user credentials
boolean isAuthenticated = authenticate(username, password);
if (isAuthenticated) {
return new SecureObject();
} else {
throw new SecurityException("Invalid credentials");
}
}
private static boolean authenticate(String username, String password) {
// check credentials against a database or external service
// return true if valid, false otherwise
}
}
Driver code
SecureObject obj = SecureObject.createInstance("username", "password");
The idea of the constructor is used everywhere in OOPS because object creation and initialization are core parts of object-oriented design. For example, all creational design patterns use the idea to simplify the idea of object instantiation. So, learning various use cases, tradeoffs and properties related to constructors is important.
Enjoy learning, Enjoy oops!