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();
}
결론
예외 처리는 강력한 코드에 매우 중요합니다. 계층 구조를 이해하고, 리소스 사용 시도 및 빌드를 사용하세요. 도메인 예외는 필수 기술입니다.
기억해야 할 핵심 사항
- 확인됨: 복구 가능한 오류, 반드시 처리해야 함
- 선택 해제됨: 프로그래밍 버그, 선택적 처리
- 리소스를 사용해 보세요: 자원을 자동으로 닫는 경우
- 사용자 정의 예외: 도메인별 오류의 경우
- 마지막으로: 항상 실행되어 정리에 유용합니다.
- 던지다: 예외 던지기, 던집니다: 메소드에서 선언
에서 다음 기사 우리는 탐구할 것이다 입력/출력 자바에서: 파일, 스트림, NIO.2 및 데이터 관리.







