JavaRush /Java Blog /Random EN /Constructors in Java

Constructors in Java

Published in the Random EN group
Hello! Today we will look at a very important topic that concerns our objects. Here, without exaggeration, we can say that you will use this knowledge every day in real work! We'll talk about constructors. You may be hearing this term for the first time, but in fact you’ve probably used constructors, but you just didn’t notice it yourself :) We’ll see this later.

What is a constructor in Java and why is it needed?

Let's look at two examples.
public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {

       Car bugatti = new Car();
       bugatti.model = "Bugatti Veyron";
       bugatti.maxSpeed = 407;

   }
}
We created our car and set its model and maximum speed. However, in a real project, the Car object will clearly have more than 2 fields. And, for example, 16 fields!
public class Car {

   String model;//model
   int maxSpeed;//max speed
   int wheels;// disk width
   double engineVolume;//engine capacity
   String color;//color
   int yearOfIssue;//year of issue
   String ownerFirstName;//Owner's name
   String ownerLastName;//owner's last name
   long price;//price
   boolean isNew;//new or not
   int placesInTheSalon;//number of seats in the cabin
   String salonMaterial;// interior material
   boolean insurance;//is it insured
   String manufacturerCountry;//manufacturer country
   int trunkVolume;// trunk volume
   int accelerationTo100km;//acceleration to 100 km/h in seconds


   public static void main(String[] args) {
       Car bugatti = new Car();

       bugatti.color = "blue";
       bugatti.accelerationTo100km = 3;
       bugatti.engineVolume = 6.3;
       bugatti.manufacturerCountry = "Italy";
       bugatti.ownerFirstName = "Amigo";
       bugatti.yearOfIssue = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.placesInTheSalon = 2;
       bugatti.maxSpeed = 407;
       bugatti.model = "Bugatti Veyron";

   }

}
We have created a new Car object . One problem: we have 16 fields, but we only initialized 12 ! Try now using the code to find those that we forgot! Not so easy, right? In such a situation, the programmer can easily make a mistake and skip the initialization of some field. As a result, the program behavior will become erroneous:
public class Car {

   String model;//model
   int maxSpeed;//max speed
   int wheels;// disk width
   double engineVolume;//engine capacity
   String color;//color
   int yearOfIssue;//year of issue
   String ownerFirstName;//Owner's name
   String ownerLastName;//owner's last name
   long price;//price
   boolean isNew;//new or not
   int placesInTheSalon;//number of seats in the cabin
   String salonMaterial;// interior material
   boolean insurance;//is it insured
   String manufacturerCountry;//manufacturer country
   int trunkVolume;// trunk volume
   int accelerationTo100km;//acceleration to 100 km/h in seconds


   public static void main(String[] args) {
       Car bugatti = new Car();

       bugatti.color = "blue";
       bugatti.accelerationTo100km = 3;
       bugatti.engineVolume = 6.3;
       bugatti.manufacturerCountry = "Italy";
       bugatti.ownerFirstName = "Amigo";
       bugatti.yearOfIssue = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.placesInTheSalon = 2;
       bugatti.maxSpeed = 407;
       bugatti.model = "Bugatti Veyron";

       System.out.println("Model Bugatti Veyron. Engine size - " + bugatti.engineVolume + ", trunk - " + bugatti.trunkVolume + ", salon is made of" + bugatti.salonMaterial +
       ", disc width - " + bugatti.wheels + ". Was acquired in 2018 by Mr. " + bugatti.ownerLastName);

   }

}
Console output:
Bugatti Veyron model. Engine displacement - 6.3, trunk - 0, interior made of null, rim width - 0. Was purchased in 2018 by Mr. null
Your buyer, who paid $2 million for a car, will obviously not like being called “Mr. Null”! But seriously, in the end, our program ended up with an incorrectly created object - a car with rim width 0 (that is, no rims at all), a missing trunk, an interior made of unknown material, and even belonging to someone unknown. One can only imagine how such an error could occur while the program is running! We need to somehow avoid such situations. We need our program to have a limitation: when creating a new vehicle object, for example, the model and maximum speed must always be specified for it. Otherwise, do not allow object creation. Constructor functions easily cope with this task. They got their name for a reason. The constructor creates a kind of “skeleton” of the class, to which each new object of the class must correspond. For convenience, let's return to a simpler version of the Car class with two fields. Given our requirements, the constructor for the Car class will look like this:
public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
And creating an object now looks like this:
public static void main(String[] args) {
   Car bugatti = new Car("Bugatti Veyron", 407);
}
Pay attentionhow the constructor is created. It is similar to a regular method, but it does not have a return type. In this case, the class name is indicated in the constructor, also with a capital letter. In our case - Car . In addition, the constructor uses the new-to-you keyword this . "this" in English means "this, this". This word refers to a specific object. Code in the constructor:
public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
can be translated almost literally: " model for this machine (which we are now creating) = the model argument , which is specified in the constructor. maxSpeed ​​for this machine (which we are creating) = the maxSpeed ​​argument , which is specified in the constructor." This is what happened:
public class Car {

