CodeGym /Java Blog /Random-IT /Costruttori Java
John Squirrels
Livello 41
San Francisco

Costruttori Java

Pubblicato nel gruppo Random-IT
CIAO! Oggi considereremo un argomento molto importante che riguarda i nostri oggetti. Senza esagerare, possiamo dire che utilizzerai questo argomento nella vita reale tutti i giorni! Stiamo parlando di costruttori Java. Questa potrebbe essere la prima volta che senti questo termine, ma in realtà hai già utilizzato i costruttori. Semplicemente non te ne sei reso conto :) Ci convinciamo di questo più tardi.

Cosa nel mondo sono i costruttori e perché sono necessari?

Consideriamo 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 = 378;

   }
}
Abbiamo creato la nostra macchina, ne abbiamo impostato il modello e la velocità massima. Ma l' oggetto Car ovviamente non avrebbe 2 campi in un vero progetto. Ad esempio, potrebbe avere 16 campi!

public class Car {

   String model;// model
   int maxSpeed;// maximum speed
   int wheels;// wheel width
   double engineVolume;// engine volume
   String color;// color
   int productionYear;// production year
   String ownerFirstName;// first name of owner
   String ownerLastName;// last name of owner
   long price;// price
   boolean isNew;// flag indicating whether car is new
   int seatsInTheCar;// number of seats in the car
   String cabinMaterial;// interior material
   boolean insurance;// flag indicating whether car is insured
   String manufacturerCountry;// manufacturer country
   int trunkVolume;// size of the trunk
   int accelerationTo100km;// how long it takes to accelerate 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.productionYear = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.seatsInTheCar = 2;
       bugatti.maxSpeed = 378;
       bugatti.model = "Bugatti Veyron";

   }

}
Abbiamo creato un nuovo oggetto Car . C'è un problema: abbiamo 16 campi, ma ne abbiamo inizializzati solo 12 ! Guarda ora il codice e prova a trovare i campi che abbiamo dimenticato! Non è così facile, eh? In questa situazione, un programmatore può facilmente commettere un errore e non riuscire a inizializzare un campo. Di conseguenza, il programma si comporterà in modo errato:

public class Car {

   String model;// model
   int maxSpeed;// maximum speed
   int wheels;// wheel width
   double engineVolume;// engine volume
   String color;// color
   int productionYear;// production year
   String ownerFirstName;// first name of owner
   String ownerLastName;// last name of owner
   long price;// price
   boolean isNew;// flag indicating whether car is new
   int seatsInTheCar;// number of seats in the car
   String cabinMaterial;// interior material
   boolean insurance;// flag indicating whether car is insured
   String manufacturerCountry;// manufacturer country
   int trunkVolume;// size of the trunk
   int accelerationTo100km;// how long it takes to accelerate 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.productionYear = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.seatsInTheCar = 2;
       bugatti.maxSpeed = 378;
       bugatti.model = "Bugatti Veyron";

       System.out.println("Model: Bugatti Veyron. Engine volume: " + bugatti.engineVolume + ". Trunk volume: " + bugatti.trunkVolume + ". Cabin material: " + bugatti.cabinMaterial +
       ". Wheel width: " + bugatti.wheels + ". Purchased in 2018 by Mr. " + bugatti.ownerLastName);

   }

}
Uscita console: Modello: Bugatti Veyron. Cilindrata del motore: 6.3. Volume bagagliaio: 0. Materiale abitacolo: nullo. Larghezza ruota: 0. Acquistata nel 2018 da Mr. null Il tuo acquirente, che ha rinunciato a $ 2 milioni per l'auto, ovviamente non gradirà essere chiamato " Mr. null "! Ma seriamente, la linea di fondo è che il nostro programma ha creato un oggetto in modo errato: un'auto con una larghezza delle ruote pari a 0 (cioè senza ruote), un bagagliaio mancante, una cabina fatta di un materiale sconosciuto e, soprattutto, un proprietario indefinito . Puoi solo immaginare come un tale errore possa "scoppiare" quando il programma è in esecuzione! Dobbiamo evitare tali situazioni in qualche modo. Dobbiamo limitare il nostro programma: durante la creazione di una nuova autooggetto, vogliamo che i campi, come il modello e la velocità massima, siano sempre specificati. Altrimenti, vogliamo impedire la creazione dell'oggetto. I costruttori gestiscono questo compito con facilità. Hanno preso il loro nome per un motivo. Il costruttore crea una sorta di "scheletro" di classe che ogni nuovo oggetto deve corrispondere. Per comodità, torniamo alla versione più semplice della classe Car con due campi. Considerando i nostri requisiti, il costruttore della classe Car sarà simile a questo:

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", 378);
}
Nota come viene dichiarato un costruttore. È simile a un metodo normale, ma non ha un tipo restituito. Inoltre, il costruttore specifica il nome della classe ( Car ) che inizia con una lettera maiuscola. Inoltre, il costruttore viene utilizzato con una parola chiave nuova per te: this . La parola chiave this serve per indicare un particolare oggetto. Il codice nel costruttore

