1. Custom Exception trong Java là gì?

Trong Java đã định nghĩa sẵn các Exception class cho ta như ArithaturesException, NullPulumException,… Những Exception này sẽ được ném ra khi gặp các trường hợp cụ thể nào đó. Thực tế có rất nhiều những ngoại lệ khác nhau xảy ra trong những quá trình, hoàn cảnh khác nhau mà các Exception class này không đáp ứng được nhu cầu của ta. Vì vậy đã sinh ra một ngoại lệ là Custom Exception hay còn được gọi là ngoại lệ do người dùng tự định nghĩa. Nghĩa là ngoại lệ này do ta tự định nghĩa, tự tạo ra theo yêu cầu của chính ta. Nhờ đó ta có thể đưa ra riêng kiểu và thông điệp của ngoại lệ cho riêng mình.

Thông thường, để tạo ra custom Exception thuộc loại checked chúng ta kế thừa từ lớp Exception. Để tạo Custom Exception thuộc loại unchecked chúng ta kế thừa từ lớp RuntimeException. Trong các ứng dụng thực tế hay trong quá trình làm việc, thông thường Custom Exception được tạo là checked Exception.

Để tạo một ngoại lệ tùy chỉnh, chúng ta cần mở rộng lớp Ngoại lệ thuộc gói java.lang

Để tạo một Custom Exception ta có thể xem qua khung mẫu sau

public class MyBusinessException extends Exception {
    public MyBusinessException(String message, Throwable cause) {
        super(message, cause);
    }
}

Ví dụ: Chúng ta truyền chuỗi cho phương thức khởi tạo của lớp cha. Ngoại lệ được lấy bằng cách sử dụng hàm getMessage() trên đối tượng được tạo.

// Lớp đại diện cho ngoại lệ do sử dụng xác định

class MyException extends Exception {
    public MyException(String s)
    {
        // Gọi hàm tạo của ngoại lệ cha
        super(s);
    }
}
// Một lớp sử dụng bên trên MyException
public class Main {
    
    public static void main(String args[])
    {
        try {
            //Ném một đối tượng ngoại lệ do người dùng xác định
            throw new MyException("custom exception");
        }
        catch (MyException ex) {
            System.out.println("laptrinhtudau.com");

            // In thông báo từ đối tượng MyException
            System.out.println(ex.getMessage());
        }
    }
}

Kết quả

laptrinhtudau.com
custom exception

Trong đoạn mã trên, phương thức khởi tạo của MyException yêu cầu một chuỗi làm đối số của nó. Chuỗi được chuyển tới phương thức khởi tạo của lớp cha Exception bằng cách sử dụng super() . Phương thức khởi tạo của lớp Exception cũng có thể được gọi mà không cần tham số và việc gọi super là không bắt buộc.

class MyException extends Exception {
}

public class Main {
    public static void main(String args[])
    {
        try {
            throw new MyException();
        }
        catch (MyException ex) {
            System.out.println("laptrinhtudau.com");
            System.out.println(ex.getMessage());
        }
    }
}

Kết quả

laptrinhtudau.com
null

2. Tại sao lại phải sử dụng Custom Exception?

Các ngôn ngữ lập trình hiện tại đều đã xây dựng hầu hết sẵn gần như tất cả các Exception có thể xảy ra trong chương trình của ta. Tuy nhiên không gì là tất cả. Vẫn có rất nhiều trường hợp khác nhau xảy ra như lỗi thuật toán, lỗi nghiệp vụ, lỗi thao tác với application… thì không thể nào try catch bằng Exception sẵn có được. Chính vì thế các ngôn ngữ lập trình cho phép lập trình viên có thể tạo ra một Exception mới để sử dụng cho các trường hợp này.

Như một ví dụ đơn giản thì việc lọc người trên 18 tuổi đủ điều kiện vào làm. Nếu có nhân viên nào dưới 18 tuổi thì đó chính là một Exception. Mà trong trường hợp này thì không có Exception có sẵn để giúp ta in ra thông báo mà ta mong muốn.

class InvalidAgeException extends Exception{  
 InvalidAgeException(String s){  
  super(s);  
 }  
} 
class Main{  
  
   static void validate(int age)throws InvalidAgeException{  
     if(age<18)  
      throw new InvalidAgeException("Bạn không đủ tuổi");  
     else  
      System.out.println("Chúc mừng bạn đủ tuổi");  
   }  
     
   public static void main(String args[]){  
      try{  
      validate(13);  
      }catch(Exception m){System.out.println("Exception xuất hiện: "+m);}  
  
      System.out.println("laptrinhtudau.com");  
  }  
}  

Kết quả

