Lỗi ioexcrption dùng try và catch trong java năm 2024

Chào mừng các bạn đã đến với bài học Java số 39, bài học về Exception (phần tiếp theo). Đây là bài học trong chuỗi bài về lập trình ngôn ngữ Java của Yellow Code Books.

Thông qua hai bài viết về Exception, bạn đã phần nào yên tâm hơn cho “số phận” của các dòng code mà bạn tạo ra rồi đúng không nào. Sự thật là với việc sử dụng tốt try catch mà hai bài học trước mình đã nói rất kỹ, thì có thể nói ứng dụng của bạn sẽ trở nên rất mạnh mẽ, an toàn, và cũng khá thông minh khi có thể thông báo kịp thời các trường hợp lỗi cho user.

Tuy nhiên, nếu đã nói về Exception thì phải nói cho tới. Đừng lấp lửng nửa vời mà lỡ mất các chức năng đặc sắc mà công cụ này mang đến. Hôm nay chúng ta tiếp tục đào sâu về Exception khi nói về try catch với finally, try catch với resource, và các phương thức hữu ích của lớp Exception.

Try Catch Với Finally

Mình nhắc lại một chút từ các bài học trước, rằng khi làm quen với cách thức bắt Exception thông qua try catch, thì try giúp bao bọc đoạn code có khả năng gây ra lỗi, còn catch sẽ giúp xử lý “hậu quả” khi mà code trên đã chính thức bị lỗi.

Tuy nhiên nhiêu đó chưa đủ. Trong Java, đôi khi chúng ta có sử dụng đến một số tài nguyên của hệ thống. Tài nguyên này chính là các resource có khả năng dùng chung giữa các ứng dụng. Đó có thể đơn giản chỉ là một file nào đó. Và bởi vì đây là các resource dùng chung, nên nếu có một tình huống khi ứng dụng của bạn đang mở một resource ra xem, nhưng chẳng may lại gây ra một Exception nào đó trong quá trình này, khi đó ứng dụng thông báo lỗi, và… xong… vô tâm không đóng lại cái resource ấy. Trường hợp này, hệ thống đang tưởng rằng resource vẫn được quản lý bởi ứng dụng của bạn, nhưng thực chất là ứng dụng của bạn đã chết từ lâu rồi.

Qua đó bạn có thể thấy rằng, trong lập trình bạn không nên để ứng dụng nắm giữ bất kỳ tài nguyên nào cho riêng bạn, bạn nên đóng nó lại sau khi dùng xong. Bạn sẽ hiểu rõ điều này khi làm quen với kiến thức về Input/Output ở các bài tiếp theo. Từ tình huống trên mà finally ra đời. Finally sẽ giúp chúng ta dọn dẹp các tàn dư từ try catch để lại, trong bài học hôm nay nhiệm vụ chính của nó là đóng các tài nguyên lại khi có Exception xảy ra. Trước hết mời bạn xem cú pháp sau.

