Khối Lệnh finally (The Finally Block)
Khối lệnh finally trong Java là một khối mã đặc biệt luôn được thực thi, bất kể có ngoại lệ xảy ra trong khối try hay không, trừ khi chương trình bị chấm dứt đột ngột (ví dụ: lỗi hệ thống nghiêm trọng). Nó thường được sử dụng để thực hiện các tác vụ dọn dẹp như đóng kết nối, đóng file, giải phóng tài nguyên, v.v.
Lưu ý:
↳ Khối finally không được thực thi nếu chương trình bị chấm dứt đột ngột (ví dụ: lỗi hệ thống nghiêm trọng).
↳ Nếu có câu lệnh return trong khối try hoặc catch, khối finally sẽ vẫn được thực thi trước khi trả về.
↳ Ngay cả khi không có khối catch nào xử lý ngoại lệ, hoặc ngoại lệ được ném ra mà không được bắt, khối finally vẫn sẽ được thực thi trước khi chương trình kết thúc (trừ khi có một lỗi hệ thống nghiêm trọng khiến JVM phải dừng đột ngột).

Tại sao sử dụng khối finally:
↳ Đảm bảo giải phóng tài nguyên như đóng file, kết nối mạng.
↳ Thực hiện các tác vụ dọn dẹp sau khi xử lý ngoại lệ hoặc không có ngoại lệ.
↳ Tránh rò rỉ tài nguyên.
Cách sử dụng khối finally trong java
Trường hợp 1: Sử dụng khối lệnh finally trong trường hợp ngoại lệ không xảy ra.
Khi không có ngoại lệ xảy ra trong khối try, các câu lệnh trong khối try sẽ được thực thi bình thường và khối finally sẽ luôn được thực thi.
Ví dụ: Example.java
public class Example {
public static void main(String[] args) {
try {
System.out.println("Bắt đầu khối try");
// Không có ngoại lệ xảy ra
System.out.println("Kết thúc khối try");
} catch (Exception e) {
System.out.println("Khối catch");
} finally {
System.out.println("Khối finally");
}
System.out.println("Chương trình tiếp tục chạy");
}
}
Kết quả của chương trình là:
Kết thúc khối try
Khối finally
Chương trình tiếp tục chạy
Trường hợp 2: Sử dụng khối lệnh finally trong trường hợp ngoại lệ xảy ra nhưng không được khối catch xử lý.
Khi ngoại lệ xảy ra nhưng không được xử lý bởi khối catch, chương trình sẽ kết thúc đột ngột sau khi khối finally được thực thi.
Ví dụ: Example.java
public class Example {
public static void main(String[] args) {
try {
System.out.println("Bắt đầu khối try");
int result = 10 / 0; // Gây ra ArithmeticException
System.out.println("Kết thúc khối try");
} catch (NullPointerException e) {
System.out.println("Khối catch");
} finally {
System.out.println("Khối finally");
}
System.out.println("Chương trình tiếp tục chạy");
}
}
Kết quả của chương trình là:
Khối finally
Exception in thread "main" java.lang.ArithmeticException: / by zero at Example.main(Example.java:5)
Trường hợp 3: Sử dụng khối lệnh finally trong trường hợp ngoại lệ xảy ra và được khối catch xử lý.
Khi ngoại lệ xảy ra và được xử lý bởi khối catch, khối finally sẽ được thực thi sau khối catch.
Ví dụ: Example.java
public class Example {
public static void main(String[] args) {
try {
System.out.println("Bắt đầu khối try");
int result = 10 / 0; // Gây ra ArithmeticException
System.out.println("Kết thúc khối try");
} catch (ArithmeticException e) {
System.out.println("Khối catch: " + e.getMessage());
} finally {
System.out.println("Khối finally");
}
System.out.println("Chương trình tiếp tục chạy");
}
}
Kết quả của chương trình là:
Khối catch: / by zero
Khối finally
Chương trình tiếp tục chạy
Trường hợp 4: Sử dụng khối lệnh finally trong trường hợp trong khối try có lệnh return.
Khi có lệnh return trong khối try, khối finally sẽ vẫn được thực thi trước khi phương thức trả về giá trị.
Ví dụ: Example.java
public class Example {
public static void main(String[] args) {
System.out.println("Giá trị trả về: " + testMethod());
}
public static int testMethod() {
try {
System.out.println("Bắt đầu khối try");
return 10;
} catch (Exception e) {
System.out.println("Khối catch");
} finally {
System.out.println("Khối finally");
}
return 0;
}
}
Kết quả của chương trình là:
Khối finally
Giá trị trả về: 10
Trường hợp đặc biệt với System.exit() và ngoại lệ trong finally
1. System.exit() và khối finally
Khi gọi System.exit() trong khối try hoặc catch, chương trình sẽ kết thúc ngay lập tức, và khối finally sẽ không được thực thi. System.exit() yêu cầu JVM kết thúc ngay lập tức, bỏ qua tất cả các khối finally.
Ví dụ: Example.java
public class Example {
public static void main(String[] args) {
try {
System.out.println("Bắt đầu khối try");
System.exit(0); // Kết thúc chương trình ngay lập tức
} finally {
System.out.println("Khối finally sẽ không được thực thi");
}
System.out.println("Dòng này sẽ không được thực thi");
}
}
Kết quả của chương trình là:
Chương trình kết thúc ngay sau lệnh System.exit(0), vì vậy khối finally và dòng cuối cùng không được thực thi.
2. Ngoại lệ trong khối finally
Nếu có ngoại lệ được ném ra trong khối finally, nó sẽ che khuất (suppress) bất kỳ ngoại lệ nào đã được ném ra trong khối try hoặc catch. Điều này có nghĩa là ngoại lệ trong khối finally sẽ là ngoại lệ chính mà bạn thấy.
Ví dụ: Example.java
public class Example {
public static void main(String[] args) {
try {
System.out.println("Bắt đầu khối try");
int result = 10 / 0; // Gây ra ArithmeticException
} catch (ArithmeticException e) {
System.out.println("Ngoại lệ: Chia cho 0");
} finally {
System.out.println("Khối finally được thực thi");
int a = 10 / 0; // Gây ra ngoại lệ trong khối finally
}
}
}
Kết quả của chương trình là:
Ngoại lệ: Chia cho 0
Khối finally được thực thi
Exception in thread "main" java.lang.ArithmeticException: / by zero at Example.main(Example.java:10)
Ngoại lệ trong finally: Ngoại lệ trong khối finally sẽ che khuất các ngoại lệ từ khối try hoặc catch, khiến nó trở thành ngoại lệ chính được ném ra.