try, catch și finally în Java

Excepții Java 1.0 (multi-catch: Java 7)

try încearcă un bloc de cod, catch prinde și tratează excepțiile aruncate în el, iar finally rulează mereu la ieșire.

Sintaxă
try {
    // cod care poate arunca o excepție
} catch (NumberFormatException e) {
    // tratarea unei erori anume
} catch (IllegalStateException | ArithmeticException e) {
    // mai multe tipuri în același catch
} finally {
    // rulează mereu, cu sau fără eroare
}

Ce face try, catch și finally

Când apare o problemă (împărțire la zero, text care nu e număr, fișier lipsă), Java aruncă o excepție: un obiect care descrie eroarea. Dacă nimeni nu o prinde, programul se oprește și afișează stiva de apeluri. Codul pus în try este „supravegheat”: la prima excepție, restul blocului este sărit și se execută primul catch al cărui tip se potrivește.

Poți avea mai multe catch-uri, de la tipul cel mai specific la cel mai general, sau un singur catch cu mai multe tipuri separate prin |. Obiectul excepției (e) are getMessage() și getClass(); e.printStackTrace() afișează detaliile complete.

Blocul finally rulează mereu: după try fără erori, după un catch, chiar și când try face return sau aruncă mai departe. Este locul pentru eliberarea resurselor, deși pentru fișiere și conexiuni try-with-resources este mai simplu.

Exemple

Exemplul 1: Prinderea erorilor de conversie

Fiecare text este convertit separat. Unde conversia eșuează, se execută catch, iar bucla continuă cu următorul.

public class Main {
    public static void main(String[] args) {
        String[] intrari = {"42", "3.5", "abc", "-7", ""};
        int suma = 0;
        for (String text : intrari) {
            try {
                int n = Integer.parseInt(text);
                suma += n;
                System.out.println("OK: " + n);
            } catch (NumberFormatException e) {
                System.out.println("Ignor \"" + text + "\": " + e.getMessage());
            }
        }
        System.out.println("Suma: " + suma);
    }
}

Rezultat

OK: 42
Ignor "3.5": For input string: "3.5"
Ignor "abc": For input string: "abc"
OK: -7
Ignor "": For input string: ""
Suma: 35

Exemplul 2: Mai multe catch-uri și multi-catch

Fiecare caz aruncă alt tip de excepție. ArithmeticException are blocul ei; celelalte două sunt prinse împreună cu |.

public class Main {
    static int imparte(String[] date, int index, int impartitor) {
        int valoare = Integer.parseInt(date[index]);
        return valoare / impartitor;
    }

    public static void main(String[] args) {
        String[] date = {"100", "x", "60"};
        int[][] cazuri = {{0, 4}, {0, 0}, {1, 2}, {5, 1}};
        for (int[] c : cazuri) {
            try {
                System.out.println("rezultat: " + imparte(date, c[0], c[1]));
            } catch (ArithmeticException e) {
                System.out.println("împărțire la zero");
            } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
                System.out.println(e.getClass().getSimpleName() + ": " + e.getMessage());
            }
        }
    }
}

Rezultat

rezultat: 25
împărțire la zero
NumberFormatException: For input string: "x"
ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 3

Exemplul 3: finally rulează mereu

Chiar și când try face return sau când apare o excepție, mesajul din finally se afișează. Ordinea afișării arată exact când rulează fiecare bloc.

public class Main {
    static String citesteNota(String text) {
        System.out.println("  deschid catalogul");
        try {
            int nota = Integer.parseInt(text);
            return "nota " + nota;
        } catch (NumberFormatException e) {
            return "notă invalidă";
        } finally {
            System.out.println("  închid catalogul");
        }
    }

    public static void main(String[] args) {
        System.out.println(citesteNota("9"));
        System.out.println(citesteNota("nouă"));
    }
}

Rezultat

  deschid catalogul
  închid catalogul
nota 9
  deschid catalogul
  închid catalogul
notă invalidă

Greșeli frecvente

  • Prinzi Exception și nu faci nimic (catch (Exception e) { }). Eroarea dispare fără urmă și bug-ul devine foarte greu de găsit. Prinde tipul exact și măcar afișează sau loghează mesajul.
  • Pui catch (Exception e) înaintea unui catch (NumberFormatException e): exception NumberFormatException has already been caught. Ordinea este de la specific la general.
  • Faci return din finally: înlocuiește valoarea sau excepția din try, iar -Xlint avertizează ([finally] finally clause cannot complete normally).

Comenzi înrudite

  • throw și throws: throw aruncă o excepție acum; throws, în antetul metodei, anunță ce excepții verificate poate arunca metoda.
  • Excepții verificate și neverificate: Excepțiile verificate (Exception) trebuie prinse sau declarate cu throws; cele neverificate (RuntimeException) nu sunt cerute de compilator.
  • try-with-resources: try-with-resources deschide resurse (fișiere, conexiuni) în paranteze și le închide automat la final, chiar dacă apare o excepție.
  • Exception: Clasa de bază pentru erorile pe care un program le poate trata; getMessage() returnează mesajul dat la creare.
  • NumberFormatException: Apare când un text nu poate fi transformat în număr: Integer.parseInt(„12a”), „3,14” cu virgulă, text gol.