public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
può essere interpretato quasi alla lettera: "Il modello per questa macchina (quella che stiamo creando ora) è l' argomento del modello passato al costruttore. Il maxSpeed ​​per questa macchina (quella che stiamo creando) è l' argomento maxSpeed ​​passato al costruttore." Ed è proprio quello che succede:

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", 378);
       System.out.println(bugatti.model);
       System.out.println(bugatti.maxSpeed);
   }

}
Uscita console: Bugatti Veyron 378 Il costruttore ha assegnato correttamente i valori richiesti. Potresti aver notato che un costruttore è molto simile a un metodo ordinario! Così è. Un costruttore è in realtà un metodo, ma con caratteristiche specifiche :) Proprio come con i metodi, abbiamo passato argomenti al nostro costruttore. E proprio come chiamare un metodo, chiamare un costruttore non funzionerà se non lo 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!
   }
  
}
Puoi vedere che il costruttore realizza ciò che stavamo cercando di ottenere. Ora non puoi creare un'auto senza una velocità o un modello! La somiglianza tra costruttori e metodi non finisce qui. Proprio come i metodi, i costruttori possono essere sovraccaricati. Immagina di avere 2 gatti domestici a casa. Ne hai preso uno da gattino. Ma il secondo l'hai preso dalla strada quando era già cresciuto e non sai esattamente quanti anni ha. In questo caso, vogliamo che il nostro programma sia in grado di creare due tipi di gatti: quelli con nome ed età (per il primo gatto), e quelli con solo nome (per il secondo gatto). Per questo, 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 smudge = new Cat("Smudge", 5);
       Cat streetCatNamedBob = new Cat("Bob");
   }

}
Oltre al costruttore originale con i parametri "name" e "age", ne abbiamo aggiunto un altro con solo un parametro name. Esattamente nello stesso modo in cui abbiamo sovraccaricato i metodi nelle lezioni precedenti. Ora possiamo creare entrambi i tipi di gatti :)
Perché abbiamo bisogno di costruttori?  - 2
Ricordi che all'inizio della lezione abbiamo detto che hai già utilizzato i costruttori senza rendertene conto? Intendevamo quello che abbiamo detto. Il fatto è che ogni classe in Java ha quello che viene chiamato un costruttore predefinito. Non accetta argomenti, ma viene invocato ogni volta che crei un oggetto di qualsiasi classe.

public class Cat {

   public static void main(String[] args) {

       Cat smudge = new Cat(); // The default constructor is invoked here
   }
}
A prima vista, è invisibile. Abbiamo creato un oggetto, e allora? Dove sta facendo qualcosa il costruttore qui? Per vederlo, scriviamo esplicitamente un costruttore vuoto per la classe Cat . Visualizzeremo alcune frasi al suo interno. Se la frase viene visualizzata, il costruttore è stato richiamato.

public class Cat {

   public Cat() {
       System.out.println("A cat has been created!");
   }

   public static void main(String[] args) {

       Cat smudge = new Cat(); // The default constructor is invoked here
   }
}
Output della console: è stato creato un gatto! C'è la conferma! Il costruttore predefinito è sempre presente in modo invisibile nelle tue classi. Ma devi sapere un'altra cosa al riguardo. Il costruttore predefinito viene eliminato da una classe una volta creato un costruttore con argomenti. In effetti, ne abbiamo già visto la prova sopra. Era 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 smudge = new Cat(); //Error!
   }
}
Non è stato possibile creare un Cat senza nome ed età, perché abbiamo dichiarato un costruttore Cat con parametri string e int . Ciò ha causato l'immediata scomparsa del costruttore predefinito dalla classe. Quindi assicurati di ricordare che se hai bisogno di diversi costruttori nella tua classe, incluso un costruttore senza argomenti, dovrai dichiararlo separatamente. Ad esempio, supponiamo di creare un programma per una clinica veterinaria. La nostra clinica vuole fare buone azioni e aiutare i gattini senzatetto i cui nomi ed età sono sconosciuti. Quindi il nostro codice dovrebbe assomigliare a questo:

