JavaRush /Java Blog /Random EN /Constructors in Java

Constructors in Java

Published in the Random EN group
Hello! Today we will analyze 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 hear this term for the first time, but in fact you probably used constructors, only you didn’t notice it yourself :) We will see this later.

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

Let's consider 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 the model and top speed for it. However, in a real project, the Car object will obviously not have 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 by 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 behavior of the program 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:
Model Bugatti Veyron. The engine size is 6.3, the trunk is 0, the interior is made of null, the width of the wheels is 0. It was purchased in 2018 by Mr. null
Your customer, who paid $2 million for a car, will obviously not like being called “Mr. Null”! But seriously, as a result, our program ended up with an incorrectly created object - a car with a disk width of 0 (that is, no disks at all), a missing trunk, an interior made of an unknown material, and even belonging to someone who knows. One can only imagine how such an error can “shoot” when the program is running! We need to somehow avoid such situations. It is necessary that our program has a restriction: when creating a new car object, for example, the model and maximum speed must always be specified for it. Otherwise, do not allow object creation. Constructor functions can 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 class object must conform. Let's go back to a simpler version of the class for convenience.Car 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 the creation of the 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 name of the class is indicated in the constructor, also with a capital letter. In our case, Car . In addition, the constructor uses the this keyword, which is new to you . "this" in English is "this one". This word refers to a specific subject. Code in constructor:
public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
can be translated almost verbatim: " model for this car (which we are currently creating) = the model argument that is specified in the constructor. maxSpeed ​​for this car (which we are creating) = the maxSpeed ​​argument that is specified in the constructor." And so it 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 desired values. You may have noticed that a constructor is very similar to a regular method! So 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 will fail if they are not specified:
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 not create a car without speed or without a model! The similarities between constructors and methods don't 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 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. In the same way, we overloaded methods in previous lessons. Now we can successfully create both variants of cats :) Why are constructors needed?  - 2Do you remember at the beginning of the lecture we said that you already used constructors, but you didn’t notice it yourself? This is true. The fact is that every class in Java has a so-called default constructor. It doesn't take 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 imperceptible. Well, they created an object and created it, where is the work of the constructor? To see this, let's directly write an empty constructor for the Cat class , and inside it we will display some phrase in 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:
Created a cat!
Here is 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 immediately after that disappeared from the class. 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 which we do not know either the 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. Therefore, our code does not work. Be sure to keep this in mind and keep it 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 designs! Why is a constructor needed?  - 3If we express in one sentence the answer to the question “Why do we need a constructor?”, We can say: in order for objects to always be in the correct state. When you use constructors, all your variables will be correctly initialized, and there will be no machines with speed 0 and other "wrong" objects in the program. Their use is very beneficial primarily for the programmer himself. If you initialize the fields yourself, there is a great risk of missing something and making a mistake. But this will not happen with the constructor: if you did not pass all the required arguments to it or mixed up their types, the compiler will immediately give an error. Separately, it should be said 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 watch,
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 car factory. Inside the constructor, we initialize all the fields, and put the logic here: print 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 that time, it produced 50,000,000 cars On average, it produces 434,782 cars a year
But in fact, we have planted a time bomb. And such code can very easily lead to errors. 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 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. Can you guess what is the reason? The reason is in the logic that 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 perform a calculation and divide the number of machines produced by the age of the factory. And since our factory is new (that is, it is 0 years old), the result is a division by 0, which is forbidden in mathematics. As a result, the program terminates with an error. How 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 - the handling of 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. Do not mix one with the other.
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION