# Bài 4: Ném Ngoại Lệ (Throwing Exceptions)

#### 1\. Giới Thiệu

Ném ngoại lệ (throwing exceptions) là một khía cạnh quan trọng trong việc xử lý lỗi trong Java. Khi một phương thức gặp phải tình huống không thể xử lý, nó có thể ném ra một ngoại lệ để báo hiệu lỗi này cho phương thức gọi. Bài viết này sẽ đi sâu vào cách ném ngoại lệ, tạo ngoại lệ tuỳ chỉnh (custom exceptions), và khi nào nên ném ngoại lệ.

#### 2\. Ném Ngoại Lệ Sử Dụng Từ Khóa `throw`

##### 2.1. Khái Niệm

Từ khóa `throw` được sử dụng để ném một ngoại lệ cụ thể. Bạn có thể ném bất kỳ đối tượng nào là một instance của lớp `Throwable` (thường là `Exception` hoặc `Error`).

##### 2.2. Cú Pháp

```java
throw new ExceptionType("Error message");
```

##### 2.3. Ví Dụ Chi Tiết

###### Ví Dụ 1: Ném Một Ngoại Lệ Cụ Thể

```java
public class ThrowExample {
    public static void main(String[] args) {
        try {
            validateAge(15);
        } catch (IllegalArgumentException e) {
            System.err.println("Caught IllegalArgumentException: " + e.getMessage());
        }
    }

    public static void validateAge(int age) {
        if (age < 18) {
            throw new IllegalArgumentException("Age must be at least 18");
        }
        System.out.println("Age is valid");
    }
}
```

Trong ví dụ này, phương thức `validateAge` ném ra `IllegalArgumentException` nếu tuổi nhỏ hơn 18. Phương thức gọi xử lý ngoại lệ này trong khối `catch`.

#### 3\. Tạo Ngoại Lệ Tuỳ Chỉnh (Custom Exceptions)

##### 3.1. Khái Niệm

Ngoại lệ tuỳ chỉnh là các ngoại lệ do người dùng định nghĩa, kế thừa từ lớp `Exception` hoặc `RuntimeException`. Chúng giúp cung cấp thông tin cụ thể và rõ ràng hơn về lỗi xảy ra.

##### 3.2. Cách Tạo Ngoại Lệ Tuỳ Chỉnh

###### Ví Dụ: Tạo Ngoại Lệ Tuỳ Chỉnh

```java
public class InvalidAgeException extends Exception {
    public InvalidAgeException(String message) {
        super(message);
    }
}

public class CustomExceptionExample {
    public static void main(String[] args) {
        try {
            validateAge(15);
        } catch (InvalidAgeException e) {
            System.err.println("Caught InvalidAgeException: " + e.getMessage());
        }
    }

    public static void validateAge(int age) throws InvalidAgeException {
        if (age < 18) {
            throw new InvalidAgeException("Age must be at least 18");
        }
        System.out.println("Age is valid");
    }
}
```

Trong ví dụ này, chúng ta tạo một ngoại lệ tuỳ chỉnh `InvalidAgeException` và sử dụng nó trong phương thức `validateAge`.

##### 3.3. Ngoại Lệ Checked và Unchecked Tuỳ Chỉnh

* **Checked Exception**: Kế thừa từ `Exception`.
    
* **Unchecked Exception**: Kế thừa từ `RuntimeException`.
    

###### Ví Dụ: Tạo Unchecked Exception Tuỳ Chỉnh

```java
public class InvalidAgeRuntimeException extends RuntimeException {
    public InvalidAgeRuntimeException(String message) {
        super(message);
    }
}

public class CustomUncheckedExceptionExample {
    public static void main(String[] args) {
        try {
            validateAge(15);
        } catch (InvalidAgeRuntimeException e) {
            System.err.println("Caught InvalidAgeRuntimeException: " + e.getMessage());
        }
    }

    public static void validateAge(int age) {
        if (age < 18) {
            throw new InvalidAgeRuntimeException("Age must be at least 18");
        }
        System.out.println("Age is valid");
    }
}
```

Trong ví dụ này, chúng ta tạo một ngoại lệ tuỳ chỉnh `InvalidAgeRuntimeException` kế thừa từ `RuntimeException` và sử dụng nó trong phương thức `validateAge`.

#### 4\. Khi Nào Nên Ném Ngoại Lệ

##### 4.1. Khi Điều Kiện Đầu Vào Không Hợp Lệ

Nếu một phương thức nhận được các tham số không hợp lệ, ném ngoại lệ là cách tốt để báo hiệu lỗi này cho phương thức gọi.

```java
public class InputValidationExample {
    public static void main(String[] args) {
        try {
            divide(10, 0);
        } catch (IllegalArgumentException e) {
            System.err.println("Caught IllegalArgumentException: " + e.getMessage());
        }
    }

    public static int divide(int a, int b) {
        if (b == 0) {
            throw new IllegalArgumentException("Divider cannot be zero");
        }
        return a / b;
    }
}
```

##### 4.2. Khi Không Thể Hoàn Thành Công Việc

Nếu một phương thức không thể hoàn thành công việc của mình do một lý do nào đó (ví dụ: không thể kết nối đến cơ sở dữ liệu), nó nên ném ngoại lệ để báo hiệu lỗi này.

```java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DatabaseConnectionExample {
    public static void main(String[] args) {
        try {
            connectToDatabase();
        } catch (SQLException e) {
            System.err.println("Caught SQLException: " + e.getMessage());
        }
    }

    public static void connectToDatabase() throws SQLException {
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase", "username", "password");
        if (conn == null) {
            throw new SQLException("Failed to establish a database connection");
        }
        System.out.println("Connected to the database");
    }
}
```

##### 4.3. Khi Có Lỗi Nội Bộ

Nếu một phương thức gặp lỗi nội bộ mà nó không thể tự xử lý, nó nên ném ngoại lệ để báo hiệu lỗi này.

```java
public class InternalErrorExample {
    public static void main(String[] args) {
        try {
            processFile("example.txt");
        } catch (Exception e) {
            System.err.println("Caught Exception: " + e.getMessage());
        }
    }

    public static void processFile(String filePath) throws Exception {
        try {
            readFile(filePath);
        } catch (IOException e) {
            throw new Exception("Internal error while processing file", e);
        }
    }

    public static void readFile(String filePath) throws IOException {
        throw new IOException("Failed to read file");
    }
}
```

#### 5\. Kết Luận

Trong bài viết này, chúng ta đã tìm hiểu cách ném ngoại lệ sử dụng từ khóa `throw`, tạo các ngoại lệ tuỳ chỉnh, và khi nào nên ném ngoại lệ. Hiểu rõ và áp dụng đúng cách việc ném ngoại lệ là bước quan trọng để viết mã nguồn chất lượng cao và dễ bảo trì trong Java. Trong bài viết tiếp theo, chúng ta sẽ tiếp tục khám phá các kỹ thuật xử lý ngoại lệ nâng cao.
