markdown:
Nội dung bài viết
[Làm Quen Với Try Catch](#Lam_Quen_Voi_Try_Catch) [Một Số Quy Tắc Với Try Catch](#Mot_So_Quy_Tac_Voi_Try_Catch) [Try Với Nhiều Catch](#Try_Voi_Nhieu_Catch)
Chào mừng các bạn đã đến với bài học Java số 38, bài học về Exception (phần tiếp theo). Đây là bài học trong
[ của Yellow Code Books. chuỗi bài về lập trình ngôn ngữ Java](https://yellowcodebooks.com/category/lap-trinh-java/bai-hoc-theo-chuong-trinh-lap-trinh-java/)
Ở bài hôm trước các bạn đã làm quen với khái niệm
*Exception*, và thử tạo một vài *Exception* để cùng *“xem chơi”*. Tuy nhiên, cái chính mà *Exception* ra đời không phải để chúng ta chỉ đứng nhìn một cách *“bất lực”* như vậy đâu. Chúng ta hoàn toàn có thể đối mặt với *Exception*, bằng cách bắt lấy chúng, chặn chúng lại không để chương trình bị lỗi, và làm những gì chúng ta mong muốn, như bài trước mình có dùng từ *“bẻ luồng”*.
Như vậy, phần thứ hai của chuỗi bài về
*Exception* hôm nay chúng ta nói đến các kỹ thuật để bắt các *Exception* này.
Bạn có thể xem lại kiến thức giới thiệu về
*Exception* ở [ này. phần đầu tiên](https://yellowcodebooks.com/2018/01/25/java-bai-37-exception-tap-1-lam-quen-voi-exception/)
# Làm Quen Với Try Catch
Như phần đầu bài học có giới thiệu, hôm nay chúng ta tìm cách
*, hay một số tài liệu gọi là bắt Exception* *“bẫy lỗi”*.
Và tất nhiên, làm gì cũng vậy, nếu muốn chúng ta làm một điều gì đó, phải cung cấp cho chúng ta công cụ để thực hiện. Công cụ bẫy được cái
*Exception* mà chúng đa đang nói tới có cái tên *. Try Catch* *Try catch* là một công cụ giúp bạn bao bọc lấy đoạn code có khả năng xảy ra *Exception*. Hoặc các đoạn code đã được báo lỗi bởi hệ thống (chính là các [). Các đoạn code mình nói đến này sẽ được chúng ta bao trong khối Checked Exception](https://yellowcodebooks.com/2018/01/25/java-bai-37-exception-tap-1-lam-quen-voi-exception/#Checked_Exception) *. Để rồi nếu quả thực cái* **try** *Exception*đó xảy ra trong khối *đó, thì hệ thống sẽ nhanh chóng* **try** *“bẻ”*luồng logic của ứng dụng, chuyển sang thực thi các đoạn code bên trong khối *.* **catch**
Dựa vào lý giải trên đây, mời bạn cùng xem qua cú pháp cho
* như sau. try catch*
try { // Các dòng code có khả năng gây ra Exception } catch (ExceptionClass e) { // Nếu thực sực Exception xảy ra, các dòng code này sẽ được gọi }
Để dễ hiểu hơn, mời bạn cùng
* thử cho một trường hợp có khả năng xảy ra lỗi mà bài học trước bạn đã biết thông qua bài thực hành sau. try catch*
## Thực Hành Xây Dựng Try Catch
Chúng ta cùng xem loại đoạn code có khả năng gây ra
*Exception*, bạn có thể xem lại chúng ở [. Còn đây mình sẽ viết chúng lại. mục này](https://yellowcodebooks.com/2018/01/25/java-bai-37-exception-tap-1-lam-quen-voi-exception/#Unchecked_Exception)
int num[] = {1, 2, 3, 4}; System.out.println(num[5]);
Bạn đã biết code đây rơi vào một
**, tức là trình biên dịch sẽ không nhận biết và kiểm tra giúp bạn liệu Unchecked Exception** *Exception*có xảy ra hay không.
Nhưng nếu trình biên dịch không làm gì cả thì chúng ta cũng đừng đứng khoanh tay nhé. Để chắc chắn ứng dụng của chúng ta
*“khỏe mạnh”*, bạn nên lường trước các tình huống có khả năng xảy ra *Exception* và bao các đoạn code *“có khả năng gây bệnh”* vào khối *. try catch*
Nếu ngoan cố thực thi ứng dụng, bạn sẽ nhận được một thông báo lỗi như thế này. Bạn hãy chú ý cái
*Exception* nhé, chúng ta sẽ * chúng lại ở bên dưới. catch*
Nào, chúng ta hãy bao đoạn có khả năng gây ra lỗi bằng khối
* như sau. try catch*
try { int num[] = {1, 2, 3, 4}; System.out.println(num[5]); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("Không thể in được vì không tìm thấy phần tử mảng như mong đợi."); }
Khi này, nếu bạn thực thi lại ứng dụng, người dùng sẽ dễ hiểu hơn với kiểu thông báo lỗi mang
*“tính người”* hơn như sau.
## Thực Hành Kiểm Chứng Cách Hoạt Động Của Try Catch
Với bài thực hành này bạn sẽ hiểu rõ hơn thực chất việc
*“bẫy”* lỗi và việc thực hiện các dòng code * xảy ra ngay như thế nào khi cái catch* *“bẫy”*đã bẫy được lỗi nhé.
Bạn xem đoạn code với
*đầy đủ như sau. Hãy thử đoán xem kết quả in ra console sẽ như thế nào. try catch *
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); ketQua = 10 / 1; System.out.println("10 chia 1 bằng " + ketQua); ketQua = 10 / 0; System.out.println("10 chia 0 bằng " + ketQua); } catch (ArithmeticException e) { System.out.println("Có một phép chia không thực hiện được"); }
Và đây là kết quả.
Ồ. Dòng in ra consolde
* đâu rồi nhỉ. Thực ra là, ngay khi hệ thống phát hiện có System.out.println(“10 chia 0 bằng ” + ketQua);* *Exception*trong khối *xảy ra, thì các dòng code bên dưới của khối* **try** *đó sẽ không được thực thi nữa mà nhường chỗ cho các khối lệnh trong* **try** *thực thi kế tiếp. Như minh họa vui mắt một chút như sau.* **catch**
# Một Số Quy Tắc Với Try Catch
Đến đây thì bạn đã biết cách bao lấy các dòng code có khả năng gây ra
*Exception* bằng * như thế nào rồi. Tuy nhiên mình biết với các bạn mới bắt đầu làm quen với try catch* *sẽ có rất nhiều câu hỏi, mình xin liệt kê chúng dưới dạng các quy tắc sau.* **try catch**
## Không Phải Lúc Nào Try Thì Luồng Cũng Bị Bẻ Vào Catch
Như bạn đã làm quen, không phải lúc nào các câu lệnh bên trong khối
* cũng gây ra try* *Exception*, và vì vậy không phải lúc nào các câu lệnh bên trong khối *cũng được thực thi đâu nhé, nếu logic trong* **catch** *được thực thi an toàn. Như ví dụ ngay trên đây, nếu bạn không thực hiện việc chia* **try** *cho* **10** *thì sẽ không có câu thông báo “* **0** *Có một phép chia không thực hiện được”*được in ra màn hình.
## Nhưng Nếu Bạn Định Nghĩa Try, Bạn Phải Định Nghĩa Catch
Tuy không phải lúc nào các đoạn code trong
* cũng gây ra try* *Exception*. Nhưng nếu có định nghĩa *, thì bạn buộc phải định nghĩa* **try** *kèm theo. Đó là một sự đảm bảo. Rằng bạn cứ phải luôn chỉ định logic dự phòng cho trường hợp có* **catch** *Exception*xảy ra thật.
## Catch Với Lớp Exception Luôn Được Không?
Bạn không nhất thiết phải catch với cụ thể các lớp con như
* hay ArrayIndexOutOfBoundsException* *như các ví dụ trên kia. Mà bạn có thể sử dụng lớp cha* **ArithmeticException** *luôn. Khi đó câu lệnh sẽ là t* **Exception** *. Vì đâu phải lúc nào bạn cũng nhớ các lớp con của* **ry {…} catch (Exception e) {…}** *đâu! Nhưng hãy cẩn thận! Hãy học việc sử dụng các* **Exception** *cụ thể, đừng vì lười mà sử dụng luôn lớp* **Exception con** *, thậm chí là lớp* **Exception** *để thay thế.* **Throwable**
Để làm chi ư, dĩ nhiên đầu tiên code của bạn sẽ dễ dàng để quản lý hơn, và việc tung ra lỗi cho người dùng sẽ cụ thể hơn, điều này sẽ được sáng tỏ khi bạn làm quen với việc bắt nhiều lỗi cùng lúc như mục dưới đây.
# Try Với Nhiều Catch
Có nhiều khi chúng ta nhồi nhét quá nhiều code vào khối
*, khiến nó try* *. Thì chúng ta hoàn toàn có thể áp dụng cú pháp sau để có thể bắt nhiều* **có khả năng gây ra nhiều hơn một Exception** *Exception*ở cùng một lần *.* **try**
try { // Các dòng code có khả năng gây ra Exception } catch (ExceptionClass1 e1) { // Bắt các EceptionClass1 } catch (ExceptionClass2 e2) { // Bắt các EceptionClass2 } catch (ExceptionClass3 e3) { // Bắt các EceptionClass3 }
Theo như cú pháp trên đây, bạn vẫn sẽ bao lấy các câu lệnh có khả năng gây ra
*Exception* vào một khối *. Rồi mỗi try* *sẽ là một* **catch** *Exception*riêng biệt. Sau đó, với bất cứ dòng nào gây ra *Exception*, hệ thống sẽ lần lượt tìm đến các khối *bên dưới* **catch** *cho đến khi tìm thấy một* **try** *Exception*tương ứng.
Đối với
*, bạn còn có thể viết cú pháp Java 7* *với nhiều* **try** *như sau. Các* **catch** *Exception*ở cách viết này được nằm trong cùng một *thôi nhưng chúng cách nhau bởi ký tự “* **catch** *“.* **|**
try { // Các dòng code có khả năng gây ra Exception } catch (ExceptionClass1|ExceptionClass2|ExceptionClass3 e) { // Bắt các EceptionClass1, EceptionClass2 và EceptionClass3 }
Chúng ta sẽ làm quen với việc xây dựng nhiều
* như thế này ở bài thực hành tiếp theo. catch*
## Thực Hành Xây Dựng Try Với Nhiều Catch
Ở bài thực hành này, bạn sẽ được làm quen với các
*, và làm quen với cách nhanh nhất để thêm khối lệnh Checked Exception* *(hoặc* **try catch** *với nhiều* **try** *) trong Eclipse.* **catch**
Đầu tiên, bạn hãy code dòng sau vào một phương thức nào đó.
FileOutputStream outputStream; outputStream = new FileOutputStream("E://file.txt"); outputStream.write(65); outputStream.close();
Bạn sẽ ngay lập tức thấy các báo lỗi từ trình biên dịch.
Đây không hẳn là một lỗi. Vì khi bạn click vào icon hình bóng đèn bên trái, bạn sẽ thấy một gợi ý khắc phục như sau.
Rõ ràng đây là một
*, và hệ thống đang gợi ý cho bạn với hai lựa chọn. Checked Exception*
–
*: lựa chọn này cho phép bạn không cần phải tạo ra bất kỳ khối Add throws declaration* *nào, mà tiếp tục* **try catch** *“quăng cho thằng nào đó”* *giúp. Kỹ thuật này sẽ được mình nói đến ở bài tiếp theo.* **try catch**
–
*: đây là lựa chọn mà chúng ta mong muốn, với việc chọn lựa tùy chọn này, hệ thống sẽ bao lấy code của bạn bằng một khối* **Surround with try/catch** *với* **try catch** *Exception*có tên là *.* **FileNotFoundException**
Bạn có thể tự gõ vào
* như gợi ý. Nhưng nếu bạn chọn tùy chọn thứ hai, hệ thống sẽ try catch* *tự động cho bạn, tuy nhiên cái tự động này khá* **try catch** *“vụng về”*, bạn cứ nên sửa lại cho code nhìn đẹp đẹp như sau.
try { FileOutputStream outputStream; outputStream = new FileOutputStream("E://file.txt"); outputStream.write(65); outputStream.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); }
Và bạn có thể thấy, dù cho đã
* với try catch* *, thì hệ thống vẫn cứ đang báo lỗi. Chúng ta cùng xem.* **FileNotFoudException**
Thì ra là hệ thống cần bạn phải
* thêm một catch* *Exception*khác có tên *. Lúc bấy giờ tùy chọn có thể nhiều hơn. Như hình trên, chúng bao gồm.* **IOException**
–
*: giống như mình đã nói ở trên. Add throws declaration*
–
*: thêm một* **Add catch clause to surrounding try** *nữa, dạng* **catch** *như cú pháp thứ nhất trong mục này.* **try catch catch**
–
*: thêm một* **Add exception to existing catch clause** *Exception*vào trong *sẵn có. Dạng* **catch** *như cú pháp thứ hai trong mục này.* **try catch (ExceptionClass1 | ExceptionClass2)**
–
*: lồng thêm một* **Surround with try/catch** *vào* **try catch** *hiện tại, cách này mình ít áp dụng vì nhìn code phức tạp lắm.* **try catch**
Đến đây bạn có thể chọn cách thứ hai để code trông như thế này.
try { FileOutputStream outputStream; 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(); }
Hoặc chọn cách thứ ba để ra code này.
try { FileOutputStream outputStream; outputStream = new FileOutputStream("E://file.txt"); outputStream.write(65); outputStream.close(); } catch (FileNotFoundException | IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }
## Bài Tập Số 1
Giả sử mình có chương trình sau. Chương trình sẽ tạo ra
* số nguyên ngẫu nhiên và lưu vào mảng 10* *phần tử. Chương trình sẽ cho người dùng nhập vào một chỉ số của mảng rồi xuất giá trị của mảng đó ra console.* **10**
// Tạo một mảng 10 các số nguyên ngẫu nhiên int randomIntNumbers[] = new int[10]; Random rand = new Random(); for(int i = 0; i < 10; i++) { randomIntNumbers[i] = rand.nextInt(100); } // Cho người dùng nhập một số nguyên và in ra màn hình // phần tử mảng tương ứng Scanner input = new Scanner(System.in); System.out.println("Bạn muốn in ra phần tử mảng thứ mấy? "); int index = input.nextInt(); System.out.println("OK, phần tử mảng thứ " + index + " mang giá trị " + randomIntNumbers[index]);
Tất nhiên, chương trình này tiềm ẩn nhiều nguy cơ gây ra
*Exception* ở runtime. Bạn hãy cố gắng ràng các * tương ứng để khi có try catch* *Exception*xảy ra thì chương trình có thể thông báo lỗi kịp thời đến với người dùng nhé.
Và đây là code đáp án, bạn nên tự code và tự
*“phá”* chương trình trước khi click vào đáp án này.
// Tạo một mảng 10 các số nguyên ngẫu nhiên int randomIntNumbers[] = new int[10]; Random rand = new Random(); for(int i = 0; i < 10; i++) { randomIntNumbers[i] = rand.nextInt(100); } try { // Cho người dùng nhập một số nguyên và in ra màn hình // phần tử mảng tương ứng Scanner input = new Scanner(System.in); System.out.println("Bạn muốn in ra phần tử mảng thứ mấy? "); int index = input.nextInt(); System.out.println("OK, phần tử mảng thứ " + index + " mang giá trị " + randomIntNumbers[index]); } catch (InputMismatchException e) { System.out.println("Phần tử mảng chưa hợp lệ, vui lòng nhập vào một số nguyên!"); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("Phần tử mảng chưa hợp lệ, vui lòng nhập vào giá trị từ 0 đến 9!"); }
## Bài Tập Số 2
Mình có đoạn code sau đã được
**try catch** *“cẩn thận”*, bạn xem * như vậy có còn khả năng gây ra lỗi nào nữa không. try catch*
Scanner input = new Scanner(System.in); Integer intNumber = null; try { System.out.println("Hãy nhập vào một số nguyên: "); String strNumber = input.nextLine(); intNumber = new Integer(strNumber); } catch (NumberFormatException e) { System.out.println("Vui lòng nhập vào một số nguyên"); } System.out.println("Chuyển thành Hexa: " + Integer.toHexString(intNumber));
Và mình đã phát hiện nó chưa tốt. Vì nếu có
*Exception* xảy ra với đoạn kêu người dùng nhập vào một số, thì * khi đó chưa được khởi tạo (mang giá trị intNumber* *), và sẽ có một* **null** *khác xảy ra cho đoạn code cuối cùng. Mình sửa lại code này như sau.* **Exception**
try { Scanner input = new Scanner(System.in); Integer intNumber = null; System.out.println("Hãy nhập vào một số nguyên: "); String strNumber = input.nextLine(); intNumber = new Integer(strNumber); System.out.println("Chuyển thành Hexa: " + Integer.toHexString(intNumber)); } catch (NumberFormatException e) { System.out.println("Vui lòng nhập vào một số nguyên"); }
Các bạn vừa xem xong phần 2 của loạt bài về
*Exception*. Qua bài học này chúng ta đã cùng nhau làm quen với việc bắt lỗi và bẻ luồng của ứng dụng thông qua công cụ *. try catch* *sẽ còn có* **Try catch** *“biến thể”*nữa và mình sẽ dành phần sau để nói tiếp nhé.
Cảm ơn bạn đã đọc các bài viết của
[. Bạn hãy ủng hộ blog bằng cách: Yellow Code Books](https://yellowcodebooks.com/)
–
* bên dưới mỗi bài nếu thấy thích. Đánh giá 5 sao*
–
*bên dưới mỗi bài nếu có thắc mắc.* **Comment**
–
*để nhận được thông báo sớm nhất khi có bài viết mới.* **Để lại địa chỉ email của bạn ở thanh bên phải**
–
*các bài viết của Yellow Code Books đến nhiều người khác.* **Chia sẻ**
*Bài Kế Tiếp* *Chúng ta sẽ nói đến việc sử dụng try catch nhưng có kèm thêm khối finally. Và còn một biến thể nữa của try catch. Các bạn đón xem tiếp nhé.*
## 3 comments
Cảm ơn bạn nhiều về bài viết nhé <3
cam on ban ve bai viet, that de hieu . Mong ban som viet tiep de moi nguoi cung hoc
Cảm ơn bạn rất nhiều. Đây là bài viết dễ hiểu nhất mà mình từng đọc. Hy vọng bạn sẽ update thêm kiến thức về Java. Luôn ủng hộ blog