Un costruttore può chiamarne un altro della stessa classe usando la parola chiave this, oppure il costruttore della sua superclasse usando la parola chiave super.
This e super, usati in questa accezione, devono comparire alla prima riga del costruttore, pena un errore di compilazione.
- this rappresenta il riferimento all’oggetto corrente;
- super si usa per chiamare un metodo di una superclasse, oppure in una classe interna per ottenere il riferimento all’oggetto che ha creato quello corrente.
Se un costruttore non inizia con una chiamata ad un altro costruttore, viene automaticamente inserita una chiamata al costruttore senza argomenti della superclasse, in questo caso, se la superclasse non ha un costruttore senza argomenti, si verifica un errore di compilazione.
Il meccanismo tramite il quale i costruttori di chiamano a vicenda prende il nome di concatenazione dei costruttori (constructor chaining)_
Il compilatore controlla che la concatenazione non sia ciclica.
Infatti, se dei costruttori si chiamano a vicenda, siccome tali chiamate si trovano alla prima riga del rispettivo costruttore, e pertanto avvengono incondizionatamente, ci si trova in presenza di una mutua ricorsione non ben fondata, ovvero infinita.
Ad esempio, tentando di compilare la seguente classe:
class A {
public A() { this(3); }
public A(int i) { this(); }
}
si ottiene il seguente errore di compilazione:
A.java:3: recursive constructor invocation
public A(int i) { this(); }
^
Per analizzare la concatenazione dei costruttori di una data gerarchia di classi, ed in particolare controllare che essa non sia ciclica, è possibile realizzare il seguente diagramma:
Le classi di eccezione si dividono in verificate (in Inglese, checked) e non verificate (unchecked).
Il termine “verificata” si riferisce al fatto che il compilatore verifica che tali eccezioni siano opportunamente trattate dal programmatore (si veda dopo), mentre le eccezioni non verificate sono ad uso libero.
Intuitivamente, se in un metodo c’è un’istruzione che potrebbe lanciare un’eccezione verificata, o tale istruzione è contenuta in un blocco try…catch, oppure il metodo deve dichiarare che può lanciare quella eccezione.
Precisamente, se un metodo contiene un’istruzione “throw x”, oppure chiama un metodo la cui intestazione contiene la dichiarazione “throws x”, dove x è un’eccezione verificata, il compilatore controlla che sia rispettata una delle seguenti condizioni:
Naturalmente, delle quattro combinazioni che sono corrette per il compilatore, una non ha senso: che un metodo lanci direttamente un’eccezione con throw e subito la catturi con try…catch.
Le eccezioni servono per segnalare una situazione anomala al chiamante, mentre in questo caso l’eccezione sarebbe usata solo per modificare il flusso di controllo all’interno di un metodo, cosa per la quale esistono apposite istruzioni (in particolare, if-then-else e break)_
Con quale criterio si dovrebbe scegliere tra le due categorie di eccezioni?
In linea di massima, dovrebbero essere di tipo verificato quelle eccezioni che il programmatore non può avere la certezza di evitare e che richiedono un opportuno trattamento.
Ad esempio, si consideri l’eccezione che viene lanciata quando si cerca di aprire un file inesistente (java.io.FileNotFoundException).
Per quanto si sforzi, il programmatore non può raggiungere la certezza che tale eccezione non venga lanciata.
Difatti, anche se con una apposita istruzione si controllasse l’esistenza del file subito di prima di aprirlo, nei moderni sistemi multi-tasking niente può impedire ad un altro programma di cancellare il file in un momento intermedio tra il controllo di esistenza e la sua apertura vera e propria.
Pertanto, l’eccezione FileNotFoundException è verificata.
Come ulteriore esempio, si consideri l’eccezione ArrayOutOfBoundsException, che viene lanciata dalla Java Virtual Machine (JVM) quando si tenta di accedere ad un array con un indice non valido.
Questa eccezione è evitabile da parte del programmatore e difatti indica la presenza di un errore di programmazione.
Inoltre, se tale eccezione fosse verificata, il programmatore dovrebbe dichiararla o trattarla in occasione di ogni accesso ad array.
Pertanto, ArrayOutOfBoundsException non è verificata.
Infine, si consideri l’eccezione OutOfMemoryError, che viene lanciata dalla JVM se il progamma ha richiesto una allocazione di memoria (new), ma non vi è memoria libera a sufficienza.
Anche se tale situazione non è evitabile da parte del programmatore, c’è poco che il programmatore possa fare di conseguenza, tranne terminare il programma, che del resto è il comportamento di default in seguito al lancio di un’eccezione.
Inoltre, come per ArrayIndexOutOfBoundsException, le occasioni per il verificarsi di questa eccezione sono troppo frequenti per obbligare il programmatore a gestirla.
Pertanto, OutOfMemoryError è un’eccezione non verificata.
Per approfondire questo argomento, si veda anche l’articolo “Simple guide to checked exceptions“, di Gabriele Carcassi.
Quali eccezioni della libreria standard sono verificate?
Come si sceglie la categoria di una nuova eccezione?
Tutte le eccezioni della libreria standard sono verificate, tranne quelle che discendono da RuntimeException o da Error.
Quando ci crea una nuova classe di eccezione, tale classe eredita la categoria (verificata o non verificata) dalla sua superclasse.
In ciascuno dei seguenti contesti, dire se è opportuno usare un’eccezione verificata o non verificata.
In un metodo che calcola la radice quadrata, se l’argomento passato è negativo.
In un metodo che interagisce con una stampante, se la stampante ha esaurito la carta.
In un metodo che accetta un oggetto, se l’argomento passato è null.
In un metodo che chiude un file, se quel file non era stato precedentemente aperto.
In un metodo che accede ad un campo istanza privato, se quel campo si trova in uno stato non valido (cioè, contiene un valore non previsto).
4. Risoluzione dell'overloading e dell'overriding
5. Controllo di uguaglianza tra oggetti
6. Classi interne, locali ed anonime
7. Iteratori, teoria e pratica
8. Clonazione di oggetti. Confronto tra oggetti.
9. Elementi di programmazione di interfacce grafiche
10. Il paradigma Model-View-Controller. Il pattern Strategy
11. I pattern Composite e Decorator
12. I pattern Template Method e Factory Method
13. Classi e metodi parametrici
14. La libreria Java Collection Framework: le interfacce Iterable, ...
15. La libreria Java Collection Framework: la classe HashSet e le l...
16. Parametri di tipo con limiti
17. L'implementazione della programmazione generica: la cancellazio...
18. La riflessione
19. Introduzione al multi-threading
22. Classi enumerate