12 - Gestione delle Eccezioni
Impara a gestire gli errori con eleganza nei tuoi programmi Java.
Le eccezioni sono errori che si verificano mentre il programma è in esecuzione. Invece di andare in crash, puoi “catturare” questi errori e gestirli con eleganza.
Che cos'è un'eccezione?
Un'eccezione è un evento che interrompe il normale flusso del programma:
String text = null;
System.out.println(text.length()); // NullPointerException - crash!Senza gestione, questo causa il crash della mod e interrompe il gioco!
Blocchi Try-Catch
Usa try-catch per gestire le eccezioni:
try {
// Codice che potrebbe lanciare un'eccezione
String text = null;
System.out.println(text.length());
} catch (NullPointerException e) {
// Codice per gestire l'errore
System.out.println("Errore: testo nullo!");
}
System.out.println("Il programma continua...")- Il codice nel blocco
tryviene eseguito normalmente - Se si verifica un'eccezione, l'esecuzione salta al blocco
catch - Dopo il blocco
catch, il programma continua normalmente - Se non si verifica alcuna eccezione, il blocco
catchè saltato
try {
// Prova a fare qualcosa di rischioso
int result = 10 / 0; // Divisione per zero!
} catch (ArithmeticException e) {
// Gestisci l'errore
System.out.println("Impossibile dividere per zero!");
}
// Il programma continua a funzionareTipi Di Eccezioni Comuni
NullPointerException
Accesso a metodi/proprietà in oggetti nulli:
String name = null;
try {
int length = name.length();
} catch (NullPointerException e) {
System.out.println("Il nome e' nullo!");
}ArrayIndexOutOfBoundsException
Accesso ad una posizione dell'array non valida:
nt[] numbers = {1, 2, 3};
try {
int value = numbers[10]; // la posizione 10 non esiste!
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Posizione nell'array non valida!");
}NumberFormatException
Conversione di stringhe non valide in numeri:
try {
int number = Integer.parseInt("abc"); // Non un numero!
} catch (NumberFormatException e) {
System.out.println("Formato numerico non valido!");
}ArithmeticException
Errori matematici come divisione per zero:
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("Errore matematico!");
}Blocchi Catch Multipli
Gestisce diverse eccezioni in modo diverso:
String input = "abc";
try {
int number = Integer.parseInt(input);
int result = 100 / number;
System.out.println(result);
} catch (NumberFormatException e) {
System.out.println("Numero non valido!");
} catch (ArithmeticException e) {
System.out.println("Impossibile dividere per zero!");
}Cattura di Più Tipi di Eccezione
Cattura più eccezioni in un blocco:
try {
// Del codice rischioso
} catch (NumberFormatException | ArithmeticException e) {
System.out.println("Si e' veificato un errore relativo a Math!");
}Il Blocco Finally
Codice che sempre viene eseguito, indipendentemente dal fatto che si verifichi o meno:
try {
System.out.println("Opening file...");
// Codice che potrebbe fallire
} catch (Exception e) {
System.out.println("Errore: " + e.getMessage());
} finally {
System.out.println("Chiusura file...");
// Questa linea viene SEMPRE eseguita - ottimo per la pulizia delle risorse
}Usa finally per operazioni di pulizia che devono sempre accadere:
- Chiusura dei file
- Rilascio delle risorse
- Salvataggio dati
- Registro log
FileReader file = null;
try {
file = new FileReader("data.txt");
// Read file
} catch (Exception e) {
System.out.println("Errore leggendo il file");
} finally {
if (file != null) {
file.close(); // Sempre chiudere il file!
}
}Ottenere Informazioni Sull'Eccezione
L'oggetto dell'eccezione contiene informazioni utili:
try {
int result = Integer.parseInt("xyz");
} catch (NumberFormatException e) {
System.out.println("Messaggio: " + e.getMessage());
System.out.println("Tipo: " + e.getClass().getName());
e.printStackTrace(); // Stampa tutti i dettagli dell'errore
}Esempi Pratici
Input Sicuro Del Giocatore
import java.util.Scanner;
public class PlayerInput {
public static int getPlayerChoice(Scanner scanner) {
while (true) {
try {
System.out.print("Inserisci scelta (1-5): ");
String input = scanner.nextLine();
int choice = Integer.parseInt(input);
if (choice < 1 || choice > 5) {
System.out.println("Per favore inserisci un numero compreso tra 1 e 5");
continue;
}
return choice;
} catch (NumberFormatException e) {
System.out.println("Input non valido! Per favore inserisci un numero.");
}
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int choice = getPlayerChoice(scanner);
System.out.println("La tua scelta: " + choice);
}
}Aggiornamento della durabilità degli item in maniera sicura
public class Item {
private String name;
private int durability;
private int maxDurability;
public Item(String name, int maxDurability) {
this.name = name;
this.durability = maxDurability;
this.maxDurability = maxDurability;
}
public void damage(int amount) {
try {
if (amount < 0) {
throw new IllegalArgumentException("Il danno non può essere negativo!");
}
durability -= amount;
if (durability < 0) {
durability = 0;
}
System.out.println(name + " durabilità: " + durability + "/" + maxDurability);
if (durability == 0) {
System.out.println(name + " rotto!");
}
} catch (IllegalArgumentException e) {
System.out.println("Errore: " + e.getMessage());
}
}
}Caricamento Configurazione Sicuro
import java.util.HashMap;
public class ConfigLoader {
private HashMap<String, String> settings;
public ConfigLoader() {
this.settings = new HashMap<>();
loadDefaults();
}
private void loadDefaults() {
settings.put("maxPlayers", "10");
settings.put("difficulty", "normal");
settings.put("pvpEnabled", "true");
}
public int getIntSetting(String key, int defaultValue) {
try {
String value = settings.get(key);
if (value == null) {
return defaultValue;
}
return Integer.parseInt(value);
} catch (NumberFormatException e) {
System.out.println("Numero non valido per " + key + ", utilizzando il valore predefinito");
return defaultValue;
}
}
public boolean getBooleanSetting(String key, boolean defaultValue) {
try {
String value = settings.get(key);
if (value == null) {
return defaultValue;
}
return Boolean.parseBoolean(value);
} catch (Exception e) {
System.out.println("Valore booleano non valido " + key + ", utilizzando il valore predefinito"");
return defaultValue;
}
}
public void setSetting(String key, String value) {
if (key == null || value == null) {
System.out.println("Key e Value non possono essere nulli!");
return;
}
settings.put(key, value);
}
}Accesso all'Array Sicuro
public class Inventory {
private String[] items;
public Inventory(int size) {
this.items = new String[size];
}
public boolean setItem(int slot, String item) {
try {
if (slot < 0 || slot >= items.length) {
throw new ArrayIndexOutOfBoundsException("Slot non valido: " + slot);
}
items[slot] = item;
System.out.println("Piazzato " + item + " nello slot " + slot);
return true;
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Errore: " + e.getMessage());
return false;
}
}
public String getItem(int slot) {
try {
return items[slot];
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Slot non valido: " + slot);
return null;
}
}
}Analisi dei comandi con gestione degli errori
public class CommandParser {
public static void parseCommand(String command) {
try {
if (command == null || command.trim().isEmpty()) {
throw new IllegalArgumentException("Il comando non puo' essere vuoto!");
}
String[] parts = command.split(" ");
if (parts.length < 2) {
throw new IllegalArgumentException("Formato comando non valido!");
}
String action = parts[0];
String target = parts[1];
switch (action) {
case "give":
if (parts.length < 4) {
throw new IllegalArgumentException("Utilizzo: give <player> <item> <amount>");
}
String item = parts[2];
int amount = Integer.parseInt(parts[3]);
if (amount <= 0) {
throw new IllegalArgumentException("L'ammontare deve essere positivo!");
}
System.out.println("Dando " + amount + " " + item + " a " + target);
break;
case "tp":
if (parts.length < 5) {
throw new IllegalArgumentException("Utilizzo: tp <player> <x> <y> <z>");
}
int x = Integer.parseInt(parts[2]);
int y = Integer.parseInt(parts[3]);
int z = Integer.parseInt(parts[4]);
System.out.println("Teletrasportando " + target + " a " + x + ", " + y + ", " + z);
break;
default:
throw new IllegalArgumentException("Comando sconosciuto: " + action);
}
} catch (NumberFormatException e) {
System.out.println("Errore: Numero non valido nel comando!");
} catch (IllegalArgumentException e) {
System.out.println("Errore: " + e.getMessage());
} catch (Exception e) {
System.out.println("Errore imprevisto: " + e.getMessage());
}
}
public static void main(String[] args) {
parseCommand("give Steve diamond 5");
parseCommand("give Steve sword abc"); // Numero non valido
parseCommand("tp Alice 10 64 20");
parseCommand("comando sconosciuto"); // Comando sconosciuto
}
}Lanciare eccezzioni
È possibile lanciare le proprie eccezioni:
public class Player {
private int health;
public void setHealth(int health) {
if (health < 0) {
throw new IllegalArgumentException("La salute non puo' essere negativa!");
}
if (health > 100) {
throw new IllegalArgumentException("La salute non puo' superare 100!");
}
this.health = health;
}
}Lancia delle eccezioni quando:
- Il Metodo riceve input non valido
- L'operazione non può essere completata
- Non sono soddisfatte le condizioni preliminari
- Qualcosa di inaspettato accade
public void damagePlayer(int damage) {
if (damage < 0) {
throw new IllegalArgumentException("Il danno deve essere positivo!");
}
// Applica danno
}
public Item getItem(int slot) {
if (slot < 0 || slot >= inventory.length) {
throw new IndexOutOfBoundsException("Slot dell'inventario non valido!");
}
return inventory[slot];
}Checked vs Unchecked Exceptions
Eccezioni Unchecked (RuntimeException):
- Non devono essere catturate
- Normalmente errori di programmazione
- Esempi: NullPointerException, ArrayIndexOutOfBoundsException
Eccezioni Checked:
- Devono essere catturate o dichiarate
- Normalmente errori esterni (file, rete)
- Esempi: IOException, FileNotFoundException
// Unchecked - non serve catturarla
int result = 10 / 0; // ArithmeticException
// Checked - categorico dichiarare e catturare
try {
FileReader file = new FileReader("data.txt"); // IOException
} catch (IOException e) {
// Handle error
}Esercizi Pratici
-
Calcolatore di Divisione Sicura: Crea un metodo che divide due numeri. Gestisci con eleganza la divisione per zero e gli input non validi.
-
Setter per il Livello del Giocatore: Crea un metodo che imposta il livello del giocatore (1-100). Lancia un'eccezione se non è valido e gestisci l'input in generale.
-
Accesso sicuro all'array: crea un metodo che acceda in modo sicuro agli elementi dell'array. Restituisce null se la posizione non è valida invece di crashare.
-
Parser della configurazione: legge una stringa di configurazione come “maxPlayers=10” e la analizza. Gestisci i formati non validi con eleganza.
-
Convalidatore dei nomi di file: crea un metodo che verifica se un nome file è valido (senza caratteri speciali, non vuoto). Lancia eccezioni per i nomi non validi.