   String model;
   int maxSpeed;

   public Car(String model, int maxSpeed) {
       this.model = model;
       this.maxSpeed = maxSpeed;
   }

   public static void main(String[] args) {
       Car bugatti = new Car("Bugatti Veyron", 407);
       System.out.println(bugatti.model);
       System.out.println(bugatti.maxSpeed);
   }

}
Console output:
Bugatti Veyron 407
The constructor successfully assigned the required values. You may have noticed that a constructor is very similar to a regular method! That's how it is: a constructor is a method, only a little specific :) Just like in a method, we passed parameters to our constructor. And just like calling a method, calling a constructor won't work if you don't specify them:
public class Car {

   String model;
   int maxSpeed;

   public Car(String model, int maxSpeed) {
       this.model = model;
       this.maxSpeed = maxSpeed;
   }

   public static void main(String[] args) {
       Car bugatti = new Car(); //error!
   }

}
You see, the designer did what we were trying to achieve. Now you can't create a car without speed or without a model! The similarities between constructors and methods do not end there. Just like methods, constructors can be overloaded. Imagine that you have 2 cats at home. You took one of them as a kitten, and you brought the second one home from the street as an adult and you don’t know exactly how old he is. This means that our program should be able to create cats of two types - with a name and age for the first cat, and only with a name - for the second cat. To do this, we will overload the constructor:
public class Cat {

   String name;
   int age;

   //for the first cat
   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   //for the second cat
   public Cat(String name) {
       this.name = name;
   }

   public static void main(String[] args) {

       Cat barsik = new Cat("Barsik", 5);
       Cat streetCatNamedBob = new Cat("Bob");
   }

}
To the original constructor with the parameters “name” and “age” we added another one, only with a name. We overloaded methods in the same way in previous lessons. Now we can successfully create both versions of cats :) Why are constructors needed?  - 2Do you remember at the beginning of the lecture we said that you had already used constructors, but you just didn’t notice it? This is true. The fact is that every class in Java has a so-called default constructor. It doesn't have any arguments, but it fires every time any object of any class is created.
public class Cat {

   public static void main(String[] args) {

       Cat barsik = new Cat(); //this is where the default constructor worked
   }
}
At first glance this is not noticeable. Well, we created an object and created it, where is the designer’s work? To see this, let’s write an empty constructor for the Cat class with our own hands , and inside it we’ll print some phrase to the console. If it is displayed, then the constructor has worked.
public class Cat {

   public Cat() {
       System.out.println("Created a cat!");
   }

   public static void main(String[] args) {

       Cat barsik = new Cat(); //this is where the default constructor worked
   }
}
Console output:
They created a cat!
Here's the confirmation! The default constructor is always invisibly present in your classes. But you need to know one more feature of it. The default constructor disappears from the class when you create some constructor with arguments. The proof of this, in fact, we have already seen above. Here in this code:
public class Cat {

   String name;
   int age;

   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   public static void main(String[] args) {

       Cat barsik = new Cat(); //error!
   }
}
We couldn't create a cat without a name and age because we defined a constructor for Cat : string + number. The default constructor disappeared from the class immediately after this. Therefore, be sure to remember: if you need several constructors in your class, including an empty one, you need to create it separately. For example, we are creating a program for a veterinary clinic. Our clinic wants to do good deeds and help homeless cats, about whom we do not know their name or age. Then our code should look like this:
public class Cat {

