JavaRush /Java Blog /Random-IT /Costruttori in Java

Costruttori in Java

Pubblicato nel gruppo Random-IT
Ciao! Oggi affronteremo un argomento molto importante che riguarda i nostri oggetti. Qui, senza esagerare, possiamo dire che utilizzerai queste conoscenze ogni giorno nel lavoro reale! Parleremo di costruttori. Potresti sentire questo termine per la prima volta, ma in realtà probabilmente hai usato i costruttori, ma non te ne sei accorto tu stesso :) Lo vedremo più tardi.

Cos'è un costruttore in Java e perché è necessario?

Diamo un'occhiata a due esempi.
public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {

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

   }
}
Abbiamo creato la nostra macchina e ne abbiamo impostato il modello e la velocità massima. Tuttavia, in un progetto reale, l' oggetto Auto avrà chiaramente più di 2 campi. E, per esempio, 16 campi!
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";

   }

}
Abbiamo creato un nuovo oggetto Auto . Un problema: abbiamo 16 campi, ma ne abbiamo inizializzati solo 12 ! Prova ora a utilizzare il codice per ritrovare quelli che abbiamo dimenticato! Non è così facile, vero? In una situazione del genere, il programmatore può facilmente commettere un errore e saltare l'inizializzazione di alcuni campi. Di conseguenza, il comportamento del programma diventerà errato:
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);

   }

}
Uscita console:
Modello Bugatti Veyron. Cilindrata del motore - 6,3, bagagliaio - 0, interni realizzati in nullo, larghezza del cerchione - 0. È stato acquistato nel 2018 dal signor null
Al tuo acquirente, che ha pagato 2 milioni di dollari per un'auto, ovviamente non piacerà essere chiamato "Mr. Null"! Ma seriamente, alla fine il nostro programma si è ritrovato con un oggetto creato in modo errato: un'auto con larghezza del cerchione pari a 0 (cioè senza cerchioni), un bagagliaio mancante, interni di materiale sconosciuto e persino appartenente a qualcuno sconosciuto . Si può solo immaginare come possa verificarsi un errore del genere mentre il programma è in esecuzione! Dobbiamo in qualche modo evitare tali situazioni. Abbiamo bisogno che il nostro programma abbia una limitazione: quando si crea un nuovo oggetto veicolo, ad esempio, è necessario specificarne sempre il modello e la velocità massima. In caso contrario, non consentire la creazione di oggetti. Le funzioni del costruttore affrontano facilmente questo compito. Hanno preso il loro nome per un motivo. Il costruttore crea una sorta di “scheletro” della classe, al quale deve corrispondere ogni nuovo oggetto della classe. Per comodità, torniamo a una versione più semplice della classe Car con due campi. Dati i nostri requisiti, il costruttore della classe Car sarà simile a questo:
public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
E la creazione di un oggetto ora assomiglia a questa:
public static void main(String[] args) {
   Car bugatti = new Car("Bugatti Veyron", 407);
}
Fai attenzionecome viene creato il costruttore. È simile a un metodo normale, ma non ha un tipo restituito. In questo caso il nome della classe è indicato nel costruttore, anch'esso con la lettera maiuscola. Nel nostro caso - Auto . Inoltre, il costruttore utilizza la parola chiave new-to-you this . "questo" in inglese significa "questo, questo". Questa parola si riferisce a un oggetto specifico. Codice nel costruttore:
public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
può essere tradotto quasi letteralmente: " modello per questa macchina (che stiamo creando) = l' argomento model , che è specificato nel costruttore. maxSpeed ​​​​per questa macchina (che stiamo creando) = l' argomento maxSpeed ​​, che è specificato nel costruttore." Questo è quello che è successo:
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);
   }

}
Uscita console:
Bugatti Veyron 407
Il costruttore ha assegnato con successo i valori richiesti. Potresti aver notato che un costruttore è molto simile a un metodo normale! È così: un costruttore è un metodo, solo un po' specifico :) Proprio come in un metodo, abbiamo passato i parametri al nostro costruttore. E proprio come chiamare un metodo, chiamare un costruttore non funzionerà se non li specifichi:
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!
   }

}
Vedi, il designer ha fatto quello che stavamo cercando di ottenere. Ora non puoi creare un'auto senza velocità o senza un modello! Le somiglianze tra costruttori e metodi non finiscono qui. Proprio come i metodi, i costruttori possono essere sovraccaricati. Immagina di avere 2 gatti a casa. Ne hai preso uno da gattino e il secondo l'hai portato a casa dalla strada da adulto e non sai esattamente quanti anni abbia. Ciò significa che il nostro programma dovrebbe essere in grado di creare gatti di due tipi: con un nome ed età per il primo gatto e solo con un nome - per il secondo gatto. Per fare ciò, sovraccaricheremo il costruttore:
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");
   }

}
Al costruttore originale con i parametri “name” e “age” ne abbiamo aggiunto un altro, solo con un nome. Abbiamo sovraccaricato i metodi allo stesso modo nelle lezioni precedenti. Ora possiamo creare con successo entrambe le versioni dei gatti :) Perché sono necessari i costruttori?  - 2Ti ricordi all'inizio della lezione abbiamo detto che avevi già utilizzato i costruttori, ma non te ne sei accorto? Questo è vero. Il fatto è che ogni classe in Java ha un cosiddetto costruttore predefinito. Non ha argomenti, ma si attiva ogni volta che viene creato un oggetto di qualsiasi classe.
public class Cat {

