Java でのエラー処理
La 例外処理 書くのに必要不可欠です 堅牢なコード。 Java は盗聴のための構造化されたメカニズムを提供します アプリケーションをクラッシュさせることなくランタイム エラーを処理します。
何を学ぶか
- Java の例外階層
- トライ・キャッチ・最後に
- チェックされた例外とチェックされていない例外
- 投げて投げる
- カスタム例外を作成する
- リソースを試す
- エラー管理のベスト プラクティス
例外の階層
Throwable
/ \
Error Exception
| |
VirtualMachineError IOException (checked)
OutOfMemoryError SQLException (checked)
StackOverflowError RuntimeException (unchecked)
|
NullPointerException
IllegalArgumentException
IndexOutOfBoundsException
例外の種類
| タイプ | 説明 | 管理 |
|---|---|---|
| エラー | JVM の重大な問題 | 管理しないでください |
| チェックされた例外 | 予測可能な例外 | 義務的 |
| チェックなし (実行時) | プログラミングエラー | オプション |
トライ・キャッチ・最後に
public class GestioneVoti {
public static void main(String[] args) {
try {
// Codice che può generare eccezioni
int voto = Integer.parseInt("trenta");
System.out.println("Voto: " + voto);
} catch (NumberFormatException e) {
// Gestione dell'eccezione
System.out.println("Errore: il voto deve essere numerico");
System.out.println("Dettaglio: " + e.getMessage());
} finally {
// Sempre eseguito (anche se c'è eccezione)
System.out.println("Fine elaborazione voti");
}
}
}
複数のキャッチ
public class RegistroStudenti {
private String[] studenti = new String[100];
private int count = 0;
public void registraStudente(String nome, String matricolaStr) {
try {
// Può lanciare NumberFormatException
int matricola = Integer.parseInt(matricolaStr);
// Può lanciare ArrayIndexOutOfBoundsException
studenti[count] = nome + " (" + matricola + ")";
count++;
System.out.println("Studente registrato: " + nome);
} catch (NumberFormatException e) {
System.out.println("Errore: matricola non valida");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Errore: registro pieno");
} catch (Exception e) {
// Catch generico (deve essere l'ultimo)
System.out.println("Errore imprevisto: " + e.getMessage());
}
}
// Multi-catch (Java 7+)
public void registraConMultiCatch(String nome, String matricolaStr) {
try {
int matricola = Integer.parseInt(matricolaStr);
studenti[count++] = nome + " (" + matricola + ")";
} catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
// Gestione comune per entrambe le eccezioni
System.out.println("Errore nella registrazione: " + e.getMessage());
}
}
}
投げて投げる
throw 例外をスローし、 throws 宣言します
メソッドが例外をスローできること。
public class ValidatoreVoti {
// throws dichiara che il metodo può lanciare IllegalArgumentException
public void registraVoto(String studente, int voto) throws IllegalArgumentException {
// Validazione con throw
if (studente == null || studente.trim().isEmpty()) {
throw new IllegalArgumentException("Nome studente obbligatorio");
}
if (voto < 18 || voto > 30) {
throw new IllegalArgumentException(
"Voto non valido: " + voto + ". Deve essere tra 18 e 30"
);
}
System.out.println(studente + " ha preso " + voto);
}
public static void main(String[] args) {
ValidatoreVoti validatore = new ValidatoreVoti();
try {
validatore.registraVoto("Mario Rossi", 28);
validatore.registraVoto("", 25); // Lancerà eccezione
} catch (IllegalArgumentException e) {
System.out.println("Errore validazione: " + e.getMessage());
}
}
}
チェックされた例外とチェックされていない例外
import java.io.*;
public class LettoreFile {
// Checked exception: DEVE essere dichiarata o gestita
public String leggiFile(String percorso) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(percorso));
StringBuilder contenuto = new StringBuilder();
String linea;
while ((linea = reader.readLine()) != null) {
contenuto.append(linea).append("\n");
}
reader.close();
return contenuto.toString();
}
public static void main(String[] args) {
LettoreFile lettore = new LettoreFile();
// Opzione 1: Gestire con try-catch
try {
String contenuto = lettore.leggiFile("voti.txt");
System.out.println(contenuto);
} catch (IOException e) {
System.out.println("Impossibile leggere il file: " + e.getMessage());
}
}
}
public class CalcolatriceMedia {
// Unchecked: non obbligatorio dichiarare
public double calcolaMedia(int[] voti) {
// Può lanciare ArithmeticException (division by zero)
// Può lanciare NullPointerException
if (voti == null) {
throw new NullPointerException("Array voti non può essere null");
}
if (voti.length == 0) {
throw new IllegalArgumentException("Array voti vuoto");
}
int somma = 0;
for (int voto : voti) {
somma += voto;
}
return (double) somma / voti.length;
}
public static void main(String[] args) {
CalcolatriceMedia calc = new CalcolatriceMedia();
// Gestione opzionale (ma consigliata)
try {
double media = calc.calcolaMedia(new int[]{28, 30, 25});
System.out.println("Media: " + media);
// Questo genererà eccezione
calc.calcolaMedia(null);
} catch (NullPointerException e) {
System.out.println("Errore: " + e.getMessage());
}
}
}
オンとオフを使用する場合
| タイプ | いつ使用するか | Esempi |
|---|---|---|
| チェック済み | 回復可能なエラー、外部リソース | IO例外、SQL例外 |
| チェックなし | バグ、プログラミングエラー | NullPointer、IllegalArgument |
カスタム例外
// Eccezione checked (estende Exception)
public class StudenteNonTrovatoException extends Exception {
private String matricola;
public StudenteNonTrovatoException(String matricola) {
super("Studente con matricola " + matricola + " non trovato");
this.matricola = matricola;
}
public StudenteNonTrovatoException(String matricola, Throwable cause) {
super("Studente con matricola " + matricola + " non trovato", cause);
this.matricola = matricola;
}
public String getMatricola() {
return matricola;
}
}
// Eccezione unchecked (estende RuntimeException)
public class VotoNonValidoException extends RuntimeException {
private int voto;
private int minimo;
private int massimo;
public VotoNonValidoException(int voto, int minimo, int massimo) {
super(String.format(
"Voto %d non valido. Deve essere compreso tra %d e %d",
voto, minimo, massimo
));
this.voto = voto;
this.minimo = minimo;
this.massimo = massimo;
}
public int getVoto() { return voto; }
public int getMinimo() { return minimo; }
public int getMassimo() { return massimo; }
}
import java.util.*;
public class RegistroEsami {
private Map<String, List<Integer>> votiStudenti = new HashMap<>();
public void iscriviStudente(String matricola, String nome) {
votiStudenti.put(matricola, new ArrayList<>());
System.out.println("Iscritto: " + nome + " (" + matricola + ")");
}
public void registraVoto(String matricola, int voto)
throws StudenteNonTrovatoException {
// Verifica studente
if (!votiStudenti.containsKey(matricola)) {
throw new StudenteNonTrovatoException(matricola);
}
// Verifica voto (unchecked - errore di programmazione)
if (voto < 18 || voto > 30) {
throw new VotoNonValidoException(voto, 18, 30);
}
votiStudenti.get(matricola).add(voto);
System.out.println("Voto " + voto + " registrato per " + matricola);
}
public double getMedia(String matricola) throws StudenteNonTrovatoException {
if (!votiStudenti.containsKey(matricola)) {
throw new StudenteNonTrovatoException(matricola);
}
List<Integer> voti = votiStudenti.get(matricola);
if (voti.isEmpty()) {
return 0;
}
return voti.stream()
.mapToInt(Integer::intValue)
.average()
.orElse(0);
}
public static void main(String[] args) {
RegistroEsami registro = new RegistroEsami();
registro.iscriviStudente("MAT001", "Mario Rossi");
registro.iscriviStudente("MAT002", "Laura Bianchi");
try {
registro.registraVoto("MAT001", 28);
registro.registraVoto("MAT001", 30);
registro.registraVoto("MAT003", 25); // Studente non esiste
} catch (StudenteNonTrovatoException e) {
System.out.println("Errore: " + e.getMessage());
System.out.println("Matricola cercata: " + e.getMatricola());
}
try {
registro.registraVoto("MAT002", 35); // Voto non valido
} catch (VotoNonValidoException e) {
System.out.println("Errore voto: " + e.getMessage());
} catch (StudenteNonTrovatoException e) {
System.out.println("Studente non trovato");
}
}
}
リソースを試す
Java 7 で導入され、リソースのクローズを自動的に処理します。
彼らが実装すること AutoCloseable.
// Vecchio modo: finally per chiudere
public void leggiFileVecchio(String path) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(path));
String linea;
while ((linea = reader.readLine()) != null) {
System.out.println(linea);
}
} catch (IOException e) {
System.out.println("Errore lettura: " + e.getMessage());
} finally {
// Chiusura manuale (verbosa e error-prone)
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
System.out.println("Errore chiusura: " + e.getMessage());
}
}
}
}
import java.io.*;
import java.nio.file.*;
public class GestoreFile {
// Try-with-resources: chiusura automatica
public void leggiFile(String path) {
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
String linea;
while ((linea = reader.readLine()) != null) {
System.out.println(linea);
}
} catch (IOException e) {
System.out.println("Errore: " + e.getMessage());
}
// reader.close() chiamato automaticamente!
}
// Risorse multiple
public void copiaFile(String sorgente, String destinazione) {
try (
BufferedReader reader = new BufferedReader(new FileReader(sorgente));
BufferedWriter writer = new BufferedWriter(new FileWriter(destinazione))
) {
String linea;
while ((linea = reader.readLine()) != null) {
writer.write(linea);
writer.newLine();
}
System.out.println("File copiato con successo");
} catch (IOException e) {
System.out.println("Errore copia: " + e.getMessage());
}
}
// Con NIO.2 (più moderno)
public void leggiFileNio(String path) {
try {
String contenuto = Files.readString(Path.of(path));
System.out.println(contenuto);
} catch (IOException e) {
System.out.println("Errore: " + e.getMessage());
}
}
}
public class ConnessioneDatabase implements AutoCloseable {
private String nome;
private boolean connesso;
public ConnessioneDatabase(String nome) {
this.nome = nome;
this.connesso = true;
System.out.println("Connessione aperta: " + nome);
}
public void eseguiQuery(String query) {
if (!connesso) {
throw new IllegalStateException("Connessione chiusa");
}
System.out.println("Eseguo: " + query);
}
@Override
public void close() {
if (connesso) {
connesso = false;
System.out.println("Connessione chiusa: " + nome);
}
}
public static void main(String[] args) {
// Chiusura automatica garantita
try (ConnessioneDatabase db = new ConnessioneDatabase("ScuolaDB")) {
db.eseguiQuery("SELECT * FROM studenti");
db.eseguiQuery("SELECT * FROM esami");
}
// Output:
// Connessione aperta: ScuolaDB
// Eseguo: SELECT * FROM studenti
// Eseguo: SELECT * FROM esami
// Connessione chiusa: ScuolaDB
}
}
完全な例: 試験管理システム
package scuola.eccezioni;
public class EsameException extends Exception {
public EsameException(String message) {
super(message);
}
public EsameException(String message, Throwable cause) {
super(message, cause);
}
}
public class StudenteNonIscrittoException extends EsameException {
private String matricola;
private String corso;
public StudenteNonIscrittoException(String matricola, String corso) {
super(String.format(
"Studente %s non iscritto al corso %s",
matricola, corso
));
this.matricola = matricola;
this.corso = corso;
}
public String getMatricola() { return matricola; }
public String getCorso() { return corso; }
}
public class EsameGiaSostenutoException extends EsameException {
private String matricola;
private String esame;
private int votoEsistente;
public EsameGiaSostenutoException(String matricola, String esame, int voto) {
super(String.format(
"Studente %s ha già sostenuto %s con voto %d",
matricola, esame, voto
));
this.matricola = matricola;
this.esame = esame;
this.votoEsistente = voto;
}
}
package scuola;
import scuola.eccezioni.*;
import java.util.*;
public class GestioneEsami {
private Map<String, Set<String>> iscrizioni = new HashMap<>();
private Map<String, Map<String, Integer>> verbali = new HashMap<>();
public void iscriviStudente(String matricola, String corso) {
iscrizioni.computeIfAbsent(corso, k -> new HashSet<>())
.add(matricola);
System.out.println(matricola + " iscritto a " + corso);
}
public void registraEsame(String matricola, String corso, int voto)
throws StudenteNonIscrittoException,
EsameGiaSostenutoException,
VotoNonValidoException {
// Verifica iscrizione
Set<String> studentiCorso = iscrizioni.get(corso);
if (studentiCorso == null || !studentiCorso.contains(matricola)) {
throw new StudenteNonIscrittoException(matricola, corso);
}
// Verifica voto valido
if (voto < 18 || voto > 30) {
throw new VotoNonValidoException(voto, 18, 30);
}
// Verifica esame già sostenuto
String chiaveVerbale = matricola + "_" + corso;
if (verbali.containsKey(matricola)) {
Map<String, Integer> esamiStudente = verbali.get(matricola);
if (esamiStudente.containsKey(corso)) {
throw new EsameGiaSostenutoException(
matricola, corso, esamiStudente.get(corso)
);
}
}
// Registra voto
verbali.computeIfAbsent(matricola, k -> new HashMap<>())
.put(corso, voto);
System.out.println("Esame registrato: " + matricola +
" - " + corso + ": " + voto);
}
public void stampaCarriera(String matricola) {
Map<String, Integer> esami = verbali.get(matricola);
if (esami == null || esami.isEmpty()) {
System.out.println("Nessun esame sostenuto per " + matricola);
return;
}
System.out.println("\n=== Carriera di " + matricola + " ===");
double somma = 0;
for (Map.Entry<String, Integer> entry : esami.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
somma += entry.getValue();
}
System.out.printf("Media: %.2f%n", somma / esami.size());
}
public static void main(String[] args) {
GestioneEsami gestione = new GestioneEsami();
// Setup
gestione.iscriviStudente("MAT001", "Programmazione");
gestione.iscriviStudente("MAT001", "Database");
gestione.iscriviStudente("MAT002", "Programmazione");
// Registrazione esami con gestione eccezioni
try {
gestione.registraEsame("MAT001", "Programmazione", 28);
gestione.registraEsame("MAT001", "Database", 30);
gestione.registraEsame("MAT002", "Programmazione", 25);
// Errori intenzionali per test
gestione.registraEsame("MAT001", "Programmazione", 27);
} catch (StudenteNonIscrittoException e) {
System.err.println("ERRORE ISCRIZIONE: " + e.getMessage());
} catch (EsameGiaSostenutoException e) {
System.err.println("ERRORE DUPLICATO: " + e.getMessage());
} catch (VotoNonValidoException e) {
System.err.println("ERRORE VOTO: " + e.getMessage());
}
// Stampa carriere
gestione.stampaCarriera("MAT001");
gestione.stampaCarriera("MAT002");
}
}
ベストプラクティス
例外の黄金律
- 通常のフローでは例外を使用しないでください。 例外は例外的な場合にのみ適用されます
- 一般的な前に特定のものをキャッチします。 最も具体的なものから最も一般的なものの順に並べる
- 捕まえたり無視したりしないでください。 少なくともエラーをログに記録してください
- try-with-resources を使用します。 閉じる必要があるリソースの場合
- 文書の例外: Javadoc で @throws を使用する
- プログラミング エラーの場合はチェックを外しておくことをお勧めします。 NullPointer、IllegalArgument
- ドメイン例外を作成します。 特定のビジネス ロジック エラーの場合
- 役立つコンテキストを含めます: 関連データを含むメッセージを明確にする
// ❌ SBAGLIATO: catch vuoto
try {
rischiosa();
} catch (Exception e) {
// Non fare nulla = bug nascosto!
}
// ❌ SBAGLIATO: catch troppo generico
try {
rischiosa();
} catch (Throwable t) { // Cattura anche Error!
// ...
}
// ❌ SBAGLIATO: eccezioni per controllo flusso
try {
int i = 0;
while (true) {
array[i++].process(); // Aspetta IndexOutOfBounds
}
} catch (IndexOutOfBoundsException e) {
// Fine array
}
// ✅ CORRETTO
for (int i = 0; i < array.length; i++) {
array[i].process();
}
結論
例外処理は堅牢なコードにとって重要です。 階層を理解し、try-with-resources を使用してビルドする ドメイン例外は必須のスキルです。
覚えておくべき重要なポイント
- チェック済み: 回復可能なエラー、それらの処理が必須
- チェックを外した場合: プログラミングのバグ、オプションの処理
- リソースを試してみます: リソースの自動クローズ用
- カスタム例外: ドメイン固有のエラーの場合
- ついに: 常に実行、クリーンアップに便利
- 投げる: 例外をスローします。 投げる: メソッド内で宣言する
Nel 次の記事 私たちは探索します 入力/出力 Javaで: ファイル、ストリーム、NIO.2、およびデータ管理。