public class Cat {

   String name;
   int age;

   // For cats with owners
   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   // For street cats
   public Cat() {
   }

   public static void main(String[] args) {

       Cat smudge = new Cat("Smudge", 5);
       Cat streetCat = new Cat();
   }
}
Ora che abbiamo scritto un costruttore predefinito esplicito, possiamo creare entrambi i tipi di cat :) Come con qualsiasi metodo, l'ordine degli argomenti passati a un costruttore è molto importante. Scambiamo gli argomenti name ed age 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 smudge = new Cat("Smudge", 10); // Error!
   }
}
Un errore! Il costruttore stabilisce chiaramente che quando viene creato un oggetto Cat , deve essere passato un numero e una stringa, in questo ordine. Quindi, il nostro codice non funziona. Assicurati di ricordarlo e tienilo a mente quando dichiari 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 costruttori completamente diversi! Se dovessimo esprimere in una singola frase la risposta alla domanda "Perché ho bisogno di un costruttore?", potremmo dire "Per garantire che gli oggetti abbiano sempre uno stato valido". Quando usi i costruttori, tutte le tue variabili saranno inizializzate correttamente. I tuoi programmi non avranno auto con una velocità pari a 0 o altri oggetti "non validi". Il loro vantaggio principale è per il programmatore. Se inizializzi i campi manualmente (dopo aver creato un oggetto), c'è un grande rischio che ti perda qualcosa e introduca un bug. Ma questo non accadrà con un costruttore: se non riesci a passare tutti gli argomenti richiesti o passi i tipi di argomenti sbagliati, il compilatore registrerà immediatamente un errore. Dobbiamo anche dire separatamente che non dovresti mettere il tuo programma' s logica all'interno di un costruttore. Ecco a cosa servono i metodi. I metodi sono dove dovresti definire tutte le funzionalità richieste. Vediamo perché aggiungere la logica a un 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("It was founded " + this.age + " years ago" );
   System.out.println("Since that time, it has produced " + this.carsCount +  " cars");
   System.out.println("On average, it 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 la fabbrica di automobili. All'interno del costruttore, inizializziamo tutti i campi e includiamo una logica: mostriamo alcune informazioni sulla fabbrica. Sembra che non ci sia niente di male in questo. Il programma funziona bene. Uscita console: la nostra fabbrica di automobili si chiama Ford È stata fondata 115 anni fa Da allora, ha prodotto 50000000 auto In media, produce 434782 auto all'anno Ma in realtà abbiamo piazzato una mina ritardata. E questo tipo di codice può facilmente portare a errori. Supponiamo che ora non stiamo parlando di Ford, ma di una nuova fabbrica chiamata "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 factor is called " + this.name);
   System.out.println("It was founded " + this.age + " years ago" );
   System.out.println("Since that time, it has produced " + this.carsCount +  " cars");
   System.out.println("On average, it produces " + (this.carsCount/this.age) + " cars per year");
}


   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Amigo Motors", 0 , 1000);
   }
}
Uscita della console: la nostra fabbrica di automobili si chiama Amigo Motors Eccezione nel thread "principale" java.lang.ArithmeticException: / per zero È stata fondata 0 anni fa Da allora, ha prodotto 1000 auto presso CarFactory. (CarFactory.java:15) in CarFactory.main(CarFactory.java:23) Processo terminato con codice di uscita 1 Boom! Il programma termina con una sorta di errore incomprensibile. Puoi provare a indovinare la causa? Il problema è nella logica che inseriamo nel costruttore. In particolare, questa riga:

System.out.println("On average, it produces " + (this.carsCount/this.age) + " cars per year");
Qui esegui un calcolo e dividi il numero di auto prodotte per l'età della fabbrica. E poiché la nostra fabbrica è nuova (cioè ha 0 anni), dividiamo per 0, cosa che non possiamo fare in matematica. Di conseguenza, il programma termina con un errore.

Cosa avremmo dovuto fare?

Metti tutta la logica in un metodo separato. Chiamiamolo printFactoryInfo() . Puoi passargli un oggetto CarFactory come argomento. Puoi mettere tutta la logica lì e gestire contemporaneamente potenziali errori (come il nostro che coinvolge zero anni). A ciascuno il suo. I costruttori sono necessari per impostare lo stato dell'oggetto valido. Abbiamo metodi per la logica aziendale. Non mescolare l'uno con l'altro. Per rafforzare ciò che hai imparato, ti suggeriamo di guardare una lezione video dal nostro corso Java
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION