throw și throws în Java

Excepții Java 1.0

throw aruncă o excepție acum; throws, în antetul metodei, anunță ce excepții verificate poate arunca metoda.

Sintaxă
void retrage(double suma) throws FondInsuficientException {   // throws: anunță
    if (suma <= 0) {
        throw new IllegalArgumentException("sumă invalidă");    // throw: aruncă
    }
    ...
}

Ce face throw și throws

throw este o instrucțiune: throw new IllegalArgumentException("mesaj"); oprește imediat metoda și trimite excepția în sus, la apelanți, până când un catch o prinde. Se folosește pentru a refuza date invalide sau pentru a semnala o situație din care metoda nu își poate reveni.

throws stă în antetul metodei și face parte din „contractul” ei: void citeste() throws IOException spune că metoda poate arunca o IOException. Pentru excepțiile verificate (IOException, excepțiile tale care extind Exception) este obligatoriu; pentru cele neverificate (RuntimeException și subclasele) este opțional.

O excepție prinsă poate fi aruncată din nou sau „împachetată” într-una mai potrivită, păstrând cauza: throw new RuntimeException("nu pot încărca", e);. Cauza se citește apoi cu getCause().

Exemple

Exemplul 1: Validare cu throw

setNota aruncă o excepție pentru valori în afara intervalului. Apelantul decide ce face cu ea.

class Catalog {
    private double nota;

    void setNota(double nota) {
        if (nota < 1 || nota > 10) {
            throw new IllegalArgumentException("nota trebuie să fie între 1 și 10, nu " + nota);
        }
        this.nota = nota;
    }

    double getNota() {
        return nota;
    }
}

public class Main {
    public static void main(String[] args) {
        Catalog c = new Catalog();
        for (double n : new double[] {9.5, 11, 0, 7}) {
            try {
                c.setNota(n);
                System.out.println("nota salvată: " + c.getNota());
            } catch (IllegalArgumentException e) {
                System.out.println("respins: " + e.getMessage());
            }
        }
    }
}

Rezultat

nota salvată: 9.5
respins: nota trebuie să fie între 1 și 10, nu 11.0
respins: nota trebuie să fie între 1 și 10, nu 0.0
nota salvată: 7.0

Exemplul 2: throws: excepția urcă prin apeluri

citesteConfig și pornire declară throws IOException, deci nu o tratează; excepția urcă până în main, unde e prinsă. Fișierul nu există în directorul curent.

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;

public class Main {
    static String citesteConfig(String fisier) throws IOException {
        System.out.println("citesc " + fisier);
        return Files.readString(Path.of(fisier));
    }

    static void pornire() throws IOException {
        String config = citesteConfig("setari.ini");
        System.out.println("config: " + config);
    }

    public static void main(String[] args) {
        try {
            pornire();
        } catch (NoSuchFileException e) {
            System.out.println("lipsește fișierul: " + e.getMessage());
        } catch (IOException e) {
            System.out.println("altă eroare: " + e);
        }
    }
}

Rezultat

citesc setari.ini
lipsește fișierul: setari.ini

Exemplul 3: Împachetare cu păstrarea cauzei

Eroarea tehnică (NumberFormatException) este transformată într-una pe înțelesul aplicației, iar originalul rămâne accesibil prin getCause().

public class Main {
    static int citestePort(String valoare) {
        try {
            return Integer.parseInt(valoare);
        } catch (NumberFormatException e) {
            throw new IllegalStateException("configurație greșită: port=" + valoare, e);
        }
    }

    public static void main(String[] args) {
        System.out.println("port: " + citestePort("8080"));
        try {
            citestePort("80a");
        } catch (IllegalStateException e) {
            System.out.println(e.getMessage());
            System.out.println("cauza: " + e.getCause());
        }
    }
}

Rezultat

port: 8080
configurație greșită: port=80a
cauza: java.lang.NumberFormatException: For input string: "80a"

Greșeli frecvente

  • Confunzi cuvintele: throws în corpul metodei sau throw în antet nu compilează. throw = acțiune; throws = declarație.
  • Arunci o excepție verificată fără să o declari: unreported exception IOException; must be caught or declared to be thrown.
  • Împachetezi o excepție fără cauză (new RuntimeException("eroare") în loc de new RuntimeException("eroare", e)) și pierzi informația despre problema reală.
  • Scrii throw IllegalArgumentException("x") fără new: cannot find symbol.

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.
  • 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.
  • IllegalArgumentException: Excepția aruncată când o metodă primește un argument cu valoare nepermisă (vârstă negativă, lună 13).
  • Exception: Clasa de bază pentru erorile pe care un program le poate trata; getMessage() returnează mesajul dat la creare.