try { // Các dòng code có khả năng gây ra Exceptipn } catch (ExceptionClass1 e1) { // Bắt các EceptionClass1 } catch (ExceptionClass2 e2) { // Bắt các EceptionClass2 } catch (ExceptionClass3 e3) { // Bắt các EceptionClass3 } finally { // Giải phóng các tài nguyên đang sử dụng } Qua cú pháp trên, bạn có thể thấy finally sẽ đi kèm với try catch. Nếu không có try catch sẽ không có finally. Nhưng dù try có đi kèm với bao nhiêu catch đi chăng nữa, thì chỉ cần có một finally để dọn dẹp những gì bạn đã dùng là đủ.

Dưới đây là một vài ý liên quan đến finally, mình hi vọng các mục này sẽ giúp bạn có cái nhìn rõ nhất về finally này.

Chỉ Cần Có Try – Finally (Mà Không Cần Catch) Cũng Được

Bạn có thể hiểu rằng finally trong trường hợp này được dùng để dọn dẹp cái gì đó mà không nhất thiết phải có Exception xảy ra. Vì nhu cầu sử dụng finally khi này rất ít nên ví dụ dưới đây của mình sẽ không có tính thực tế lắm, chủ yếu giúp bạn hiểu thôi.

try { System.out.println("Bài toán thực hiện phép chia:"); int ketQua = 10 / 2; System.out.println("10 chia 2 bằng: " + ketQua); } finally { System.out.println("Kết thúc chương trình"); } Chắc bạn cũng đoán biết kết quả rồi. Rằng cả 3 dòng in ra console ở trên đều thực hiện một cách trôi chảy.

Lỗi ioexcrption dùng try và catch trong java năm 2024

Bạn Có Thể Không Cần Đến Finally, Nhưng Đã Có Finally Thì Code Trong Finally Luôn Được Gọi Đến Cuối Cùng

Ví dụ về try catch không cần đến finally thì bạn đã biết thông qua bài học trước. Nhưng nếu bạn có khai báo finally cho try catch, thì các câu lệnh trong khối finally sẽ được thực thi cuối cùng, sau khi ứng dụng đã thực thi các câu lệnh không gây ra Exception ở try, rồi đến các câu lệnh trong khối catch tương ứng nếu có, cuối cùng mới đến finally.

try { System.out.println("Bài toán thực hiện phép chia:"); int ketQua = 10 / 0; System.out.println("10 chia 0 bằng: " + ketQua); } catch (ArithmeticException e) { System.out.println("Phép chia không thực hiện được"); } finally { System.out.println("Kết thúc chương trình"); } Với các dòng code này thì console sẽ in ra như sau. Và có lẽ mình cũng không cần giải thích gì nhiều.

Lỗi ioexcrption dùng try và catch trong java năm 2024

Trong Finally Vẫn Có Thể Có Try Catch Trong Đó

Nếu như trong try hay catch ta đều có thể lồng thêm try catch vào. Thì finally cũng cho phép như thế. Mặc dù trông có vẻ phức tạp và hơi thiếu an toàn, nhưng đời không có gì hoàn hảo cả, đã bắt lỗi rồi vẫn có thể xảy ra lỗi ở quá trình bắt lỗi là chuyện thường tình.

Bạn sẽ được làm quen với finally và cả try catch bên trong finally qua bài thực hành dưới đây.

Thực Hành Xây Dựng Try Catch Với Finally

Bài thực hành này giúp chúng ta làm quen với việc sử dụng try catch và finally một cách thực tế hơn. Chúng ta sẽ kế thừa code từ .

Vì bài thực hành hôm trước chúng ta chỉ mới làm cách nào để bắt Exception hiệu quả thôi. Thực tế bạn đang sử dụng FileOutputStream để mở một file từ hệ thống và thao tác với file đó. File này chính là tài nguyên có khả năng dùng chung bởi các chương trình khác. Như mình có nói, nếu bạn sử dụng file này mà lỡ có chuyện gì xảy ra mà bạn không giải phóng việc sở hữu đó, thì khi đó bạn đang “ích kỷ” chỉ giữ file đó cho riêng bạn, các ứng dụng khác không thể đọc được file này.

Đó là lý do vì sao chúng ta cần xây dựng finally để giải phóng cái tài nguyên này như sau.

Đầu tiên, chúng ta cần phải code các dòng có gây ra như sau.

FileOutputStream outputStream = null; outputStream = new FileOutputStream("E://file.txt"); outputStream.write(65); outputStream.close(); Và như với việc thực hành ở bài trước, bạn đã biết cách thức dễ nhất để hệ thống tự thêm vào các try catch cho các dòng đang bị báo lỗi. Sau khi áp dụng các sự gợi ý từ Eclipse (xem ở bài trước), code của chúng ta trông khá là đẹp như sau.

FileOutputStream outputStream = null; try { outputStream = new FileOutputStream("E://file.txt"); outputStream.write(65); outputStream.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } Vấn đề của code trên đây là gì? Bạn biết rằng file có cái tên file.txt được xem như một tài nguyên dùng chung. Có nghĩa là trong lúc bạn mở file này ra để ghi vào giá trị 65, trong lúc đó, có thể có một chương trình khác cũng đang muốn thao tác với file này. Và vấn đề xảy ra là, ngỡ như trong quá trình bạn ghi vào file mà có bất cứ lỗi nào xảy ra, khiến cho chương trình bị bẻ luồng thực thi vào các dòng code bên trong khối catch bên dưới. Thì câu lệnh outputStream.close() sẽ không có cơ hội thực thi. File mà bạn thao tác sẽ vẫn được hệ thống xem là đang được sử dụng. Và như vậy ứng dụng khác phải chờ hoài mà không thấy ứng dụng của bạn trả lại quyền sử dụng file này.

Các vấn đề thao tác với file chúng ta sẽ làm quen ở các bài học sau. Còn bây giờ, hãy đảm bảo rằng file mà bạn đang dùng luôn được đóng lại một cách an toàn. Trả lại quyền sử dụng tài nguyên cho các chương trình khác. Thật đơn giản, chúng ta cùng thêm finally và đoạn code đóng file này lại trong khối này là xong. Như hình sau.

Lỗi ioexcrption dùng try và catch trong java năm 2024

Ồ nhưng nhìn xem, code để vào trong finally vẫn thuộc thẩm quyền của Checked Exception. Rất tốt, chúng ta cũng nên cần một lần try catch nữa. Code cuối cùng sẽ như sau.

FileOutputStream outputStream = null; try { outputStream = new FileOutputStream("E://file.txt"); outputStream.write(65); outputStream.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try {

outputStream.close();  
} catch (IOException e) {
// TODO Auto-generated catch block  
e.printStackTrace();  
} }

Try Catch Với Resource

Kiểu try catch này xuất hiện từ Java 7. Và đây là một kiểu nâng cấp so với try catch với finally mà bạn vừa làm quen trên kia.

Cụ thể là, thông qua bài thực hành trên đây, bạn đã thấy tầm quan trọng khi giải phóng các tài nguyên của hệ thống rồi đúng không nào. Và bạn cũng đã thấy cách đóng file lại trong khối finally rồi đó. Nhưng nếu chú ý kỹ bài thực hành trên thì, chúng ta thấy khối finally sẽ được gọi đến dù cho câu lệnh khởi tạo FileInputStream có thực hiện hoàn hảo hay không, do đó việc đóng file trong khối finally hoàn toàn có thể gây ra một Exception khác. Và cũng vì lý do này mà chúng ta mới có thêm try catch bên trong finally.

Nhưng việc thực hiện thủ công finally và try catch bên trong finally như ví dụ trên cũng có cái dở, là nếu có Exception xảy ra với các code trong try và cả với code trong finally, thì các dòng lỗi in ra sẽ loạn cả lên.

Chính vì vậy mà Java 7 đã cho chúng ta một công cụ mới với cái tên try catch với resource (Try With Resources). Cú pháp của nó như sau.

try(// Khởi tạo các tài nguyên) { // Sử dụng các tài nguyên đã khởi tạo } catch (ExceptionClass1 e1) { // Bắt các EceptionClass1 } catch (ExceptionClass2 e2) { // Bắt các EceptionClass2 } catch (ExceptionClass3 e3) { // Bắt các EceptionClass3 } Như bạn thấy, cách try catch này không còn đi kèm với finally nữa. Vì các tài nguyên được khởi tạo bên trong cặp ngoặc đơn của try như trên giờ đây đã có thể tự biết cách đóng lại khi kết thúc khối lệnh try.

Trước khi đi vào bài thực hành chi tiết, chúng ta xem qua một vài ý chính của try catch với resource để giúp chúng ta hiểu rõ hơn.

Không Phải Tài Nguyên Nào Cũng Dùng Được Trong Try Catch Với Resource

Chỉ có các đối tượng có hiện thực interface java.lang.AutoCloseable, như FileOutputStream ở ví dụ trên kia, mới có thể dùng trong try catch với resource.

Trong Quá Trình Đóng Các Tài Nguyên Hệ Thống, Nếu Xảy Ra Lỗi Thì Sẽ Không Có Exception Của Quá Trình Này