Java: Cách sử dụng try-catch-finally
Giải phóng thời gian, khai phóng năng lực
Những bài viết trước đã mô tả cách xây dựng các khối mã try
, catch
và finally
cho phương thức writeList
trong lớp ListOfNumbers
. Bây giờ, ta hãy đi bộ qua khối mã và xem những gì có thể xảy ra.
Khi tất cả các thành phần được đặt lại với nhau thì phương thức writeList
trông giống như sau:
public void writeList() {
PrintWriter out = null;
try {
System.out.println("Đang ở trong khối try");
out = new PrintWriter(new FileWriter("OutFile.txt"));
for (int i = 0; i < SIZE; i++) {
out.println("Giá trị tại: " + i + " = " + list.get(i));
}
} catch (IndexOutOfBoundsException e) {
System.err.println("Đã bắt IndexOutOfBoundsException: " + e.getMessage());
} catch (IOException e) {
System.err.println("Đã bắt IOException: " + e.getMessage());
} finally {
if (out != null) {
System.out.println("Đang đóng PrintWriter");
out.close();
}
else {
System.out.println("PrintWriter không mở");
}
}
}
Như đã đề cập, khối try
của phương thức này có ba khả năng thoát khác nhau, dưới đây là hai trong số đó.
- Mã lệnh trong khối
try
thất bại và ném một ngoại lệ, ngoại lệ này có thể là mộtIOException
gây ra bởi câu lệnhnew FileWriter
hoặc một ngoại lệIndexOutOfBoundsException
gây ra bởi một giá trị chỉ số sai trong vòng lặpfor
. - Tất cả đều thành công và câu lệnh
try
thoát bình thường.
Ta hãy xem điều gì sẽ xảy ra trong phương thức writeList
với hai khả năng thoát trên.
Kịch bản 1: Xảy ra một ngoại lệ
Câu lệnh tạo một FileWriter
có thể thất bại vì một số lý do. Ví dụ, hàm tạo cho FileWriter
ném một IOException
nếu chương trình không thể tạo hoặc ghi vào tập tin được chỉ định.
Khi FileWriter
ném một IOException
, hệ thống thời gian chạy ngay lập tức dừng thực hiện khối try
; các lời gọi phương thức đang được thực thi sẽ không được hoàn thành. Hệ thống thời gian chạy sau đó bắt đầu tìm kiếm phương thức ở trên cùng cho một trình xử lý ngoại lệ thích hợp. Trong ví dụ này, khi IOException
xảy ra thì hàm tạo FileWriter
sẽ nằm ở đầu của call stack. Tuy nhiên, hàm tạo FileWriter
không có một trình xử lý ngoại lệ nào thích hợp, vì vậy hệ thống thời gian chạy sẽ kiểm tra phương thức tiếp theo - phương thức writeList
- trong call stack. Phương thức writeList
có hai trình xử lý ngoại lệ: một cho IOException
và một cho IndexOutOfBoundsException
.
Hệ thống thời gian chạy kiểm tra các trình xử lý của writeList
theo thứ tự mà chúng xuất hiện sau câu lệnh try
. Tham số tới trình xử lý ngoại lệ đầu tiên là IndexOutOfBoundsException
. Điều này không phù hợp với kiểu của ngoại lệ đã ném, vì vậy hệ thống thời gian chạy kiểm tra trình xử lý ngoại lệ tiếp theo - IOException
. Trình xử lý này phù hợp với loại ngoại lệ được ném ra, vì vậy hệ thống runtime kết thúc tìm kiếm của mình cho một trình xử lý ngoại lệ thích hợp. Bây giờ thời gian chạy đã tìm thấy một trình xử lý thích hợp, mã lệnh trong khối catch
sẽ được thực thi.
Sau khi trình xử lý ngoại lệ được thực thi, hệ thống runtime sẽ chuyển quyền điều khiển đến khối finally
. Mã lệnh trong finally
được thực thi bất kể các ngoại lệ có được bắt hay không. Trong kịch bản này, FileWriter
không bao giờ được mở và vì vậy không cần phải đóng. Sau khi khối finally
kết thúc thực hiện, chương trình tiếp tục với câu lệnh đầu tiên sau khối finally
.
Đây là đầu ra hoàn chỉnh từ chương trình ListOfNumbers
xuất hiện khi một IOException
được ném ra.
Đang ở trong khối try
Đã bắt IOException: OutFile.txt
PrintWriter không mở
Các câu lênh in đậm dưới đây là những câu lệnh được thực thi trong kịch bản này:
public void writeList() {
PrintWriter out = null;
try {
System.out.println("Đang ở trong khối try");
out = new PrintWriter(new FileWriter("OutFile.txt"));
for (int i = 0; i < SIZE; i++)
out.println("Giá trị tại: " + i + " = " + list.get(i));
} catch (IndexOutOfBoundsException e) {
System.err.println("Đã bắt IndexOutOfBoundsException: " + e.getMessage());
} catch (IOException e) {
System.err.println("Caught IOException: " + e.getMessage());
} finally {
if (out != null) {
System.out.println("Đang đóng PrintWriter");
out.close();
}
else {
System.out.println("PrintWriter không mở");
}
}
}
Kịch bản 2: Khối try thoát bình thường
Trong kịch bản này, tất cả các câu lệnh trong phạm vi của khối try
thực hiện thành công và không ném ngoại lệ nào. Sự thực thi bị lỗi ở phía cuối của khối try
, và hệ thống thời gian chạy sẽ chuyển quyền điều khiển đến khối finally
. Do mọi thứ đã thành công, nên PrintWriter
sẽ được mở khi quyền điều khiển được chuyển đến khối finally
để đóng PrintWriter
. Một lần nữa, sau khi khối finally
kết thúc thực hiện thì chương sẽ tiếp tục với câu lệnh đầu tiên sau khối finally
.
Đây là kết quả từ chương trình ListOfNumbers
khi không có ngoại lệ nào được ném ra:
Đang ở trong khối try
Đang đóng PrintWriter
Các câu lệnh bôi đen trong ví dụ sau là các câu lệnh được thực thi trong kịch bản này:
public void writeList() {
PrintWriter out = null;
try {
System.out.println("Đang ở trong khối try");
out = new PrintWriter(new FileWriter("OutFile.txt"));
for (int i = 0; i < SIZE; i++)
out.println("Giá trị tại: " + i + " = " + list.get(i));
} catch (IndexOutOfBoundsException e) {
System.err.println("Đã bắt IndexOutOfBoundsException: " + e.getMessage());
} catch (IOException e) {
System.err.println("Đã bắt IOException: " + e.getMessage());
} finally {
if (out != null) {
System.out.println("Đang đóng PrintWriter");
out.close();
}
else {
System.out.println("PrintWriter không mở");
}
}
}
Giải phóng thời gian, khai phóng năng lực