📚 Introducere în Excepții
Ce sunt excepțiile?
Excepțiile sunt evenimente neașteptate care apar în timpul execuției unui program și întrerup fluxul normal al instrucțiunilor. Java oferă un mecanism robust pentru gestionarea acestor situații excepționale.
Excepțiile sunt evenimente neașteptate care apar în timpul execuției unui program și întrerup fluxul normal al instrucțiunilor. Java oferă un mecanism robust pentru gestionarea acestor situații excepționale.
Exemplu simplu de excepție
Exemplu 1
public class ExempluSimplu {
public static void main(String[] args) {
// Acest cod va genera o excepție
int[] array = new int[5];
System.out.println(array[10]); // ArrayIndexOutOfBoundsException
}
}
🎯 Tipuri de Excepții
Ierarhia Excepțiilor în Java
Throwable
Error
Exception
RuntimeException
IOException
SQLException
| Tip Excepție | Descriere | Verificată/Neverificată |
|---|---|---|
| NullPointerException | Acces la un obiect null | Neverificată |
| ArrayIndexOutOfBoundsException | Index invalid pentru array | Neverificată |
| IOException | Erori de Input/Output | Verificată |
| SQLException | Erori legate de baza de date | Verificată |
| ClassNotFoundException | Clasa nu a fost găsită | Verificată |
🛡️ Blocuri Try-Catch-Finally
1. Try-Catch Simplu
Exemplu 2
public class TryCatchSimplu {
public static void main(String[] args) {
try {
int rezultat = 10 / 0;
System.out.println("Rezultat: " + rezultat);
} catch (ArithmeticException e) {
System.out.println("Eroare: Nu se poate împărți la zero!");
System.out.println("Mesaj detaliat: " + e.getMessage());
}
}
}
2. Multiple Catch Blocks
Exemplu 3
import java.util.Scanner;
public class MultipleCatch {
public static void main(String[] args) {
try {
Scanner scanner = new Scanner(System.in);
System.out.print("Introduceți un număr: ");
int num = Integer.parseInt(scanner.nextLine());
int[] array = {1, 2, 3};
System.out.println(array[num]);
int rezultat = 100 / num;
System.out.println("Rezultat: " + rezultat);
} catch (NumberFormatException e) {
System.out.println("Eroare: Vă rugăm introduceți un număr valid!");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Eroare: Index în afara limitelor array-ului!");
} catch (ArithmeticException e) {
System.out.println("Eroare: Operație matematică invalidă!");
} catch (Exception e) {
System.out.println("Eroare generală: " + e.getMessage());
}
}
}
3. Try-Catch-Finally
Exemplu 4
import java.io.*;
public class TryCatchFinally {
public static void citesteFisier(String numeFisier) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(numeFisier));
String linie;
while ((linie = reader.readLine()) != null) {
System.out.println(linie);
}
} catch (FileNotFoundException e) {
System.out.println("Eroare: Fișierul nu a fost găsit!");
} catch (IOException e) {
System.out.println("Eroare la citirea fișierului!");
} finally {
// Blocul finally se execută întotdeauna
try {
if (reader != null) {
reader.close();
System.out.println("Fișier închis cu succes!");
}
} catch (IOException e) {
System.out.println("Eroare la închiderea fișierului!");
}
}
}
}
4. Try-with-Resources (Java 7+)
Exemplu 5
import java.io.*;
public class TryWithResources {
public static void procesareFisier(String fisierInput, String fisierOutput) {
// Resursele se închid automat după blocul try
try (
BufferedReader reader = new BufferedReader(new FileReader(fisierInput));
BufferedWriter writer = new BufferedWriter(new FileWriter(fisierOutput))
) {
String linie;
while ((linie = reader.readLine()) != null) {
writer.write(linie.toUpperCase());
writer.newLine();
}
System.out.println("Fișier procesat cu succes!");
} catch (IOException e) {
System.out.println("Eroare I/O: " + e.getMessage());
}
// Nu mai e nevoie de finally pentru închiderea resurselor
}
}
🚀 Throw și Throws
1. Folosirea throw
Exemplu 6
public class ValidareVarsta {
public static void verificaVarsta(int varsta) {
if (varsta < 0) {
throw new IllegalArgumentException("Vârsta nu poate fi negativă!");
}
if (varsta < 18) {
throw new IllegalStateException("Trebuie să aveți minim 18 ani!");
}
System.out.println("Vârstă validă: " + varsta);
}
public static void main(String[] args) {
try {
verificaVarsta(15);
} catch (IllegalStateException e) {
System.out.println("Eroare de stare: " + e.getMessage());
} catch (IllegalArgumentException e) {
System.out.println("Argument invalid: " + e.getMessage());
}
}
}
2. Folosirea throws
Exemplu 7
import java.io.*;
import java.sql.*;
public class DatabaseOperations {
// Metoda declară că poate arunca SQLException
public void conectareBazaDate() throws SQLException {
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/test",
"user",
"password"
);
// Operații cu baza de date
}
// Metodă care aruncă multiple excepții
public void procesareDate(String fisier)
throws IOException, SQLException, ClassNotFoundException {
// Citire din fișier
BufferedReader reader = new BufferedReader(new FileReader(fisier));
// Conectare la baza de date
conectareBazaDate();
// Încărcare clase dinamice
Class.forName("com.mysql.jdbc.Driver");
}
public static void main(String[] args) {
DatabaseOperations db = new DatabaseOperations();
try {
db.procesareDate("date.txt");
} catch (IOException e) {
System.out.println("Eroare I/O: " + e.getMessage());
} catch (SQLException e) {
System.out.println("Eroare SQL: " + e.getMessage());
} catch (ClassNotFoundException e) {
System.out.println("Clasă negăsită: " + e.getMessage());
}
}
}
3. Re-throwing Exceptions
Exemplu 8
public class ReThrowExample {
public static void metodaNivel1() throws Exception {
try {
metodaNivel2();
} catch (Exception e) {
System.out.println("Logare eroare în Nivel 1: " + e.getMessage());
// Re-aruncăm excepția pentru procesare ulterioară
throw e;
}
}
public static void metodaNivel2() throws Exception {
throw new Exception("Eroare originală din Nivel 2");
}
public static void main(String[] args) {
try {
metodaNivel1();
} catch (Exception e) {
System.out.println("Procesare finală în main: " + e.getMessage());
}
}
}
⚡ Excepții Personalizate
1. Excepție Simplă Personalizată
Exemplu 9
// Excepție personalizată pentru validare email
class EmailInvalidException extends Exception {
public EmailInvalidException(String mesaj) {
super(mesaj);
}
}
public class ValidatorEmail {
public static void valideazaEmail(String email)
throws EmailInvalidException {
if (email == null || email.isEmpty()) {
throw new EmailInvalidException("Email-ul nu poate fi gol!");
}
if (!email.contains("@")) {
throw new EmailInvalidException("Email-ul trebuie să conțină @!");
}
if (!email.contains(".")) {
throw new EmailInvalidException("Email-ul trebuie să conțină un domeniu valid!");
}
System.out.println("Email valid: " + email);
}
public static void main(String[] args) {
String[] emails = {"test@example.com", "invalid-email", ""};
for (String email : emails) {
try {
valideazaEmail(email);
} catch (EmailInvalidException e) {
System.out.println("Eroare: " + e.getMessage());
}
}
}
}
2. Excepție Complexă cu Mai Multe Detalii
Exemplu 10
// Excepție pentru operații bancare
class OperatiuneBancaraException extends Exception {
private String codEroare;
private double suma;
public OperatiuneBancaraException(String mesaj, String codEroare, double suma) {
super(mesaj);
this.codEroare = codEroare;
this.suma = suma;
}
public String getCodEroare() { return codEroare; }
public double getSuma() { return suma; }
}
class ContBancar {
private double sold;
private String numarCont;
public ContBancar(String numarCont, double soldInitial) {
this.numarCont = numarCont;
this.sold = soldInitial;
}
public void retrage(double suma)
throws OperatiuneBancaraException {
if (suma <= 0) {
throw new OperatiuneBancaraException(
"Suma trebuie să fie pozitivă",
"ERR_SUM_NEG",
suma
);
}
if (suma > sold) {
throw new OperatiuneBancaraException(
"Fonduri insuficiente",
"ERR_FONDURI",
suma
);
}
if (suma > 10000) {
throw new OperatiuneBancaraException(
"Suma depășește limita zilnică",
"ERR_LIMITA",
suma
);
}
sold -= suma;
System.out.println("Retragere reușită: " + suma);
System.out.println("Sold nou: " + sold);
}
public double getSold() { return sold; }
}
public class SistemBancar {
public static void main(String[] args) {
ContBancar cont = new ContBancar("RO123456", 5000);
double[] sume = {1000, -100, 15000, 6000};
for (double suma : sume) {
try {
System.out.println("\nÎncerc să retrag: " + suma);
cont.retrage(suma);
} catch (OperatiuneBancaraException e) {
System.err.println("Eroare bancară: " + e.getMessage());
System.err.println("Cod eroare: " + e.getCodEroare());
System.err.println("Suma implicată: " + e.getSuma());
// Logică specifică pe baza codului de eroare
switch (e.getCodEroare()) {
case "ERR_FONDURI":
System.out.println("→ Sugestie: Verificați soldul contului");
break;
case "ERR_LIMITA":
System.out.println("→ Sugestie: Încercați o sumă mai mică");
break;
case "ERR_SUM_NEG":
System.out.println("→ Sugestie: Introduceți o sumă validă");
break;
}
}
}
}
}
3. Chain Exceptions (Înlănțuire Excepții)
Exemplu 11
class DataAccessException extends Exception {
public DataAccessException(String mesaj, Throwable cauza) {
super(mesaj, cauza);
}
}
class ServiceException extends Exception {
public ServiceException(String mesaj, Throwable cauza) {
super(mesaj, cauza);
}
}
public class ChainedExceptions {
// Nivel de acces la date
public static void citesteDinBazaDate() throws DataAccessException {
try {
// Simulăm o eroare la conectarea cu baza de date
throw new SQLException("Connection timeout");
} catch (SQLException e) {
// Învelim excepția într-o excepție de nivel mai înalt
throw new DataAccessException(
"Nu s-au putut citi datele din baza de date", e
);
}
}
// Nivel de serviciu
public static void procesareDate() throws ServiceException {
try {
citesteDinBazaDate();
} catch (DataAccessException e) {
// Învelim din nou pentru nivelul de serviciu
throw new ServiceException(
"Serviciul nu poate procesa cererea", e
);
}
}
public static void main(String[] args) {
try {
procesareDate();
} catch (ServiceException e) {
System.err.println("Eroare la nivel de serviciu: " + e.getMessage());
// Obținem cauza originală
Throwable cauza = e.getCause();
if (cauza != null) {
System.err.println("Cauza: " + cauza.getMessage());
// Mergem mai adânc în lanțul de excepții
Throwable cauzaOriginala = cauza.getCause();
if (cauzaOriginala != null) {
System.err.println("Cauza originală: " + cauzaOriginala.getMessage());
}
}
// Afișăm stack trace complet
e.printStackTrace();
}
}
}
✨ Bune Practici și Exemple Avansate
💡 Sfaturi importante:
- ✓ Folosiți excepții specifice în loc de Exception generică
- ✓ Documentați excepțiile cu @throws în Javadoc
- ✓ Nu ignorați niciodată excepțiile (catch block gol)
- ✓ Folosiți try-with-resources pentru resurse AutoCloseable
- ✓ Loggați excepțiile pentru debugging
Exemplu Complex - Aplicație Completă
Exemplu 12 - Sistem Complet
import java.util.*;
import java.io.*;
import java.util.logging.*;
// Excepții personalizate pentru domeniul aplicației
class StudentNotFoundException extends Exception {
public StudentNotFoundException(String id) {
super("Student cu ID " + id + " nu a fost găsit");
}
}
class InvalidGradeException extends Exception {
public InvalidGradeException(double nota) {
super("Nota " + nota + " este invalidă. Trebuie să fie între 1 și 10");
}
}
// Clasa Student
class Student {
private String id;
private String nume;
private List<Double> note;
public Student(String id, String nume) {
this.id = id;
this.nume = nume;
this.note = new ArrayList<>();
}
public void adaugaNota(double nota) throws InvalidGradeException {
if (nota < 1 || nota > 10) {
throw new InvalidGradeException(nota);
}
note.add(nota);
}
public double calculeazaMedie() {
if (note.isEmpty()) {
return 0;
}
return note.stream()
.mapToDouble(Double::doubleValue)
.average()
.orElse(0.0);
}
public String getId() { return id; }
public String getNume() { return nume; }
}
// Sistem de management studenți
class SistemStudenti {
private static final Logger LOGGER = Logger.getLogger(SistemStudenti.class.getName());
private Map<String, Student> studenti;
private String fisierDate;
public SistemStudenti(String fisierDate) {
this.studenti = new HashMap<>();
this.fisierDate = fisierDate;
incarcaDate();
}
private void incarcaDate() {
try (BufferedReader reader = new BufferedReader(new FileReader(fisierDate))) {
String linie;
while ((linie = reader.readLine()) != null) {
try {
String[] parti = linie.split(",");
if (parti.length >= 2) {
Student student = new Student(parti[0], parti[1]);
// Adăugăm notele dacă există
for (int i = 2; i < parti.length; i++) {
try {
double nota = Double.parseDouble(parti[i]);
student.adaugaNota(nota);
} catch (NumberFormatException e) {
LOGGER.warning("Notă invalidă ignorată: " + parti[i]);
} catch (InvalidGradeException e) {
LOGGER.warning(e.getMessage());
}
}
studenti.put(student.getId(), student);
}
} catch (Exception e) {
LOGGER.severe("Eroare la procesarea liniei: " + linie);
}
}
LOGGER.info("Date încărcate cu succes. Total studenți: " + studenti.size());
} catch (FileNotFoundException e) {
LOGGER.warning("Fișierul de date nu există. Se va crea unul nou.");
} catch (IOException e) {
LOGGER.severe("Eroare la citirea fișierului: " + e.getMessage());
}
}
public void salveazaDate() {
try (PrintWriter writer = new PrintWriter(new FileWriter(fisierDate))) {
for (Student student : studenti.values()) {
writer.println(student.getId() + "," + student.getNume());
}
LOGGER.info("Date salvate cu succes");
} catch (IOException e) {
LOGGER.severe("Eroare la salvarea datelor: " + e.getMessage());
}
}
public void adaugaStudent(String id, String nume) {
if (studenti.containsKey(id)) {
LOGGER.warning("Studentul cu ID " + id + " există deja");
return;
}
studenti.put(id, new Student(id, nume));
LOGGER.info("Student adăugat: " + nume);
}
public void adaugaNotaStudent(String id, double nota)
throws StudentNotFoundException, InvalidGradeException {
Student student = studenti.get(id);
if (student == null) {
throw new StudentNotFoundException(id);
}
student.adaugaNota(nota);
LOGGER.info("Notă adăugată pentru " + student.getNume() + ": " + nota);
}
public double getMedieStudent(String id) throws StudentNotFoundException {
Student student = studenti.get(id);
if (student == null) {
throw new StudentNotFoundException(id);
}
return student.calculeazaMedie();
}
}
// Clasa principală cu interfață utilizator
public class AplicatieCatalog {
private static final Scanner scanner = new Scanner(System.in);
private static SistemStudenti sistem;
public static void main(String[] args) {
// Configurare logger
configurareLogger();
sistem = new SistemStudenti("studenti.txt");
boolean rulare = true;
while (rulare) {
try {
afiseazaMeniu();
int optiune = Integer.parseInt(scanner.nextLine());
switch (optiune) {
case 1:
adaugaStudentNou();
break;
case 2:
adaugaNota();
break;
case 3:
afiseazaMedie();
break;
case 4:
sistem.salveazaDate();
break;
case 5:
rulare = false;
System.out.println("La revedere!");
break;
default:
System.out.println("Opțiune invalidă!");
}
} catch (NumberFormatException e) {
System.err.println("Vă rugăm introduceți un număr valid!");
} catch (Exception e) {
System.err.println("Eroare neașteptată: " + e.getMessage());
e.printStackTrace();
}
}
scanner.close();
}
private static void configurareLogger() {
try {
FileHandler fileHandler = new FileHandler("aplicatie.log", true);
fileHandler.setFormatter(new SimpleFormatter());
Logger.getGlobal().addHandler(fileHandler);
} catch (IOException e) {
System.err.println("Nu s-a putut configura logger-ul");
}
}
private static void afiseazaMeniu() {
System.out.println("\n===== CATALOG ELECTRONIC =====");
System.out.println("1. Adaugă student nou");
System.out.println("2. Adaugă notă");
System.out.println("3. Afișează media");
System.out.println("4. Salvează date");
System.out.println("5. Ieșire");
System.out.print("Alegeți opțiunea: ");
}
private static void adaugaStudentNou() {
System.out.print("ID student: ");
String id = scanner.nextLine();
System.out.print("Nume student: ");
String nume = scanner.nextLine();
sistem.adaugaStudent(id, nume);
System.out.println("Student adăugat cu succes!");
}
private static void adaugaNota() {
try {
System.out.print("ID student: ");
String id = scanner.nextLine();
System.out.print("Nota: ");
double nota = Double.parseDouble(scanner.nextLine());
sistem.adaugaNotaStudent(id, nota);
System.out.println("Notă adăugată cu succes!");
} catch (StudentNotFoundException e) {
System.err.println("Eroare: " + e.getMessage());
} catch (InvalidGradeException e) {
System.err.println("Eroare: " + e.getMessage());
} catch (NumberFormatException e) {
System.err.println("Nota trebuie să fie un număr valid!");
}
}
private static void afiseazaMedie() {
try {
System.out.print("ID student: ");
String id = scanner.nextLine();
double medie = sistem.getMedieStudent(id);
System.out.printf("Media studentului: %.2f\n", medie);
} catch (StudentNotFoundException e) {
System.err.println("Eroare: " + e.getMessage());
}
}
}
⚠️ Antipattern-uri de evitat:
- ✗ Catch block gol:
catch (Exception e) { } - ✗ Aruncarea Exception generică
- ✗ Folosirea excepțiilor pentru control flow normal
- ✗ Ignorarea sau suprimarea excepțiilor importante
- ✗ Log și throw - alegeți una dintre ele