Exception xuất hiện: InvalidAgeException: Bạn không đủ tuổi
laptrinhtudau.com

Ngoài những cái chưa có ra, thì đối với các Exception có sẵn có vẫn tồn tại những hạn chế nhất định. Chẳng hạn như việc hiển thị lỗi, hay nhưng phương thức sẵn có không thích hợp để xử lý cho trường hợp của bạn thì lúc này Custom Exception là lựa chọn số một. Bên cạnh việc hỗ trợ cho xử lý các trường hợp không mong muốn, Custom Exception còn đóng một vai trò không nhỏ trong việc bảo mật thông tin.

3. Lưu ý khi sử dụng Custom Exception

Không nên lạm dụng Custom Exception: Custom Exception rất hữu ích trong việc xử lý ngoại lệ, nhất là trong việc cung cấp thông tin hữu ích, chính xác tương thích với từng trường hợp. Tuy nhiên hãy xem xét và cân nhắc sử dụng Custom Exception nếu nó mang lại nhiều lợi ích so với những Exception sẵn có. Còn không ta có thể sử dụng Exception sẵn có.
Khi tạo một Custom Exception thì hãy tạo thêm comment mô tả rõ Exception của ta. Từ cấu trúc, trường hợp xảy ra, message output… Điều này là cần thiết bởi vì không chỉ mình bạn thao tác với Exception đó. Vì nên nhớ có thể không chỉ mình ta sử lý chương trình hay quản lý code. Thậm chí đôi lúc ta có thể quên thì vẫn còn những comment ở đó giúp ta.
Khi đặt tên Custom Exception thì hãy lưu ý đặt tên một cách tường minh, có tính gợi nhớ đến nguyên nhân gây ra Exception đó hãy kết thúc tên bằng từ khóa Exception nhé. Một số tên gọi custom Exception mà bạn có thể tham khảo như:

  • IncorrectFileNameException : Exception liên quan đến tên file bạn đặt không hợp lệ.
  • FileNotFoundException : Exception liên quan đến việc không tìm thấy file cần xử lý.
  • MyBusinessException : Exception liên quan đến các xử lý nghiệp vụ.

4. Ví dụ về Custom Exception trong Java

Tất cả những gì chúng ta cần là thực hiện ít nhất một constructor với các tham số tương ứng với superclass là Exception hoặc RuntimeException. Tuy nhiên, để dễ hình dung hơn ta đến với ví dụ sau xử lý Exception sẵn có:

try (Scanner file = new Scanner(new File(fileName))) {
    if (file.hasNextLine()) return file.nextLine();
} catch(FileNotFoundException e) {
    // Logging, etc 
}

Đây là cách xử lý xcEeption đơn giản với try catch . Trường hợp chương trình throw FileNotFoundException thì ta không thể biết chính xác nguyên nhân gây ra lỗi là do file không tồn tại hay filename không hợp lệ dẫn đến không đọc được file. Để xác định rõ nguyên nhân Exception là do đâu thì ta cần thêm xử lý check filename khi xảy ra FileNotFoundException để biết là filename có hợp lệ hay không. Nếu filename không hợp lệ thì sẽ throw exception. Nhưng Java lại không có Exception này nên ta sẽ tạo một Custom Exception có tên là IncorrectFileNameException cho nó.

public class IncorrectFileNameException extends Exception { 
    public IncorrectFileNameException(String errorMessage) {
        super(errorMessage);
    }
}

Và nó sẽ được sử dụng như sau:

try (Scanner file = new Scanner(new File(fileName))) {
    if (file.hasNextLine())
        return file.nextLine();
} catch (FileNotFoundException e) {
    if (!isCorrectFileName(fileName)) {
        throw new IncorrectFileNameException("Incorrect filename : " + fileName );
    }
    //...
}

Với xử lý thế này thì ta có thể biến nguyên nhân do Filename không hợp lệ hoặc không tồn tại. Ở đây thông tin lỗi là file không hợp lệ. Nhưng vô tình chúng ta đã bỏ nguyên nhân gốc là FileNotFoundException. Chỉ xem thông tin lỗi thì ta không thể biết được là do xảy ra FileNotFoundException nên mới kiểm tra và xác định nguyên nhân gây lỗi là IncorrectFileNameException. Để khắc phục điều này, ta cần phải thêm thông tin của FileNotFoundException vào IncorrectFileNameException. Nghĩa là ta phải thực hiện constructor chứa java.lang tham số có thể ném của super class. Sửa lại IncorrectFileNameException:

public class IncorrectFileNameException extends Exception { 
    public IncorrectFileNameException(String errorMessage, Throwable err) {
        super(errorMessage, err);
    }
}