   String name;
   int age;

   //for domestic cats
   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   //for street cats
   public Cat() {
   }

   public static void main(String[] args) {

       Cat barsik = new Cat("Barsik", 5);
       Cat streetCat = new Cat();
   }
}
Now that we have explicitly written a default constructor, we can create cats of both types :) For a constructor (as for any method), the order of the arguments is very important. Let's swap the name and age arguments in our constructor.
public class Cat {

   String name;
   int age;

   public Cat(int age, String name) {
       this.name = name;
       this.age = age;
   }

   public static void main(String[] args) {

       Cat barsik = new Cat("Barsik", 10); //error!
   }
}
Error! The constructor clearly states that when a Cat object is created, it must be passed a number and a string, in that order. That's why our code doesn't work. Be sure to remember this and keep this in mind when creating your own classes:
public Cat(String name, int age) {
   this.name = name;
   this.age = age;
}

public Cat(int age, String name) {
   this.age = age;
   this.name = name;
}
These are two completely different designers! If we express the answer to the question “Why do we need a constructor?” in one sentence, we can say: so that objects are always in the correct state. When you use constructors, all your variables will be correctly initialized, and there will be no cars with speed 0 or other “incorrect” objects in the program. Their use is very beneficial, first of all, for the programmer himself. If you initialize the fields yourself, there is a high risk of missing something and making a mistake. But this will not happen with a constructor: if you did not pass all the required arguments to it or mixed up their types, the compiler will immediately throw an error. It is worth mentioning separately that you should not put the logic of your program inside the constructor. To do this, you have methods at your disposal in which you can describe all the functionality you need. Let's look at why constructor logic is a bad idea:
public class CarFactory {

   String name;
   int age;
   int carsCount;

   public CarFactory(String name, int age, int carsCount) {
   this.name = name;
   this.age = age;
   this.carsCount = carsCount;

   System.out.println("Our car factory is called" + this.name);
   System.out.println("She was founded" + this.age + " years ago" );
   System.out.println("During this time it was produced" + this.carsCount +  "cars");
   System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
}

   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Ford", 115 , 50000000);
   }
}
We have a CarFactory class that describes a factory for producing cars. Inside the constructor, we initialize all the fields and place the logic here: we display some information about the factory to the console. It would seem that there is nothing wrong with this, the program worked perfectly. Console output:
Our car factory is called Ford. It was founded 115 years ago. During this time, it has produced 50,000,000 cars. On average, it produces 434,782 cars per year.
But in fact, we have planted a time bomb. And such code can very easily lead to errors. Let's imagine that now we are not talking about Ford, but about the new factory "Amigo Motors", which has existed for less than a year and has produced 1000 cars:
public class CarFactory {

   String name;
   int age;
   int carsCount;

   public CarFactory(String name, int age, int carsCount) {
   this.name = name;
   this.age = age;
   this.carsCount = carsCount;

   System.out.println("Our car factory is called" + this.name);
   System.out.println("She was founded" + this.age + " years ago" );
   System.out.println("During this time it was produced" + this.carsCount +  "cars");
   System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
}


   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Amigo Motors", 0 , 1000);
   }
}
Console output:
Our car factory is called Amigo Motors Exception in thread "main" java.lang.ArithmeticException: / by zero It was founded 0 years ago During this time, it produced 1000 cars at CarFactory.<init>(CarFactory.java:15) at CarFactory.main(CarFactory.java:23) Process finished with exit code 1</init>
We've arrived! The program ended with some strange error. Will you try to guess what the reason is? The reason is the logic we placed in the constructor. Specifically, in this line:
System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
Here we do the calculation and divide the number of cars produced by the age of the factory. And since our factory is new (that is, it is 0 years old), the result is division by 0, which is prohibited in mathematics. As a result, the program terminates with an error. What should we have done? Move all the logic into a separate method and call it, for example, printFactoryInfo() . You can pass it a CarFactory object as a parameter . You can also put all the logic there, and at the same time - processing possible errors, like ours with zero years. To each his own. Constructors are needed to correctly set the state of an object. For business logic we have methods. You shouldn't mix one with the other.
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION