JavaRush /Blog Java /Random-FR /Constructeurs en Java

Constructeurs en Java

Publié dans le groupe Random-FR
Bonjour! Aujourd'hui, nous allons aborder un sujet très important qui concerne nos objets. Ici, sans exagération, nous pouvons dire que vous utiliserez ces connaissances au quotidien dans un travail réel ! Nous parlerons des constructeurs. Vous entendez peut-être ce terme pour la première fois, mais en fait vous avez probablement utilisé des constructeurs, mais vous ne l'avez tout simplement pas remarqué vous-même :) Nous verrons cela plus tard.

Qu’est-ce qu’un constructeur en Java et pourquoi est-il nécessaire ?

Regardons deux exemples.
public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {

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

   }
}
Nous avons créé notre voiture et défini son modèle et sa vitesse maximale. Cependant, dans un projet réel, l' objet Car aura clairement plus de 2 champs. Et, par exemple, 16 champs !
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";

   }

}
Nous avons créé un nouvel objet Car . Un problème : nous avons 16 champs, mais nous n’en avons initialisé que 12 ! Essayez maintenant d'utiliser le code pour retrouver ceux que nous avons oubliés ! Pas si facile, non ? Dans une telle situation, le programmeur peut facilement se tromper et ignorer l'initialisation de certains champs. En conséquence, le comportement du programme deviendra erroné :
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);

   }

}
Sortie de la console :
Modèle Bugatti Veyron. Cylindrée du moteur - 6,3, coffre - 0, intérieur en null, largeur de jante - 0. A été acheté en 2018 par M. null
Votre acheteur, qui a payé 2 millions de dollars pour une voiture, n’aimera évidemment pas qu’on l’appelle « M. Null » ! Mais sérieusement, à la fin, notre programme s'est retrouvé avec un objet mal créé - une voiture avec une largeur de jante de 0 (c'est-à-dire sans jantes du tout), un coffre manquant, un intérieur fait d'un matériau inconnu et même appartenant à quelqu'un d'inconnu. . On ne peut qu'imaginer comment une telle erreur pourrait se produire pendant l'exécution du programme ! Nous devons d’une manière ou d’une autre éviter de telles situations. Nous avons besoin que notre programme ait une limitation : lors de la création d'un nouvel objet véhicule, par exemple, le modèle et la vitesse maximale doivent toujours être spécifiés pour celui-ci. Sinon, n'autorisez pas la création d'objets. Les fonctions constructeur s’acquittent facilement de cette tâche. Ils tirent leur nom pour une raison. Le constructeur crée une sorte de « squelette » de la classe, auquel doit correspondre chaque nouvel objet de la classe. Pour plus de commodité, revenons à une version plus simple de la classe Car avec deux champs. Compte tenu de nos exigences, le constructeur de la classe Car ressemblera à ceci :
public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
Et la création d'un objet ressemble maintenant à ceci :
public static void main(String[] args) {
   Car bugatti = new Car("Bugatti Veyron", 407);
}
Faites attentioncomment le constructeur est créé. Elle est similaire à une méthode classique, mais elle n’a pas de type de retour. Dans ce cas, le nom de la classe est indiqué dans le constructeur, également avec une majuscule. Dans notre cas - Voiture . De plus, le constructeur utilise le mot clé new-to-you this . "this" en anglais signifie "this, this". Ce mot fait référence à un objet spécifique. Code dans le constructeur :
public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
peut être traduit presque littéralement : " modèle pour cette machine (que nous créons maintenant) = l' argument modèle , qui est spécifié dans le constructeur. maxSpeed ​​​​pour cette machine (que nous créons) = l' argument maxSpeed ​​​​, qui est spécifié dans le constructeur." C'est ce qui s'est passé:
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);
   }

}
Sortie de la console :
Bugatti Veyron 407
Le constructeur a attribué avec succès les valeurs requises. Vous avez peut-être remarqué qu’un constructeur est très similaire à une méthode classique ! C'est comme ça : un constructeur est une méthode, juste un peu spécifique :) Tout comme dans une méthode, nous avons passé des paramètres à notre constructeur. Et tout comme appeler une méthode, appeler un constructeur ne fonctionnera pas si vous ne les spécifiez pas :
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!
   }

}
Vous voyez, le designer a fait ce que nous essayions de réaliser. Désormais, vous ne pouvez pas créer une voiture sans vitesse ni sans modèle ! Les similitudes entre les constructeurs et les méthodes ne s’arrêtent pas là. Tout comme les méthodes, les constructeurs peuvent être surchargés. Imaginez que vous ayez 2 chats à la maison. Vous avez pris l’un d’eux quand il était chaton, et vous avez ramené le deuxième de la rue à la maison à l’âge adulte et vous ne savez pas exactement quel âge il a. Cela signifie que notre programme devrait être capable de créer des chats de deux types - avec un nom et un âge pour le premier chat, et uniquement avec un nom - pour le deuxième chat. Pour ce faire, nous allons surcharger le constructeur :
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");
   }

}
Au constructeur d'origine avec les paramètres « nom » et « âge », nous en avons ajouté un autre, uniquement avec un nom. Nous avons surchargé les méthodes de la même manière dans les leçons précédentes. Nous pouvons maintenant créer avec succès les deux versions de cats :) Pourquoi les constructeurs sont-ils nécessaires ?  - 2Vous souvenez-vous qu'au début de la conférence, nous avons dit que vous aviez déjà utilisé des constructeurs, mais que vous ne l'aviez tout simplement pas remarqué ? C'est vrai. Le fait est que chaque classe en Java possède ce qu’on appelle un constructeur par défaut. Il n'a aucun argument, mais il se déclenche à chaque fois qu'un objet d'une classe est créé.
public class Cat {

   public static void main(String[] args) {

       Cat barsik = new Cat(); //this is where the default constructor worked
   }
}
À première vue, cela n’est pas perceptible. Eh bien, nous avons créé un objet et l'avons créé, où est le travail du designer ? Pour voir cela, écrivons de nos propres mains un constructeur vide pour la classe Cat , et à l'intérieur nous imprimerons une phrase sur la console. S'il est affiché, alors le constructeur a fonctionné.
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
   }
}
Sortie de la console :
Ils ont créé un chat !
Voici la confirmation ! Le constructeur par défaut est toujours présent de manière invisible dans vos classes. Mais vous devez en connaître une autre caractéristique. Le constructeur par défaut disparaît de la classe lorsque vous créez un constructeur avec des arguments. La preuve en est en effet déjà vue plus haut. Ici dans ce 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!
   }
}
Nous ne pouvions pas créer un chat sans nom et sans âge car nous avons défini un constructeur pour Cat : string + number. Le constructeur par défaut a disparu de la classe immédiatement après. N'oubliez donc pas : si vous avez besoin de plusieurs constructeurs dans votre classe, dont un vide, vous devez le créer séparément. Par exemple, nous créons un programme pour une clinique vétérinaire. Notre clinique souhaite faire de bonnes actions et aider les chats sans abri, dont nous ne connaissons ni leur nom ni leur âge. Notre code devrait alors ressembler à ceci :
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();
   }
}
Maintenant que nous avons explicitement écrit un constructeur par défaut, nous pouvons créer des chats des deux types :) Pour un constructeur (comme pour toute méthode), l'ordre des arguments est très important. Échangeons les arguments name et age dans notre constructeur.
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!
   }
}
Erreur! Le constructeur indique clairement que lorsqu'un objet Cat est créé, il doit recevoir un nombre et une chaîne, dans cet ordre. C'est pourquoi notre code ne fonctionne pas. N'oubliez pas cela et gardez cela à l'esprit lorsque vous créez vos propres 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;
}
Ce sont deux créateurs complètement différents ! Si nous exprimons la réponse à la question « Pourquoi avons-nous besoin d'un constructeur ? » en une phrase, nous pouvons dire : pour que les objets soient toujours dans le bon état. Lorsque vous utilisez des constructeurs, toutes vos variables seront correctement initialisées et il n'y aura pas de voitures avec une vitesse de 0 ou d'autres objets « incorrects » dans le programme. Leur utilisation est très bénéfique, tout d'abord, pour le programmeur lui-même. Si vous initialisez les champs vous-même, vous risquez fort de manquer quelque chose et de commettre une erreur. Mais cela n'arrivera pas avec un constructeur : si vous ne lui avez pas transmis tous les arguments requis ou si vous avez mélangé leurs types, le compilateur générera immédiatement une erreur. Il convient de mentionner séparément que vous ne devez pas placer la logique de votre programme à l'intérieur du constructeur. Pour ce faire, vous disposez de méthodes dans lesquelles vous pouvez décrire toutes les fonctionnalités dont vous avez besoin. Voyons pourquoi la logique du constructeur est une mauvaise idée :
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);
   }
}
Nous avons une classe CarFactory qui décrit une usine de production de voitures. A l'intérieur du constructeur, nous initialisons tous les champs et plaçons ici la logique : nous affichons des informations sur l'usine sur la console. Il semblerait qu'il n'y ait rien de mal à cela, le programme a parfaitement fonctionné. Sortie de la console :
Notre usine automobile s'appelle Ford. Elle a été fondée il y a 115 ans. Pendant cette période, elle a produit 50 000 000 de voitures. En moyenne, elle produit 434 782 voitures par an.
Mais en réalité, nous avons posé une bombe à retardement. Et un tel code peut très facilement conduire à des erreurs. Imaginons que maintenant nous ne parlons pas de Ford, mais de la nouvelle usine « Amigo Motors », qui existe depuis moins d'un an et a produit 1000 voitures :
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);
   }
}
Sortie de la console :
Notre usine automobile s'appelle Amigo Motors Exception dans le fil "main" java.lang.ArithmeticException : / par zéro Elle a été fondée il y a 0 ans. Pendant cette période, elle a produit 1 000 voitures chez CarFactory.<init>(CarFactory.java:15) à CarFactory.main(CarFactory.java:23) Processus terminé avec le code de sortie 1</init>
Nous sommes arrivés ! Le programme s'est terminé avec une erreur étrange. Allez-vous essayer de deviner quelle en est la raison ? La raison est la logique que nous avons placée dans le constructeur. Plus précisément, dans cette ligne :
System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
Ici, nous faisons le calcul et divisons le nombre de voitures produites par l'âge de l'usine. Et comme notre usine est neuve (c'est-à-dire qu'elle a 0 ans), le résultat est une division par 0, ce qui est interdit en mathématiques. En conséquence, le programme se termine avec une erreur. Qu'aurions-nous dû faire ? Déplacez toute la logique dans une méthode distincte et appelez-la, par exemple, printFactoryInfo() . Vous pouvez lui passer un objet CarFactory en paramètre . Vous pouvez également y mettre toute la logique, et en même temps - traiter d'éventuelles erreurs, comme la nôtre avec zéro an. À chacun ses goûts. Les constructeurs sont nécessaires pour définir correctement l'état d'un objet. Pour la logique métier, nous avons des méthodes. Il ne faut pas mélanger l'un avec l'autre.
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION