In this article, we’ll go through what a Java constructor is and it works.
1. What Is a Java Constructor?
A constructor is a block of code, which might seem similar to methods at first, but has some fundamental differences compared to them. You can utilize a constructor for the creation of a new object.
The formula for writing a constructor is the following:
access_modifier Class_Name( Any_Object_or_primitive param_1, Any_Object_or_primitive param_2, ..., Any_Object_or_primitive param_n ){ // Usually here there are simple assignments to members of the class this.param_1 = param1; this.param_2 = param2; .... this.param_n = param_n; }
The assignment this.param_1 = param1
can translate to this:
The reference or primitive value of this object(this.param_1
, “this
” means the object) will point to the reference or primitive value param_1
.
The formula for calling a constructor is the following:
Class_Name class_name = new Class_Name(param_1, param_2, ..., param_n);
The line of code above will create a new instance of the class Class_Name
which reference will be held by class_name
variable.
A constructor in Java has the following characteristics:
- It does not have a return type like a method would have.
- If you do not declare any constructor, the default constructor (a constructor without parameters and which is empty) will be declared automatically by the compiler.
- If you declare at least one constructor, the default constructor will not be created automatically. Should you like to use it, you should declare it explicitly.
- Constructor overloading is possible and should be followed according to OOP concept of polymorphism.
- Constructor overriding is not possible in Java.
In the next section, we’ll go see how constructors work in action.
2. Default Constructor in Java
As already stated before, there is no need to create the default constructor if you haven’t created any parameterized constructors. In the example below, we do not declare any constructor for Person
, so we get the default by Java:
public class Person { private String name; private String surname; public static void main(String[] args) { // This is completely legal, the default // constructor will be created automatically Person person = new Person(); person.name = "John"; person.surname = "Smith"; } }
On the contrary, let’s say we create another class named PersonWithConstructors
and declare a constructor accepting name
and surname
as parameters:
public class PersonWithConstructors { private String name; private String surname; public PersonWithConstructors(String name, String surname) { this.name = name; this.surname = surname; } public static void main(String[] args) { PersonWithConstructors person = new PersonWithConstructors(); } }
We won’t go through the parameterized constructor in this section, it was just added to demonstrate the fact that by declaring any constructor, we are losing the default one by Java.
So, if we run the above main method, the following error will be produced:
java: constructor PersonWithConstructors in class PersonWithConstructors cannot be applied to given types; required: java.lang.String,java.lang.String found: no arguments reason: actual and formal argument lists differ in length
The solution to this error is to add the default constructor to our class:
public PersonWithConstructors() {}
3. Parameterized Java Constructor
Let’s say we have a class Car
and its members are:
String
brandString
modelint
horses (as in horsepower)int
engineVolume
Usually, when creating an object, we do not have information about all the members of an object, so we are unable to provide all the details initially. In this case, the constructor will not have all of the members.
Here we created one constructor accepting the brand and the model of the car.
Let’s say that the brand and the model are known, but to retrieve the horses (horsepower) and the engine volume, you’d need to call an external API with the brand and model as parameters.
public Car(String brand, String model) { this.brand = brand; this.model = model; } public static void main(String[] args) { Car car = new Car("Opel", "Astra"); // Brand and model are required car.setHorses(105); car.setEngineVolume(1600); } //getters and setters
The solution to this is to set the values after the creation of the object through setters.
Another case in which we might want to avoid an all-args constructor (a constructor that provides arguments for every member of a class) is when we’d like to run some checks before assigning a new value to our object. We could, of course, do this inside the all-args constructor but it wouldn’t be optimal.
public void setEngineVolume(int engineVolume) { if(engineVolume > 0 && engineVolume < 10000) this.engineVolume = engineVolume; else throw new IllegalArgumentException("An engine volume cannot be 0cc, negative cc or more than 10000cc"); } public void setHorses(int horses) { if(horses > 0 && horses < 1500) this.horses = horses; else throw new IllegalArgumentException("A car cannot have 0, negative horses or more than 1500 horses"); }
4. Java Constructor Overloading
Constructor overloading, similarly to method overloading, is when you create multiple constructors with different numbers and/or types of arguments. If you want to learn more about overloading, you can read about Polymorphism in Java.
For example, let’s take the Car
class from the previous section.
We can create multiple constructors as shown below:
// Empty public Car() {} public Car(String brand, String model) { this.brand = brand; this.model = model; } public Car(String brand) { this.brand = brand; } public Car(int horses) { this.horses = horses; } public Car(int horses, int engineVolume) { this.horses = horses; this.engineVolume = engineVolume; } public Car(String brand, String model, int horses) { this.brand = brand; this.model = model; this.horses = horses; } public Car(String brand, String model, int horses, int engineVolume) { this.brand = brand; this.model = model; this.horses = horses; this.engineVolume = engineVolume; }
Note that here we have some limitations; we cannot create a constructor that accepts only the brand and another that accepts only the model since the type is the same. This happens because it would be impossible for the compiler to choose between the 2 constructors. The error shown will be java: constructor Car(java.lang.String) is already defined in class parameterized_constructor.Car
.
5. Java Copy Constructor
Java does not have a copy constructor (a constructor that takes an object of the same class as a parameter and copies each members’ values to itself); nevertheless, you can create your own as shown below:
public Car(Car car){ this.brand = car.brand; this.model = car.model; this.horses = car.horses; this.engineVolume = car.engineVolume; }
6. Java Constructor Example – Chaining
In this example, we have created 4 classes according to the following diagram:
6.1 Vehicle Constructor
public Vehicle(String canBeDrivenOn) { System.out.println("Inside Vehicle constructor"); this.canBeDrivenOn = canBeDrivenOn; }
6.2 Car Constructors
public Car(String canBeDrivenOn, String brand, String model, int horses) { super(canBeDrivenOn); System.out.println("Inside Car constructor - 4 params"); this.brand = brand; this.model = model; this.horses = horses; } public Car(String canBeDrivenOn, String brand, String model, int horses, int engineVolume) { this(canBeDrivenOn, brand, model, horses); System.out.println("Inside Car constructor - 5 params"); this.engineVolume = engineVolume; }
Let’s explain what happens here:
- We use the
super
keyword, to call the constructor of the parent class (Vehicle
). Note that we cannot insert any line of code before calling the parent constructor. - We proceed with the usual assignments.
- Then, we use the
this
keyword to call another constructor from the same class. In this case, we call the constructor declared in the beginning. If we didn’t use this keyword, we’d have to rewrite the same boilerplate assignments (in other words, rewrite the assignments again). - Finally, we proceed with the usual assignment.
6.3 WorkingCar and FaultyCar Constructors
public WorkingCar(String canBeDrivenOn, String brand, String model, int horses, int engineVolume, int daysLeftUntilService) { super(canBeDrivenOn, brand, model, horses, engineVolume); System.out.println("Inside Working Car constructor"); this.daysLeftUntilService = daysLeftUntilService; }
Here we use as stated before, the super
keyword to call the constructor of Car
class and then we proceed with the usual assignments.
The same applies to the FaultyCar
constructor:
public FaultyCar(String canBeDrivenOn, String brand, String model, int horses, int engineVolume, int moneyNeededToFix) { super(canBeDrivenOn, brand, model, horses, engineVolume); System.out.println("Inside Faulty Car constructor"); this.moneyNeededToFix = moneyNeededToFix; }
6.4 Creating a working car object
Let’s try to create a working car:
WorkingCar workingCar = new WorkingCar("ROAD", "Opel", "Astra", 105, 1600, 200);
This will eventually call all the constructors displayed above.
The output is the following:
Inside Vehicle constructor Inside Car constructor - 4 params Inside Car constructor - 5 params Inside Working Car constructor
7. Conclusion
By now, you should be able to know when to use a constructor and how to use it in the most efficient way. You can find the source code on our GitHub page.