vuoi
o PayPal
tutte le volte che vuoi
La gestione delle eccezioni in Java
Per ogni blocco try
è possibile definire più blocchi catch
, ma ogni blocco catch
può gestire un solo tipo di eccezione. L'identificativo parametro_del_blocco_catch
serve come segnaposto per un'eccezione che potrebbe essere lanciata. Quando nel blocco try
precedente viene generata un'eccezione della classe nome_della_classe_eccezione
, questa eccezione viene assegnata a parametro_del_blocco_catch
. Il codice nel blocco catch
può utilizzare parametro_del_blocco_catch
per gestire l'eccezione. Una scelta ampiamente diffusa per l'identificativo di parametro_del_blocco_catch
è e
, ma è possibile utilizzare ogni altro identificativo valido.
Il metodo getMessage
: Ogni oggetto eccezione ha una variabile di istanza di tipo String
che contiene un messaggio. Tale messaggio solitamente identifica la ragione per cui è stata generata l'eccezione. Per esempio, se l'eccezione è lanciata dalla seguente istruzione: throw
new Exception(argomento_di_tipo_stringa);
Il valore della variabile di istanza di tipo String è argomento di tipo string. Se l'oggetto eccezione viene chiamato e, l'invocazione di e.getMessage() restituisce questa stringa.
Classi di eccezione definite dal programmatore:
Vi è la possibilità di definire nuove classi di eccezioni, a patto di derivarle da una classe di eccezione esistente, sia essa predefinita o definita dal programmatore.
Linee Guida:
- Si utilizzi Exception come base, se non vi è una particolare esigenza che porta a scegliere come classe base un'altra classe.
- Si definiscano almeno due costruttori che includono un costruttore di default e uno con un solo parametro di tipo String.
- Si dovrebbe iniziare ogni definizione di costruttore con una chiamata al costruttore della classe base, utilizzando super. Nel costruttore di default, la chiamata a super deve avere un argomento di tipo stringa che indichi il tipo di eccezione rappresentata.
Throw vs Throws:
La parola chiave throw è utilizzata per lanciare un'eccezione, mentre throws è utilizzata nell'intestazione del metodo per dichiarare un'eccezione. Quindi, un'istruzione throw lancia un'eccezione e una clausola throws ne dichiara una.
Tipologie di eccezioni:
Ogni eccezione è discendente della classe Exception. RunTimeException è una classe derivata da Exception e le classi a loro volta derivate da RunTimeException o dalle sue discendenti rappresentano eccezioni non controllate. Tali eccezioni non richiedono di essere dichiarate in una clausola throws nell'intestazione di un metodo. Tutte le altre eccezioni sono controllate e devono essere dichiarate in una clausola throws.
Checked exception: sono dovute a circostanze esterne che il programmatore non
Può prevenire e il compilatore verifica che il programma le gestisca. Esempi tipici si verificano con le operazioni di input e output.
Un-checked exception: sottoclassi di RunTimeException che derivano da problemi di programmazione.
Ereditarietà: L'ereditarietà è uno dei concetti base della programmazione object oriented. Permette di definire una classe base più generale e di definire in seguito classi specializzate che aggiungono nuovi dettagli alla classe generale.
Vantaggi: permette di risparmiare molto lavoro, e quindi tempo, perché la classe specializzata eredita tutte le proprietà della classe generale e il programmatore deve solo realizzare le nuove caratteristiche.
Esempio: Si definirà una semplice classe chiamata Persona. La classe Persona non ha molta utilità di per sé, ma sarà utilizzata per definire nuove classi. In Java, è possibile definire una classe chiamata Persona che include la
definizione di tutte quelle variabili di istanza che rappresentano le proprietà comuni a tutte le sottoclassi di persone. La definizione della classe può inoltre contenere tutti i metodi che Person gestiscono le variabili di istanza definite nella classe Persona. Uno Studente è una persona e pertanto si può definire la classe studente come una classe derivata, o sottoclasse, della classe Persona. La classe Dipendente Studente Persona è definita classe base, o superclasse. La classe Studente e Dipendente ereditano le variabili di istanza e i metodi pubblici della classe base che estende. Una classe derivata può inoltre aggiungere altre variabili di istanza e nuovi metodi oltre quelli ereditati dalla sua classe base. N.B. Date due classi se non sussiste la relazione is-a tra di esse, non si usa l’ereditarietà per derivare una classe dall’altra. Una classe derivata include automaticamente tutte le variabili di istanza, le
variabilistatiche e tutti i metodi pubblici della classe base. I membri ottenuti dalla classe base si dicono ereditati. In una classe base è possibile modificare un metodo ereditato- Overriding: Se una classe derivata definisce un metodo che ha lo stesso nome, gli stessi parametri e anche lo stesso tipo di ritorno di un metodo della classe base, il metodo della classe derivata ridefinisce il metodo presente nella classe base. Quando si ridefinisce un metodo ereditato dalla classe base, in generale non è possibile modificare il tipo di ritorno. L'eccezione a questa regola è la seguente: se il tipo di ritorno è una classe, il metodo ridefinito può restituire una qualsiasi delle sue classi derivate. Il tipo di ritorno così modificato prende il nome di tipo di ritorno covariante.- Overriding vs Overloading: Non si devono confondere i due termini. Quando si effettua l'overriding della definizione di un metodo, la nuova definizione del metodo
- HashTable: è una struttura dati dinamica che consente di immagazzinare e prelevare dati dalla memoria in maniera efficiente. Una tabella di hash può essere costruita in diversi modi, l'implementazione più comune si basa sull'utilizzo di un array e di una lista concatenata ordinaria. La ricerca di un elemento all'interno della tabella di hash avviene tramite una funzione di hash che mappa la chiave dell'elemento all'indice dell'array corrispondente.
La lista richiede in genere un tempo proporzionale alla lunghezza della lista. Una tabella di hash può, invece, eseguire la ricerca in un numero fissato di operazioni, indipendentemente dalla quantità di dati in essa contenuti.
Per immagazzinare un elemento in una tabella di hash gli si assegna una chiave. Nota: la chiave è possibile recuperare l'elemento. La chiave individua univocamente l'elemento associato. Se un elemento non fornisce intrinsecamente una chiave, si può utilizzare una funzione di hash per generarne una. Una funzione di hash produce un numero.
Si costruirà una tabella di hash mediante un singolo array (arrayHash) di lunghezza fissa nel quale ogni elemento referenzia una lista concatenata. All'inizio, ogni elemento dell'array arrayHash contiene un riferimento ad una lista concatenata vuota. Gli elementi all'interno della tabella di hash vengono inseriti all'indice dell'array corrispondente al valore di
Hashing è una tecnica utilizzata per l'accesso efficiente ai dati in una struttura dati chiamata tabella di hash. Una tabella di hash è un array di elementi, dove ogni elemento è associato a un indice calcolato tramite una funzione di hash.
La funzione di hash prende in input un elemento e restituisce un valore numerico, chiamato hash, che rappresenta l'indice in cui l'elemento sarà memorizzato nella tabella di hash. L'obiettivo è quello di minimizzare le collisioni, ovvero quando due elementi hanno lo stesso hash.
Se si verifica una collisione, si utilizza una tecnica chiamata concatenazione. Questo significa che si aggiunge semplicemente un nuovo nodo alla lista concatenata già presente nella posizione dell'array corrispondente all'hash.
Per cercare un elemento in una tabella di hash, si calcola il suo valore di hash e si cerca l'elemento nella lista concatenata presente nella posizione dell'array corrispondente all'hash ottenuto, utilizzando una ricerca sequenziale.
L'efficienza di una tabella di hash dipende da vari fattori. Se tutti gli elementi inseriti vengono associati alla stessa chiave, si avranno le prestazioni peggiori. Al contrario, se ogni elemento viene associato a una chiave diversa, si avranno le prestazioni migliori, senza collisioni. È possibile ridurre la possibilità di collisioni utilizzando funzioni di hash efficienti e dimensionando correttamente la tabella di hash.
utilizzando una funzione di hash migliore oppure aumentando la dimensione della tabella di hash.
Hashing: tecnica di memorizzazione che consente di identificare velocemente un oggetto in una struttura, senza necessità di visite sequenziali.
Problemi: Array virtualmente infinito e non vengono gestite le collisioni. Quindi bisogna lavorare con una dimensione ragionevole. È necessario usare il modulo per identificare l'indice all'interno dell'array.
Ciascuna collisione viene gestita attraverso una lista di nodi per oggetti multipli (buckets).
Interfacce: Un'interfaccia Java è un componente di un programma che contiene le intestazioni di un certo numero di metodi. Può anche definire delle costanti pubbliche. Inoltre, potrebbe includere i commenti che descrivono i metodi. Un'interfaccia Java inizia come una definizione di classe, tranne per il fatto che utilizza la parola riservata interface al posto di class. Può contenere un numero
qualsiasi diintestazioni di metodi pubblici.
Implementare un’interfaccia:
Quando si scrive una classe che definisce i metodi dichiarati in un’interfaccia, si dice che la classe implementa l’interfaccia. Una classe che implementa un’interfaccia deve definire un corpo per ogni metodo specificato nell’interfaccia. Se non definisce il corpo di tutti i metodi, deve essere dichiarata astratta. La classe potrebbe anche definire metodi non dichiarati nell’interfaccia. Un’interfaccia non deve necessariamente dichiarare ogni metodo definito in una classe. Una classe può implementare più di un’interfaccia.
A cosa servono?
Scrivere un’interfaccia è un modo con cui il progettista di una classe specifica i metodi a un altro programmatore. Implementare un’interfaccia è un modo per un programmatore per garantire che una classe definisca determinati metodi.
Il binding dinamico si applica alle interfacce esattamente come alle classi.
Ciò consente l'intercambiabilità di oggetti di classi diverse, purché implementino la stessa interfaccia. Questa funzionalità è molto utile per favorire la modularità e la flessibilità del codice.