Ce face Excepții verificate și neverificate
Toate excepțiile pornesc din Throwable. Cele care extind Exception, dar nu RuntimeException, sunt verificate (checked): compilatorul obligă fiecare metodă care le poate arunca fie să le prindă cu try/catch, fie să le declare cu throws. Ele descriu probleme externe, previzibile: fișier lipsă, rețea căzută, o regulă de business încălcată.
Subclasele lui RuntimeException sunt neverificate (unchecked) și indică de obicei greșeli de programare: NullPointerException, ArrayIndexOutOfBoundsException, IllegalArgumentException, ArithmeticException. Le poți prinde, dar compilatorul nu te obligă. Subclasele lui Error (OutOfMemoryError, StackOverflowError) nu se tratează în mod normal.
Excepțiile proprii se creează extinzând una din cele două clase. Excepțiile sunt serializabile, deci cu -Xlint trebuie să declari un serialVersionUID. Alege Exception când apelantul poate și trebuie să reacționeze, și RuntimeException pentru erori de utilizare a API-ului.
Exemple
Exemplul 1: O excepție verificată proprie
StocInsuficientException extinde Exception, deci comanda trebuie să o declare cu throws, iar main trebuie să o prindă. Excepția poartă și date proprii (câte bucăți lipsesc).
class StocInsuficientException extends Exception {
private static final long serialVersionUID = 1L;
private final int lipsa;
StocInsuficientException(String produs, int lipsa) {
super("stoc insuficient pentru " + produs + ": lipsesc " + lipsa + " buc.");
this.lipsa = lipsa;
}
int getLipsa() {
return lipsa;
}
}
class Depozit {
private int stoc = 10;
void comanda(String produs, int cantitate) throws StocInsuficientException {
if (cantitate > stoc) {
throw new StocInsuficientException(produs, cantitate - stoc);
}
stoc -= cantitate;
System.out.println("livrat " + cantitate + " x " + produs + ", rămân " + stoc);
}
}
public class Main {
public static void main(String[] args) {
Depozit d = new Depozit();
int[] comenzi = {4, 5, 3};
for (int c : comenzi) {
try {
d.comanda("borcane de zacuscă", c);
} catch (StocInsuficientException e) {
System.out.println("Eroare: " + e.getMessage() + " (comandăm " + e.getLipsa() + " de la furnizor)");
}
}
}
}
Rezultat
livrat 4 x borcane de zacuscă, rămân 6
livrat 5 x borcane de zacuscă, rămân 1
Eroare: stoc insuficient pentru borcane de zacuscă: lipsesc 2 buc. (comandăm 2 de la furnizor)
Exemplul 2: Neverificate: compilatorul nu cere nimic
Metodele de mai jos pot arunca excepții neverificate fără throws. Programul le prinde doar ca să arate tipul și ierarhia fiecăreia.
import java.util.List;
public class Main {
static void ruleaza(String descriere, Runnable cod) {
try {
cod.run();
System.out.println(descriere + ": OK");
} catch (RuntimeException e) {
System.out.println(descriere + ": " + e.getClass().getSimpleName()
+ " (verificată? " + !(e instanceof RuntimeException) + ")");
}
}
public static void main(String[] args) {
String text = null;
int[] note = {9, 10};
ruleaza("lungimea unui null", () -> System.out.println(text.length()));
ruleaza("indice în afara array-ului", () -> System.out.println(note[2]));
ruleaza("împărțire la zero", () -> System.out.println(10 / (note.length - 2)));
ruleaza("modificarea List.of", () -> List.of(1, 2).add(3));
ruleaza("conversie corectă", () -> Integer.parseInt("15"));
System.out.println("IOException e verificată? "
+ !RuntimeException.class.isAssignableFrom(java.io.IOException.class));
}
}
Rezultat
lungimea unui null: NullPointerException (verificată? false)
indice în afara array-ului: ArrayIndexOutOfBoundsException (verificată? false)
împărțire la zero: ArithmeticException (verificată? false)
modificarea List.of: UnsupportedOperationException (verificată? false)
conversie corectă: OK
IOException e verificată? true
Exemplul 3: Excepție verificată într-o lambda
Interfețele din java.util.function nu declară throws, deci o excepție verificată din lambda trebuie prinsă acolo. O soluție obișnuită: împachetarea în UncheckedIOException.
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
public class Main {
public static void main(String[] args) throws IOException {
Files.writeString(Path.of("iasi.txt"), "Iași: 271692");
Files.writeString(Path.of("cluj.txt"), "Cluj: 286598");
List<String> continut = List.of("iasi.txt", "cluj.txt", "lipsa.txt").stream()
.map(f -> {
try {
return Files.readString(Path.of(f));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
})
.limit(2)
.toList();
System.out.println(continut);
try {
List.of("lipsa.txt").forEach(f -> {
try {
Files.readString(Path.of(f));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
} catch (UncheckedIOException e) {
System.out.println("eroare împachetată: " + e.getCause().getClass().getSimpleName());
}
}
}
Rezultat
[Iași: 271692, Cluj: 286598]
eroare împachetată: NoSuchFileException
Greșeli frecvente
- Uiți
serialVersionUIDîntr-o excepție proprie: cu-Xlint:allapare[serial] serializable class StocInsuficientException has no definition of serialVersionUID. - Declari
throws Exceptionpeste tot ca să scapi de erori. Apelanții nu mai știu ce poate merge prost; declară tipurile exacte. - Prinzi
NullPointerExceptionca să ascunzi un bug. Excepțiile neverificate se repară în cod (verificinull, indicii), nu se înghit. - Prinzi o excepție verificată pe care codul din
trynu o poate arunca:exception IOException is never thrown in body of corresponding try statement.
Comenzi înrudite
try, catch și finally: try încearcă un bloc de cod, catch prinde și tratează excepțiile aruncate în el, iar finally rulează mereu la ieșire.throw și throws: throw aruncă o excepție acum; throws, în antetul metodei, anunță ce excepții verificate poate arunca metoda.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.RuntimeException: Clasa de bază a excepțiilor neverificate: nu trebuie declarate cu throws și nici prinse obligatoriu.