Chuyển đến phần nội dung

Exception là gì ?

Exception và cách xử lý ngoại lệ trong Java

 

Xử lý ngoại lệ trong Java giúp bạn handle được các trường hợp không mong muốn. Chương trình vẫn có thể hoạt động được một cách bình thường mặc dù đã có lỗi xảy ra ở một module nào đó. Sử dụng khối try-catch, try-catch-finally, try-finally hoặc từ khóa throws để xử lý. Tùy thuộc vào từng tình huống mà chúng ta lựa chọn cách dùng các khối này.

Exception – Ngoại lệ là gì?

Exception là một sự kiện chỉ xảy ra trong quá trình chương trình Java thực thi một câu lệnh nào đó và thông thường nó sẽ phá vỡ luồng làm việc của chương trình.

Ở một câu lệnh bất kỳ nếu xảy ra lỗi, chương trình sẽ tạo ra một Object và đưa nó vào Runtime System. Object này được gọi là Exception Object, nó sẽ chứa tất cả thông tin về lỗi, trạng thái của chương trình khi xảy ra lỗi. Nếu như bạn không handle Exception thì chương trình sẽ ngừng lại và thông báo lỗi.

Ví dụ: Giả sư như bạn có câu lệnh double a = b/c;. Nếu như người dùng nhập vào b = 3 và c = 0 chẳng hạn thì chương trình sẽ bị lỗi và ném ra cho chúng một exception có tên là java.lang.ArithmeticException.

Hệ thống phân cấp lớp Exception

exceptionclasshierarchy

Java có những loại Exception nào?

Trong Java có 2 loại Exception là Checked Exception và UnChecked Exception.

checked-and-unchecked-exception

Checked exceptions: là các Exception xảy ra tại thời điểm Compile time. Những Exception này bắt buộc chúng ta phải handle nó không được bỏ qua. Đựa vào mô hình phân cấp ở trên chúng ta có thể lấy ví dụ một số Checked Exception như là: IOException, FileNotFoundException,….

Unchecked exceptions: Là các Exception xảy ra tại thời điểm Runtime. Những exception này thường liên quan đến programming bug hoặc lỗi Logic. Đây là những lỗi không bắt buộc chúng ta phải handle.

Ví dụ: IndexOutOfBoundsException, NumberFormatException,…

Errors: Đây là những vấn đề rất nghiêm trọng liên quan đến môi trường thực thi của ứng dụng. Thông thường khi gặp những lỗi này, chương trình của chúng ta sẽ chết rất đột ngột.
Ví dụ: OutOfMemoryError, LinkageError, and StackOverflowError.

Xử lý ngoại lệ trong Java

Tại sao cần Try catch

Để bắt được các Exception chúng ta cần sử dụng các khối try-catchfinally.

Cú pháp try catch là gì cho ví dụ code

Khối ‘try-catch’ được sử dụng để thi hành mô hình ‘catch và throw’ của việc xử lý ngoại lệ. Khối ‘try’ chứa một tập lệnh có thể thi hành được. Các ngoại lệ có thể bị chặn khi thi hành tập lệnh này. Phương thức có khả năng tạo ra ngoại lệ có thể được khai báo trong khối ‘try’. Một hay nhiều khối ‘catch’ có thể theo sau một khối ‘try’. Các khối ‘catch’ này bắt các ngoại lệ có khả năng tạo ra trong trong khối ‘try’.

try
    {
    doFileProcessing(); // do người sử dụng định nghĩa
    displayResults();
    }
    catch (Exeption e) // thể hiện của ngoại lệ
    {
    System.err.println(“Error :” + e.toString());
    e.printStackTrace();
    }
    

Ở đây, ‘e’ là đối tượng của lớp ‘Exception’. Chúng ta có thể sử dụng đối tượng này để in các chi tiết về ngoại lệ. Các phương thức ‘toString’ và ‘printStackTrace’ được sử dụng để mô tả các ngoại lệ xảy ra. Hình sau chỉ ra kết xuất của phương thức ‘printStackTrace()’.

12152

Để xử lý được ngoại lệ nào, ta phải chỉ ra kiểu ngoại lệ tương ứng.

catch(Exception e)

Khi ngoại lệ không biết thuộc kiểu nào, chúng ta có thể sử dụng lớp ‘Exception’ để bắt ngoại lệ đó.

Khối ‘catch()’ bắt giữ bất cứ các lỗi xảy ra trong khi thi hành phương thức ‘doFileProcessing’ hay ‘display’. Nếu một lỗi xảy ra trong khi thi hành phương thức ‘doFileProcessing()’, lúc đó phương thức ‘displayResults()’ sẽ không bao giờ được gọi. Chương trình sẽ chuyển đến thực hiện khối ‘catch’. Để có nhiều lớp xử lý lỗi hơn, như là ‘LookupException’ thay vì một đối tượng ngoại lệ chung (Exceptione), lỗi thực tế có thể là một đối tượng thuộc lớp ‘LookupException’ hay một trong số những lớp con của nó. Lỗi sẽ được truyền qua khối ‘try catch’ cho tới khi gặp ‘catch’ của nó, nếu không tìm thấy chương trình phải dừng thực hiện và thoát.

Ví dụ

 Trong ví dụ này có một đoạn code lỗi nguyên nhân do phép chia cho 0. Việc chia cho 0 gây ra ngoại lệ: ArithmeticException

package org.o7planning.tutorial.exception;
 
public class HelloException {
 
 public static void main(String[] args) {
 
 System.out.println("Three");
 
 // Phép chia này hoàn toàn không có vấn đề.
 int value = 10 / 2;
 
 System.out.println("Two");
 
 // Phép chia này cũng vậy
 value = 10 / 1;
 
 System.out.println("One");
 
 // Phép chia này có vấn đề, chia cho 0.
 // Lỗi đã xẩy ra tại đây.
 value = 10 / 0;
 
 // Và dòng code dưới đây sẽ không được thực hiện.
 System.out.println("Let's go!");
 
 }
}

Kết quả chạy ví dụ:
Bạn có thể thấy thông báo lỗi trên màn hình Console, thông báo lỗi rất rõ ràng, xẩy ra ở dòng thứ mấy trên code.

18214

Hãy xem luồng đi của chương trình qua hình minh họa dưới đây.

  • Chương trình đã chạy hoàn toàn bình thường từ các bước (1),(2) cho tới (5)
  • Bước thứ (6) xẩy ra vấn đề khi chia cho 0.
  • Chương trình đã nhẩy ra khỏi hàm main, và dòng code thứ (7) đã không được thực hiện.

18218

Chúng ta sẽ sửa code của ví dụ trên.

package org.o7planning.tutorial.exception;
 
public class HelloCatchException {
 
 public static void main(String[] args) {
 
 System.out.println("Three");
 
 // Phép chia này hoàn toàn không có vấn đề.
 int value = 10 / 2;
 
 System.out.println("Two");
 
 // Phép chia này cũng vậy
 value = 10 / 1;
 
 System.out.println("One");
 
 try {
 // Phép chia này có vấn đề, chia cho 0.
 // Lỗi đã xẩy ra tại đây.
 value = 10 / 0;
 
 // Dòng code này sẽ không được chạy.
 System.out.println("Value =" + value);
 
 } catch (ArithmeticException e) {
 
 // Các dòng lệnh trong catch được thực thi
 System.out.println("Error: " + e.getMessage());
 
 // Các dòng lệnh trong catch được thực thi
 System.out.println("Ignore...");
 
 }
 
 // Dòng lệnh này được thực hiện.
 System.out.println("Let's go!");
 }
}

Và kết quả chạy ví dụ:
18228

throws và throw là gì cho ví dụ code

Trong trường hợp chúng ta không muốn xử lý Exception mà phương thức đã bắt được thì có thể ném nó ra lại cho phương thức gọi nó (caller method) xử lý. Hoặc trong một trường hợp khác là chúng ta đã sử lý nhưng sẽ ném ra một Exception đã customize thì từ khóa throws throw sẽ đảm nhiệm vấn đề này.

Ví dụ: Cũng với ví dụ trên,  sẽ tách nó ra một phương thức mới hoàn toàn và sử dụng từ khóa throws như sau:

package vidu.example;

import java.io.FileWriter;
import java.io.IOException;

public class ExceptionExample {
  public static void main(String[] args) {
    try {
      writeToFile();
    } catch (IOException ioe) {
      System.out.println("Da co loi xay ra khi thuc hien ghi file" + ioe);
    }
  }

  private static void writeToFile() throws IOException {
    FileWriter fileWriter = new FileWriter("data.txt");
    fileWriter.write("Xu ly ngoai le trong Java");
    fileWriter.close();
  }
}

Trong ví dụ tiếp theo,đến từ khóa throw. Khi nào thì chúng ta sử dụng nó? từ khóa throw khi được sử dụng khi bạn muốn ném ra một ngoại lệ đã được xử lý (nói một cách khác là dùng customized Exception) cho lớp đang gọi nó xử lý.

package vidu.example;

import java.io.FileWriter;

public class ExceptionExample {
  public static void main(String[] args) {
    try {
      writeToFile();
    } catch (CustomException ioe) {
      System.out.println(ioe.getMessage());
    }
  }

  private static void writeToFile() throws CustomException {
    try {
      FileWriter fileWriter = new FileWriter("data.txt");
      fileWriter.write("Xu ly ngoai le trong Java");
      fileWriter.close();
    } catch (Exception e) {
      throw new CustomException("Da co loi xay ra khi thuc hien ghi file: " + e.getMessage());
    }
  }
}

finally là gì cho ví dụ

Khối ‘finally’

Khi một ngoại lệ xuất hiện, phương thức đang được thực thi có thể bị dừng mà không được hoàn thành. Nếu điều này xảy ra, thì các đoạn mã phía sau (ví dụ như đoạn mã có chức năng thu hồi tài nguyên, như các lệnh đóng tập viết ở cuối phương thức) sẽ không bao giờ được gọi. Java cung cấp khối ‘finally’ để giải quyết việc này. Khối ‘finally’ thực hiện tất cả các việc thu dọn khi một ngoại lệ xảy ra. Khối này có thể được sử dụng kết hợp với khối ‘try’. Khối ‘finally’ chứa các câu lệnh thu hồi tài nguyên về cho hệ thống hay lệnh in ra các câu thông báo. Các lệnh này bao gồm:

  • Đóng tập tin.
  • Đóng ResultSet (được sử dụng trong chương trình cơ sở dữ liệu).
  • Đóng lại các kết nối được tạo trong cơ sở dữ liệu.
try
 {
 doSomethingThatMightThrowAnException();
 }
 finally
 {
 cleanup();
 }

Phương thức ‘cleanup()’ được gọi nếu phương thức ‘doSomethingThatMightThrowAnException()’ gây ra ngoại lệ. Mặt khác ‘cleanup()’ cũng được gọi ngay khi không có ngoại lệ nào xảy ra và thực hiện tiếp phần sau khối lệnh ‘finally’.

Khối ‘finally’ là tuỳ ý, không bắt buộc. Khối này được đặt sau khối ‘catch’ cuối cùng. Chương trình sẽ thực thi câu lệnh đầu tiên của khối ‘finally’ ngay sau khi gặp câu lệnh ‘return’ hay lệnh ‘break’ trong khối ‘try’.

Khối ‘finally’ bảo đảm lúc nào cũng được thực thi, bất chấp có ngoại lệ xảy ra hay không.

Chương trình dưới đây sẽ sử dụng khối ‘finally’. Ở đây, khối ‘finally’ được thi hành bất chấp ‘ArithmeticException’ có xảy ra hay không. Khối này khai báo các hoạt động thu dọn.

public class JavaFinallyBlock {
    
    public static void main(String[] args) {
        
        System.out.println("-- Case 1 ------------------");
        /**
         * Ngoại lện sẽ xảy ra ở đây, và sau khi khối
         * Catch được thực thi xong thì ứng dụng sẽ 
         * được chuyển tới khối Finally
         */
        /**
         * Exception will occur here, and after Catch block
         * the application will goto finally block.
         */
        try{
            int i = 9/0;
        } catch(Exception ex){
            System.out.println("Inside 1st catch Block");
        } finally {
            System.out.println("Inside 1st finally block");
        }
        
        System.out.println("\n-- Case 2 ------------------");
        /**
         * Trong trường này ngoại lệ sẽ không xảy ra,
         * sau khi khối Try được thực xong thì ứng dụng
         * sẽ chuyển tới khối Finally.
         */
        /**
         * In this case exception won't, after executing try block
         * the application will goto finally block.
         */
        try{
            int i = 9/9;
        } catch(Exception ex){
            System.out.println("Inside 2nd catch Block");
        } finally {
            System.out.println("Inside 2nd finally block");
        }
        
        System.out.println("\n \n \t -- vnlives.net --");
        
    }
}

Tại sao cần có nhiều catch cho ví dụ

Nhiều khối catch trong Java

Nếu bạn phải thực hiện các tác vụ khác nhau mà có thể xảy ra các exception khác nhau, bạn sử dụng nhiều khối catch trong Java. Bạn theo dõi ví dụ đơn giản sau:

public class TestMultiCatchBlock{  
  public static void main(String args[]){  
   try{  
    int a[]=new int[5];  
    a[5]=30/0;  
   }  
   catch(ArithmeticException e){System.out.println("Task1 duoc hoan thanh");}  
   catch(ArrayIndexOutOfBoundsException e){System.out.println("Task2 duoc hoan thanh");}  
   catch(Exception e){System.out.println("Task chung duoc hoan thanh");}  
  
   System.out.println("Phan code con lai...");  
 }  
}  

Chương trình sẽ cho kết quả sau:

Task1 duoc hoan thanh
Phan code con lai... 

Qui tắc 1: Tại một thời điểm, chỉ một exception được xuất hiện và tại một thời điểm chỉ có một khối catch được thực thi.

Qui tắc 2: Tất cả khối catch phải được sắp xếp từ cụ thể nhất tới chung nhất, ví dụ: việc bắt ArithimeticExption phải ở trước việc bắt Exception.

class TestMultipleCatchBlock1{  
  public static void main(String args[]){  
   try{  
    int a[]=new int[5];  
    a[5]=30/0;  
   }  
   catch(Exception e){System.out.println("Task chung duoc hoan thanh");}  
   catch(ArithmeticException e){System.out.println("Task1 duoc hoan thanh");}  
   catch(ArrayIndexOutOfBoundsException e){System.out.println("Task2 duoc hoan thanh");}  
   System.out.println("Phan code con lai...");  
 }  
}  

Chạy chương trình trên sẽ cho một lỗi compile time error.

Share this:

Thích bài này:

Thích

Đang tải…

Lên trên