1. Khối finally trong Java là gì?

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. Vì vậy Java cung cấp khối finally để giải quyết việc này. Khối lệnh finally được sử dụng để thực thi các lệnh quan trọng như đóng kết nối, đóng các stream,…(câu lệnh dọn dẹp).  Khối lệnh này luôn được thực thi cho dù có ngoại lệ xảy ra hay không hoặc gặp lệnh return trong khối try .

Khối finally luôn luôn được thực thi khi khối try thoát. Điều này được đảm bảo rằng khối này được thực hiện ngay cả khi một ngoại lệ bất ngờ xảy ra. Nhưng finally hữu ích hơn nhiều so với việc ta chỉ xử lý ngoại lệ. Vì nó cho phép ta tránh được việc mã dọn dẹp vô tình bị bỏ qua bởi một lệnh return , continue hay break . Việc đặt mã dọn dẹp vào khối finally luôn là một việc làm tốt ngay cả khi ngoại lệ không được bắt.

Chú ý

Nếu ta không xử lý ngoại lệ trước khi kết thúc chương trình, JVM sẽ thực thi khối finally (nếu ta không có).

Trong trường hợp JVM thoát khi đang thực hiện try hay catch thì khối finally sẽ không được thực thi.

2. Cấu trúc khối lệnh Finally trong Java

Khối finally này là tùy chọn không bắt buộc phải có. Nó đượ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 cả sau khi gặp câu lệnh return hay lệnh break trong khối try . Ta có cấu trúc của khối lệnh finally trong Java như sau:

try{
  //Các lệnh có khả năng ném ra ngoại lệ
} catch(Exception1 ex1){
…
} catch(Exception2 ex2){
…
} catch(Exceptionn exn){
…
} finally{
//Mã lệnh dọn dẹp
}

Ví dụ:

public class Main {
  public static void main(String[] args) {
    try {
      int[] myNumbers = {1, 2, 3};
      System.out.println(myNumbers[10]);
    } catch (Exception e) {
      System.out.println("Đã xảy ra lỗi");
    } finally {
      System.out.println("Quá trình 'try catch' đã kết thúc.");
    }
  }
}

Kết quả

Đã xảy ra lỗi
Quá trình 'try catch' đã kết thúc.

Ta có thể dễ dàng thấy rằng exception xảy ra trong try block đã được xử lý trong catch block, sau đó đến finally block được thực thi.

Một ví dụ khác:

class Main
{
   public static void main(String args[]) {
      try{  
     int num=121/0;  
     System.out.println(num);  
      }  
      catch(ArithmeticException e){
         System.out.println("Lỗi: không thể chia cho số 0");
      }  
      // Finally block sẽ luôn được thực thi dù có exception hay không 
      finally{
     System.out.println("finally block");
      }  
      System.out.println("Ra khỏi try-catch-finally");  
   }   
}

Kết quả

Lỗi: không thể chia cho số 0
finally block
Ra khỏi try-catch-finally

Ngoài ra ta cũng có thể sử dụng khối lệnh finally mà không sử dụng catch block

InputStream input = null;
try {
    input = new FileInputStream("inputfile.txt");
} 
finally {
    if (input != null) {
       try {
         in.close();
       }catch (IOException exp) {
           System.out.println(exp);
        }
    }
}

 

Lưu ý

Finally block phải được dùng chung với với try block, ta không thể sử dụng nó mà không có try block.

Với một khối try thì có thể có 0 hoặc nhiều khối catch , nhưng chỉ có một khối finally .

Trong trường hợp bình thường khi không có exception trong try block thì finally block được thực thi sau try block. Tuy nhiên, nếu một exception xảy ra thì catch block được thực thi trước finally block.

Một exception trong finally block có thể chạy như bất kỳ exception nào khác bên ngoài nó.

3. Sử dụng finally trong Java

Khối lệnh finally trong Java có thể được sử dụng trong các trường hợp khác nhau. Tùy vào bài toán đặt ra hay tùy vào những trường hợp mà ta phải sử dụng nó. Dưới đây sẽ là một số trường hợp sử dụng finally trong Java

3.1. Sử dụng finally khi ngoại lệ không xảy ra

Khi ta chạy chương trình mà không có bất kể ngoại lệ nào xảy ra thì khối lệnh finally vẫn được thực thi sau khối try . Khối lệnh trong catch block sẽ được bỏ qua và tiến thẳng đến finally block sau đó thực thi phần còn lại của method.

Ví dụ:

public class Main{
    public static void main(String args[]) {
        try {
        // mã bên dưới không ném bất kỳ ngoại lệ nào
            int data = 10 / 2 ;
            System.out.println(data);
        // catch sẽ không được thực thi
        } catch (NullPointerException e) {
            System.out.println(e);
        // được thực thi bất kể ngoại lệ có xảy ra hay không
        } finally {
            System.out.println("finally block");
        }
        System.out.println("laptrinhtudau.com");
    }
}

Kết quả

5
finally block
laptrinhtudau.com

3.2. Sử dụng finally khi ngoại lệ xảy ra nhưng không được xử lý

Ở trường hợp này khi ta ném ra một ngoại lệ tuy nhiên thì khối catch không xử lý nó. Tuy nhiên khối lệnh finally vẫn được thực thi sau khối try và chương trình sẽ kết thúc bất thường.

Ví dụ:

public class Main{    
      public static void main(String args[]){    
      try {    
  
        System.out.println("Bên trong khối try");  
          
       //ngoại lệ khi cho 0 được ném ra
       int data=10/0;    
       System.out.println(data);    
      }    
      //không thể xử lý ngoại lệ kiểu Số học
      //chỉ có thể chấp nhận ngoại lệ kiểu Null Pointer
      catch(NullPointerException e){  
        System.out.println(e);  
      }   
  
      //thực thi bất kể ngoại lệ có xảy ra hay không
      finally {  
        System.out.println("finally block");  
      }    
  
      System.out.println("laptrinhtudau.com");    
      }    
    }

Kết quả

Bên trong khối try
finally block
Exception in thread "main" java.lang.ArithmeticException: / by zero
    at Main.main(Main.java:9)

3.3. Sử dụng finally khi ngoại lệ xảy ra và được xử lý

Nếu ngoại lệ xảy ra bên trong try block thì luồng xử lý sẽ được chuyển tiếp cho catch block sau đó chuyển qua finally block và thực thi phần còn lại của method. Phần còn lại của chương trình cũng kết thúc một cách bình thường.

Ví dụ:

class Main {
    public static void main(String args[]) {
        int x = 0;
        int y = 10;
        try {
            int num = y / x;
            return;
        } catch (Exception ex) {
            System.out.println("catch block");
        } finally {
            System.out.println("finally block");
        }
        System.out.println("laptrinhtudau.com");
    }
}

Kết quả

catch block
finally block
laptrinhtudau.com

3.4. Sử dụng finally và return trong try block

Dù trong try block có sử dụng return thì khối lệnh finally bên dưới vẫn được sử dụng và chạy hoàn toàn bình thường.

Ví dụ:

class Main
{
   public static void main(String args[])
   {
      System.out.println(Main.myMethod());  
   }
   public static int myMethod()
   {
      try {
        return 112;
      }
      finally {
        System.out.println("finally block");
        System.out.println("Finally block vẫn chạy mặc dù lệnh return trong try block đã được thực thi trước");
      }
   }
}

Kết quả

finally block
Finally block vẫn chạy mặc dù lệnh return trong try block đã được thực thi trước
112

3.5. Sử dụng finally và System.exit()

Câu lệnh System.exit() hoạt động khác với câu lệnh return . Không giống như return , bất cứ khi nào System.exit() được gọi trong try block thì finally block sẽ không thực hiện.

Ví dụ:

class Main
{
   public static void main(String args[]){
      try {
      //try block
        System.out.println("Bên trong try block");
        System.exit(0);
      }
      catch (Exception exp) {
        System.out.println(exp);
      }
      finally {
        System.out.println("Bên trong finally block");
      }    
   }
}

Kết quả

Bên trong try block

Trong ví dụ trên nếu System.exit(0) được gọi mà không có exception nào thì finally sẽ không được thực thi. Tuy nhiên, nếu có bất kỳ exception nào xảy ra trong khi gọi System.exit(0) thì finally block sẽ được thực thi.

4. So sánh final và finally

Ta có thể so sánh finally và final thông qua bảng sau:

Finally Final
Là một khối Là một từ khóa
Final được sử dụng để áp dụng các ràng buộc trên lớp, phương thức và biến. Lớp final không thể được kế thừa, phương thức final không thể bị ghi đè, và giá trị biến final không thể bị thay đổi Finally được sử dụng để đặt các code quan trọng, nó sẽ được thực thi dù exception có được xử lý hay không