   public static void main(String[] args) {

       Cat barsik = new Cat(); //this is where the default constructor worked
   }
}
A prima vista questo non è evidente. Bene, abbiamo creato un oggetto e lo abbiamo creato, dov’è finito il lavoro del designer? Per vederlo, scriviamo con le nostre mani un costruttore vuoto per la classe Cat e al suo interno stamperemo qualche frase sulla console. Se viene visualizzato, il costruttore ha funzionato.
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
   }
}
Uscita console:
Hanno creato un gatto!
Ecco la conferma! Il costruttore predefinito è sempre presente in modo invisibile nelle tue classi. Ma devi conoscerne un'altra caratteristica. Il costruttore predefinito scompare dalla classe quando crei un costruttore con argomenti. La prova di ciò, in effetti, l'abbiamo già vista sopra. Qui in questo codice:
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!
   }
}
Non potevamo creare un gatto senza nome ed età perché abbiamo definito un costruttore per Cat : stringa + numero. Il costruttore predefinito è scomparso dalla classe subito dopo. Pertanto, assicurati di ricordare: se hai bisogno di più costruttori nella tua classe, incluso uno vuoto, devi crearlo separatamente. Ad esempio, stiamo creando un programma per una clinica veterinaria. La nostra clinica vuole fare buone azioni e aiutare i gatti senza casa, di cui non conosciamo il nome né l'età. Quindi il nostro codice dovrebbe assomigliare a questo:
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();
   }
}
Ora che abbiamo scritto esplicitamente un costruttore predefinito, possiamo creare gatti di entrambi i tipi :) Per un costruttore (come per qualsiasi metodo), l'ordine degli argomenti è molto importante. Scambiamo gli argomenti nome ed età nel nostro costruttore.
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!
   }
}
Errore! Il costruttore afferma chiaramente che quando viene creato un oggetto Cat, è necessario passargli un numero e una stringa, in quest'ordine. Ecco perché il nostro codice non funziona. Assicurati di ricordarlo e di tenerlo a mente quando crei le tue classi:
public Cat(String name, int age) {
   this.name = name;
   this.age = age;
}

public Cat(int age, String name) {
   this.age = age;
   this.name = name;
}
Questi sono due designer completamente diversi! Se esprimiamo la risposta alla domanda “Perché abbiamo bisogno di un costruttore?” in una frase, possiamo dire: in modo che gli oggetti siano sempre nello stato corretto. Quando usi i costruttori, tutte le tue variabili saranno inizializzate correttamente e non ci saranno auto con velocità 0 o altri oggetti "errati" nel programma. Il loro utilizzo è molto vantaggioso, prima di tutto, per il programmatore stesso. Se inizializzi tu stesso i campi, c'è un alto rischio di perdere qualcosa e di commettere un errore. Ma questo non accadrà con un costruttore: se non gli hai passato tutti gli argomenti richiesti o non hai confuso i loro tipi, il compilatore genererà immediatamente un errore. Vale la pena ricordare separatamente che non dovresti inserire la logica del tuo programma all'interno del costruttore. Per fare ciò, hai a tua disposizione metodi in cui puoi descrivere tutte le funzionalità di cui hai bisogno. Diamo un'occhiata al motivo per cui la logica del costruttore è una cattiva 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);
   }
}
Abbiamo una classe CarFactory che descrive una fabbrica per la produzione di automobili. All'interno del costruttore inizializziamo tutti i campi e posizioniamo qui la logica: mostriamo alla console alcune informazioni sulla factory. Sembrerebbe che non ci sia niente di sbagliato in questo, il programma ha funzionato perfettamente. Uscita console:
La nostra fabbrica di automobili si chiama Ford, è stata fondata 115 anni fa e durante questo periodo ha prodotto 50.000.000 di automobili, ovvero in media 434.782 automobili all'anno.
Ma in realtà abbiamo piazzato una bomba a orologeria. E tale codice può facilmente portare a errori. Immaginiamo che ora non stiamo parlando di Ford, ma della nuova fabbrica "Amigo Motors", che esiste da meno di un anno e ha prodotto 1000 auto:
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);
   }
}
Uscita console:
La nostra fabbrica di automobili si chiama Amigo Motors Exception nel thread "main" java.lang.ArithmeticException: / by zero È stata fondata 0 anni fa Durante questo periodo, ha prodotto 1000 auto presso CarFactory.<init>(CarFactory.java:15) su CarFactory.main(CarFactory.java:23) Processo terminato con codice di uscita 1</init>
Siamo arrivati! Il programma si è concluso con qualche strano errore. Proverai a indovinare qual è il motivo? Il motivo è la logica che abbiamo inserito nel costruttore. Nello specifico in questa riga:
System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
Qui facciamo il calcolo e dividiamo il numero di auto prodotte per l'età della fabbrica. E poiché la nostra fabbrica è nuova (cioè ha 0 anni), il risultato è la divisione per 0, cosa vietata in matematica. Di conseguenza, il programma termina con un errore. Cosa avremmo dovuto fare? Sposta tutta la logica in un metodo separato e chiamalo, ad esempio, printFactoryInfo() . Puoi passargli un oggetto CarFactory come parametro . Puoi anche inserire tutta la logica lì e, allo stesso tempo, elaborare possibili errori, come il nostro con zero anni. A ciascuno il suo. I costruttori sono necessari per impostare correttamente lo stato di un oggetto. Per la logica aziendale abbiamo metodi. Non dovresti mescolare l'uno con l'altro.
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION