Gestione degli errori in Java
Gerarchia delle eccezioni
R: Sì. In Java c'è una gerarchia di classi per gestire gli errori, la cui radice è Throwable (java.lang), che ha due sottoclassi:
- Exception: per gestire errori riparabili (tipo 1 o 2);
- Error: per errori fatali (tipo 3).
La classe Exception ha svariate sottoclassi che permettono di gestire le eccezioni, cioè situazioni anomale possibili quando il programma è in esecuzione.
Gestione delle eccezioni
D: Cosa succede quando un metodo lancia un'eccezione?
R: Quando un metodo lancia un'eccezione, esso crea un oggetto di tipo eccezione e ne delega la gestione a un gestore che cattura ed esegue alcune istruzioni: questo meccanismo è detto delegation model.
Posizione del gestore dell'eccezione
D: Dove può trovarsi il gestore dell'eccezione?
R: Il gestore dell'eccezione può trovarsi o nel metodo che ha lanciato l'eccezione, oppure in un metodo che ha invocato il metodo che ha lanciato l'eccezione. Nel primo caso si parla di cattura dell'eccezione, nel secondo caso si parla di propagazione dell'eccezione.
Quindi -> Un metodo può sollevare o catturare l'eccezione: se la solleva allora il gestore si troverà in un altro metodo che invoca quello sollevante l'eccezione, se la cattura deve gestirla opportunamente. In caso di propagazione, il prototipo del metodo deve essere seguito dalla clausola 'throws NomeEccezione'.
Categorie delle eccezioni
D: Quanti e quali sono le categorie delle eccezioni?
R: Esistono 2 tipi di eccezione:
- Non controllate dal compilatore (unchecked);
- Controllate dal compilatore (checked).
Le prime sono dovute a circostanze evitabili con una correzione del codice da parte del programmatore, le altre sono situazioni inevitabili, come ad esempio 'file non trovato'.
Nel caso di eccezioni controllate la clausola 'throws NomeEccezione' è obbligatoria; nel caso di quelle non controllate, non è obbligatoria, ma è consigliata.
Esempi di eccezioni non controllate: ArithmeticException, ClassCastException, ArrayIndexOutOfBoundsException...
Esempi di eccezioni controllate: IOException, FileNotFoundException, ClassNotFoundException...
Esempi di gestione delle eccezioni
Esempio:
public static void main(String[] args) throws FileNotFoundException {
FileReader f = new FileReader("Pippo.txt");
}
Se manca il file genera un’eccezione a runtime: La clausola throws qui è necessaria perché FileNotFoundException è di tipo controllato e il metodo non contiene un gestore dell’eccezione.
public static void main(String[] args) {
int[] ar = new int[5];
System.out.println(ar[5]);
}
Genera un’eccezione a runtime: Il metodo non contiene un gestore dell’eccezione, quindi la propaga all’esterno, tuttavia la clausola throws qui non è necessaria perché ArrayIndexOutOfBoundsException è di tipo non controllato.
Lancio di eccezioni
Dopo la clausola throws bisogna far seguire almeno la lista delle classi di eccezioni che il metodo in questione potrebbe lanciare. Ogni nome di classe d'eccezione deve essere seguito da ','.
ESEMPIO:
public static void main(String[] args) throws ArrayIndexOutOfBoundsException, IOException {
FileReader f = new FileReader("Pippo.txt");
int[] ar = new int[5];
System.out.println(ar[5]);
}
Lancio manuale delle eccezioni
D: Possiamo lanciare manualmente qualunque tipo di eccezione?
R: Sì, è possibile lanciare eccezioni manualmente invocando un costruttore di una sottoclasse di Exception tramite la clausola throw. In questo caso il metodo termina e il controllo passa al gestore dell'eccezione.
ESEMPIO: Metodo per dividere due interi.
public static int dividi(int a, int b) {
if(b == 0) throw new ArithmeticException();
else return a / b;
}
Ogni altra istruzione dopo la clausola throw genererà un errore in compilazione.
Costruttori e metodi di Exception
D: Quanti e quali costruttori ha Exception?
R: Ha 2 costruttori: uno senza parametri e uno con parametro String, che rappresenta il messaggio di errore associato.
D: Quali metodi sono definiti in tutte le sottoclassi di Exception?
R: Il metodo getMessage() che, ereditato da Throwable, restituisce il messaggio di errore incapsulato e un metodo printStackTrace() che stampa lo stack delle chiamate ai metodi dove si verifica l'eccezione. Quest'ultimo può essere invocato, oltre che senza parametri, con parametro di tipo PrintStream o PrintWriter, ed è utile per il debugging.
Eccezioni personalizzate
D: Posso definire eccezioni personalizzate?
R: Sì, basta estendere alla propria classe eccezione, l'eccezione più adeguata, e definire i due costruttori usando al loro interno la parola chiave super() come richiamo al costruttore della superclasse che abbiamo ereditato. La classe creata sarà controllata se abbiamo esteso una classe di eccezione di tipo controllato.
Esempio:
public class SaldoInsufficienteException extends IllegalArgumentException {
public SaldoInsufficienteException() {
super("Saldo non sufficiente!");
}
public SaldoInsufficienteException(String msg) {
super(msg);
}
}
Gestione e propagazione delle eccezioni
D: Come funzionano la gestione e la propagazione delle eccezioni?
R: Nello stack delle chiamate ai metodi si risale partendo dal metodo che ha lanciato l'eccezione, fino al metodo che lo invoca. Se anche il main (ultimo metodo dello stack), propaga l'eccezione, essa viene catturata dal System Exception Handler della JVM, che interrompe l'esecuzione del programma dando un messaggio di errore a runtime.
ESEMPIO
public class Test {
public static void metodo1(File f) throws FileNotFoundException {
if (!(f.exists())) throw new FileNotFoundException();
}
public static void metodo2(File f) throws FileNotFoundException {
metodo1(f);
}
public static void metodo3(File f) throws FileNotFoundException {
metodo2(f);
}
public static void main(String[] args) throws FileNotFoundException {
metodo3(new File("abc.txt"));
}
}
Exception in thread "main" java.io.FileNotFoundException at Test.metodo1(Test.java:6) at Test.metodo2(Test.java:9) at Test.metodo3(Test.java:12) at Test.main(Test.java:15). I metodi vengono impilati per come riportato nella descrizione dell'eccezione, poi lo stack si va svuotando, quindi metodo1 propaga l'eccezione a metodo2 che la propaga a metodo3 che la propaga al main che la propaga alla S.E.H. che interrompe il programma e genera l'eccezione sopra descritta.
Installazione del gestore delle eccezioni
D: Come si gestisce un'eccezione?
R: Per catturare un'eccezione occorre installare un gestore delle eccezioni tramite il blocco try/catch.
Nel blocco try vanno messe tutte le istruzioni suscettibili di lanciare eccezioni, e nel/nei blocco/blocchi catch vanno catturate tutte le eccezioni eventualmente lanciate dalle istruzioni presenti nel blocco try. Se nel try un'istruzione lancia un'eccezione, il controllo va subito nel blocco catch relativo a quell'eccezione.
Esempio:
try {
String nomefile = args[0]; // potrebbe lanciare IndexOutOfBoundsException
Scanner in = new Scanner(new File(nomefile)); // potrebbe lanciare FileNotFoundException
} catch (IndexOutOfBoundsException ex) {
System.out.println("Manca " + nomefile);
} catch (FileNotFoundException ex) {
System.out.println("File non trovato");
}
Grazie al subtyping, un catch cattura anche istanze di sottoclassi della classe dell’oggetto passato a parametro, quindi se nel blocco try vi sono più istruzioni dove una genera un'eccezione che è sottoclasse di un'altra eccezione generata da un'altra istruzione, e se sono presenti due blocchi catch di cui il primo cattura l'eccezione superclasse e l'altro cattura l'eccezione sottoclasse, il secondo non sarà mai eseguito perché, per subtyping, viene eseguito dal primo blocco catch.
Esempio di subtyping con eccezioni
try {
String nomefile = args[0]; // potrebbe lanciare IndexOutOfBoundsException
double d = 7/0; // lancia ArithmeticException
} catch (RuntimeException ex) {
// cattura IndexOutOfBoundsException o ArithmeticException
} catch (ArithmeticException ex) {
// non verrà mai eseguito perché un’eccezione di tipo
// ArithmeticException verrà sempre catturata dal catch precedente
}
Blocco try/catch e istruzioni senza eccezioni
D: E se nel try mettessi istruzioni che non generano eccezioni?
R: Si avrebbe quest'errore in compilazione: error: exception <nomeEccezione> is never thrown in body of corresponding try statement
Blocco finally
D: Esiste un terzo blocco dopo try/catch?
R: Sì. Il blocco finally, le cui istruzioni, a prescindere dall'esito delle istruzioni suscettibili di lanciare eccezioni presenti nel try, verranno comunque eseguite. Inserire il blocco finally di solito non è obbligatorio, ma lo diventa quando non si definisce nessun blocco catch.
Posizione del gestore try/catch
D: Dove posso piazzare il gestore try/catch?
R: Esso può essere inserito all'interno di un metodo invocante altri metodi suscettibili di propagare eccezioni. L’eccezione viene dunque lanciata (o sollevata) dal metodo chiamato, il quale la propaga, e poi viene catturata, e quindi gestita, dal metodo chiamante.
Documentazione delle eccezioni con Javadoc
D: Se creo un metodo suscettibile di lanciare eccezioni, cosa devo specificare nella Javadoc?
R: Nella documentazione della classe vanno specificate tutte le eccezioni che i metodi potrebbero propagare, usando l’annotazione @throws.
Esempio:
@throws ArithmeticException if b is equal to zero;
public int dividi(int a, int b) {
// istruzioni
}
Programmazione generica in Java
D: Esiste, in Java, la programmazione generica?
R: Sì. Java supporta la programmazione generica, grazie all'uso dei Generics, ossia permette di parametrizzare classi e oggetti assegnando un generico tipo T, ovvero un tipo di una classe qualsiasi, tranne che primitivo (per lavorare con i tipi primitivi si usano le classi wrapper). Tra i vantaggi dell'uso dei Generics, abbiamo che il compilatore può verificare qualsiasi operazione verificando la compatibilità dei tipi e, quindi, non vi è necessità di eseguire un casting.
Tipo parametrico e costruttori
D: È necessario ripetere il tipo parametrico quando si invoca il costruttore?
R: A partire da Java 7, non è più necessario scrivere il tipo parametrico all'invocazione del costruttore, però se si omettono anche le parentesi uncinate '<>' si ottiene il seguente warning: uses unchecked or unsafe operations.
Esempio:
ArrayList<Integer> a = new ArrayList<Integer>();
o scrivere
ArrayList<Integer> a = new ArrayList<>();
è la stessa cosa. Se invece si scrive ArrayList<Integer> a = new ArrayList() viene generato, in compilazione, quel warning per il mancato uso dei Generics.
Metodi generici
D: Si possono scrivere metodi generici?
R: Sì. Si possono scrivere metodi generici e sia i parametri che il valore di ritorno saranno generici. Per il valore di ritorno bisogna mettere il tipo generico prima del valore di ritorno.
ESEMPIO:
public static <T> ArrayList<T> metodo(ArrayList<T> a)
Metodo che prende a parametro un ArrayList di tipi T generici e restituisce qualcosa di tipo ArrayList di tipi T.
Relazione di ereditarietà tra classi parametriche
D: Avendo due tipi di classi parametriche X<S> e X<K>, c'è relazione di ereditarietà tra S e K?
R: No. Ad esempio, se avessimo le classi Persona e Studente, e volessimo istanziare un ArrayList<Studente>, scrivere:
ArrayList<Persona> a = new ArrayList<Studente>()
è errato perché non c'è relazione di ereditarietà tra ArrayList<Persona> e ArrayList<Studente>.
Gestione del subtyping con i Generics
D: Quindi come si gestisce il subtyping con i Generics?
R: Per far ciò entrano in campo le wildcard. Possiamo parametrizzare sia una classe che un metodo con le wildcard, oltre che col tipo generico.
Esistono due tipi di wildcard:
- Unbounded: X<?> riferimento dove ? è un tipo qualsiasi non fissato
- Bounded:
- Superiormente: X<? extends T>
- Inferiormente: X<? super T>
dove '? extends T' indica T o un suo sottotipo, e '? super T' indica T o un suo supertipo
Esempi:
ArrayList<?> a = new ArrayList<String>(); ArrayList<? extends Persona> a = new ArrayList<Studente>(); ArrayList<? super Gatto> a = new ArrayList<Mammifero>();
Implementazione di interfacce
D: Come implementiamo una o più interfacce?
R: Per implementare un'interfaccia, in questo caso, dobbiamo far seguire al tipo parametrico la parola chiave 'extends' seguita dal nome dell'interfaccia.
Esempio:
public static <T extends Comparable<? super T>> T metodo()
L'esempio indica un metodo static che implementa l'interfaccia Comparable di un qualunque supertipo di T, con il tipo T incluso.
Nel caso di implementazione di più interfacce o estensione di classe e interfaccia/e, basta aggiungere il carattere & tra i nomi delle interfacce o tra il nome classe e l'interfaccia (o le interfacce).
Esempio:
public static <T extends Object & Comparable<? super T>> T metodo()
Strutture dati astratte (SDA)
D: Cosa è una S.D.A. (Struttura Dati Astratta)?
R: Una SDA è una struttura definita soltanto in base alle sue proprietà e alle operazioni su di essa fattibili, cioè è definita in base al 'cosa fa'.
Strutture dati complete (SDC)
D: Cosa è una struttura dati completa?
R: Una SDC è un'implementazione di una SDA e fornisce i metodi per realizzare le operazioni, quindi il 'come fare' queste operazioni. Esempi di implementazione della SDA 'array' sono Array e ArrayList. Una corretta implementazione di una SDA non dovrebbe far sì che si possano compiere operazioni che violano il concetto stesso di Struttura Dati Astratta.
Esempi di SDA
D: Quali sono alcuni esempi di SDA?
R: Alcuni esempi di SDA sono: array, matrice, insieme, stringa, lista, pila, coda, albero, grafo, mappa e heap, ma ne esistono anche altre.
Implementazione di una SDA
D: Come può essere implementata quindi una SDA?
R: Una SDA può essere implementata con diverse strutture dati concrete (ad esempio Array e ArrayList implementano array) e, viceversa, una SDC può implementare diverse SDA (ad esempio Array può essere usato per implementare array, matrici, stringhe,...).
Modellizzazione di una SDA in Java
D: Come si modellizza in Java una SDA?
R: In Java, una SDA viene modellizzata attraverso un'interfaccia, infatti il concetto stesso di interfaccia è quello di definire cosa fare, invece, una classe che implementa l'interfaccia definisce come farlo.
Java Collection Framework (JCF)
D: Cosa è il Java Collection Framework (JCF)?
R: Il JCF è una gerarchia costituita da interfacce per modellizzare collezioni di oggetti e da classi implementanti queste interfacce.
Interfaccia Collection
D: Cosa fa l'interfaccia Collection?
R: L'interfaccia Collection modellizza una collezione generica astratta. I suoi metodi si dividono in:
- BASIC: operazioni di base, come inserimento, cancellazione e ricerca di un elemento;
- BULK: operazioni su intere collezioni, come inserimento, cancellazione e ricerca di collezioni;
- ARRAY: operazioni per trasformare una collezione in un array;
- OPTIONAL: operazioni che, se non supportate da un'implementazione, lanciano l'eccezione UnsupportedOperationException, sottoclasse di RuntimeException.
Tra i metodi di Collection vi sono:
- int size() che restituisce la dimensione della collezione;
- boolean isEmpty() ritorna 'true' se la collezione è vuota;
- boolean add(E element) aggiunge l'emento in coda;
- boolean remove(Object element) rimuove l'oggetto della collezione;
- boolean contains(Object element) ritorna 'true' se vi è l'oggetto nella collezione;
- Iterator<E> iterator() crea un iteratore per la collezione;
- void clear() cancella tutti gli elementi della collezione;
- Object[] toArray() riversa la collezione su un array di Object;
- <T> T[] toArray(T[] a) riversa la collezione su un array di tipo dato;
Interfaccia Set
D: Cosa è cosa fa l'interfaccia Set?
R: L'interfaccia Set è una sottointerfaccia di Collection che modellizza un insieme, ovvero una collezione senza duplicati. I metodi di Set realizzano le operazioni insiemistiche di unione, intersezione, differenza e sottoinsieme:
- boolean addAll(Collection<? extends E> c) unisce con un insieme dato;
- boolean containsAll(Collection<?> c) ritorna 'true' se l'insieme contiene un insieme dato;
- boolean removeAll(Collection<?> c) fa la differenza con un insieme dato;
- boolean retainAll(Collection<?> c) fa l'intersezione con un insieme dato.
Classe HashSet
D: Cosa è e cosa fa la classe HashSet?
R: La classe HashSet è un'implementazione dell'interfaccia Set basata su una tabella hash. Il metodo add ritorna false se si prova a inserire un elemento già esistente nella collezione, quindi, in questo caso, non fa nessuna operazione. Inoltre, essendo un HashSet un insieme, quindi non indicizzato, non possiamo usare un for classico, ma possiamo usare un for esteso o un iteratore. L'ordine di scansione potrebbe essere diverso da quello di inserimento.
I metodi add, remove e contains sono operazioni molto veloci, cioè richiedono tempo costante rispetto alla taglia dell'insieme. Ogni HashSet ha una sua 'size', cioè il numero degli elementi presenti nella tabella, e una sua 'capacity', cioè lo spazio allocato per contenere gli elementi della tabella.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
-
Riepilogo JAVA Prima Parte
-
Riepilogo Esame di Calcolatori Elettronici - Architetture
-
Riepilogo Bilanci
-
Riepilogo su Machiavelli