1. Inizializzazione delle variabili
Come già sai, puoi dichiarare diverse variabili nella tua classe, e non solo dichiararle, ma anche inizializzarle immediatamente con i loro valori iniziali.
E queste stesse variabili possono essere inizializzate anche in un costruttore. Ciò significa che, in teoria, a queste variabili potrebbero essere assegnati valori due volte. Esempio
Codice | Nota |
---|---|
|
Alla age variabile viene assegnato un valore iniziale Il valore iniziale viene sovrascritto La variabile age memorizza il suo valore iniziale. |
|
Questo è consentito: verrà chiamato il primo costruttore |
|
Questo è consentito: verrà chiamato il secondo costruttore |
Questo è ciò che accade quando Cat cat = new Cat("Whiskers", 2);
viene eseguito:
Cat
Viene creato un oggetto- Tutte le variabili di istanza vengono inizializzate con i loro valori iniziali
- Il costruttore viene chiamato e il suo codice viene eseguito.
In altre parole, le variabili ottengono prima i loro valori iniziali e solo allora viene eseguito il codice del costruttore.
2. Ordine di inizializzazione delle variabili in una classe
Le variabili non vengono semplicemente inizializzate prima dell'esecuzione del costruttore, ma vengono inizializzate in un ordine ben definito: l'ordine in cui vengono dichiarate nella classe.
Diamo un'occhiata a un codice interessante:
Codice | Nota |
---|---|
|
Questo codice non verrà compilato, poiché al momento a
della creazione della variabile non ci sono ancora variabili b
e . c
Ma puoi scrivere il tuo codice come segue: questo codice verrà compilato e funzionerà correttamente.
Codice | Nota |
---|---|
|
00 +20 +2+3 |
Ma ricorda che il tuo codice deve essere trasparente per gli altri sviluppatori. È meglio non utilizzare tecniche di questo tipo, poiché compromettono la leggibilità del codice.
Qui dobbiamo ricordare che prima che alle variabili venga assegnato un valore hanno un valore predefinito . Per il int
tipo, questo è zero.
Quando la JVM inizializza la a
variabile, assegnerà semplicemente il valore predefinito per il tipo int: 0.
Quando raggiunge b
, la variabile a sarà già nota e avrà un valore, quindi la JVM le assegnerà il valore 2.
E quando raggiunge la c
variabile, le variabili a
e b
saranno già inizializzate, quindi la JVM calcolerà facilmente il valore iniziale per c
: 0+2+3.
Se crei una variabile all'interno di un metodo, non puoi usarla a meno che tu non le abbia precedentemente assegnato un valore. Ma questo non è vero per le variabili di una classe! Se un valore iniziale non è assegnato a una variabile di una classe, allora viene assegnato un valore predefinito.
3. Costanti
Mentre analizziamo come vengono creati gli oggetti, vale la pena soffermarsi sull'inizializzazione delle costanti, cioè delle variabili con il final
modificatore.
Se una variabile ha il final
modificatore, deve essere assegnato un valore iniziale. Lo sai già e non c'è nulla di sorprendente in questo.
Ma quello che non sai è che non devi assegnare subito il valore iniziale se lo assegni nel costruttore. Questo funzionerà bene per una variabile finale. L'unico requisito è che se si dispone di più costruttori, a una variabile finale deve essere assegnato un valore in ciascun costruttore.
Esempio:
public class Cat
{
public final int maxAge = 25;
public final int maxWeight;
public Cat (int weight)
{
this.maxWeight = weight; // Assign an initial value to the constant
}
}
4. Codice in un costruttore
E alcune note più importanti sui costruttori. Successivamente, mentre continui ad imparare Java, ti imbatterai in cose come ereditarietà, serializzazione, eccezioni, ecc. Tutte influenzano il lavoro dei costruttori a vari livelli. Non ha senso approfondire questi argomenti ora, ma siamo obbligati almeno a toccarli.
Ad esempio, ecco un'osservazione importante sui costruttori. In teoria, puoi scrivere codice di qualsiasi complessità in un costruttore. Ma non farlo. Esempio:
|
Aprire un file read stream Leggere il file in un array di byte Salvare l'array di byte come una stringa Visualizzare il contenuto del file sullo schermo |
Nel costruttore della classe FilePrinter, abbiamo immediatamente aperto un flusso di byte su un file e letto il suo contenuto. Questo è un comportamento complesso e può causare errori.
E se non esistesse un file del genere? E se ci fossero problemi con la lettura del file? E se fosse troppo grande?
La logica complessa implica un'alta probabilità di errori e ciò significa che il codice deve gestire correttamente le eccezioni.
Esempio 1 — Serializzazione
In un programma Java standard, ci sono molte situazioni in cui non sei tu a creare oggetti della tua classe. Ad esempio, supponiamo che tu decida di inviare un oggetto sulla rete: in questo caso, la macchina Java stessa convertirà il tuo oggetto in un insieme di byte, lo invierà e ricreerà l'oggetto dall'insieme di byte.
Ma poi supponiamo che il tuo file non esista sull'altro computer. Ci sarà un errore nel costruttore e nessuno lo gestirà. E questo è perfettamente in grado di causare la chiusura del programma.
Esempio 2 — Inizializzazione dei campi di una classe
Se il tuo costruttore di classe può lanciare eccezioni controllate, cioè è contrassegnato con la parola chiave throws, allora devi intercettare le eccezioni indicate nel metodo che crea il tuo oggetto.
Ma cosa succede se non esiste un tale metodo? Esempio:
Codice | Nota |
---|---|
|
Questo codice non verrà compilato. |
Il FilePrinter
costruttore della classe può generare un'eccezione verificata , il che significa che non è possibile creare un FilePrinter
oggetto senza racchiuderlo in un blocco try-catch. E un blocco try-catch può essere scritto solo in un metodo
5. Costruttore della classe base
Nelle lezioni precedenti abbiamo parlato un po' dell'ereditarietà. Sfortunatamente, la nostra discussione completa su ereditarietà e OOP è riservata al livello dedicato a OOP e l'ereditarietà dei costruttori è già rilevante per noi.
Se la tua classe eredita un'altra classe, un oggetto della classe genitore verrà incorporato all'interno di un oggetto della tua classe. Inoltre, la classe genitore ha le proprie variabili ei propri costruttori.
Ciò significa che è molto importante per te sapere e capire come vengono inizializzate le variabili e come vengono chiamati i costruttori quando la tua classe ha una classe genitore e ne erediti le variabili e i metodi.
Classi
Come facciamo a sapere l'ordine in cui le variabili vengono inizializzate e vengono chiamati i costruttori? Iniziamo scrivendo il codice per due classi. Uno erediterà l'altro:
Codice | Nota |
---|---|
|
La ChildClass classe eredita la ParentClass classe. |
Dobbiamo determinare l'ordine in cui le variabili vengono inizializzate e vengono chiamati i costruttori. La registrazione ci aiuterà a farlo.
Registrazione
La registrazione è il processo di registrazione delle azioni eseguite da un programma durante l'esecuzione, scrivendole nella console o in un file.
È abbastanza semplice determinare che il costruttore è stato chiamato: nel corpo del costruttore, scrivi un messaggio alla console. Ma come puoi sapere se una variabile è stata inizializzata?
In realtà, anche questo non è molto difficile: scrivere un metodo speciale che restituirà il valore utilizzato per inizializzare la variabile e registrare l'inizializzazione. Ecco come potrebbe apparire il codice:
Codice finale
|
Crea un ChildClass oggetto Questo metodo scrive il testo passato alla console e lo restituisce. Dichiara la ParentClass classe Display text e inizializza anche le variabili con essa. Scrivi un messaggio che il costruttore è stato chiamato. Ignora il valore restituito. Dichiara la ChildClass classe Display text e inizializza anche le variabili con essa. Scrivi un messaggio che il costruttore è stato chiamato. Ignora il valore restituito. |
Se esegui questo codice, il testo verrà visualizzato sullo schermo come segue:
Output della console del metodoMain.print() |
---|
|
Quindi puoi sempre assicurarti personalmente che le variabili di una classe vengano inizializzate prima che venga chiamato il costruttore. Una classe base viene inizializzata completamente prima dell'inizializzazione della classe ereditata.
GO TO FULL VERSION