Ngoại lệ trong Java là một vấn đề phát sinh trong quá trình thực hiện một chương trình, khi nó xảy ra, luồng thực thi của chương trình bị gián đoạn. Trong bài viết này, chúng ta sẽ tìm hiểu về các ngoại lệ trong Java. Chúng ta sẽ đề cập đến các lỗi, các ngoại lệ khác nhau và cách xử lý.
Ngoại lệ
Ngoại lệ (Exception) là một sự kiện bất ngờ xảy ra trong quá trình thực hiện chương trình. Nó ảnh hưởng đến luồng hướng dẫn chương trình có thể khiến chương trình kết thúc một cách bất thường.
Một ngoại lệ có thể xảy ra vì nhiều lý do gồm có:
- Đầu vào của người dùng không hợp lệ
- Lỗi thiết bị
- Mất kết nối mạng
- Giới hạn vật lý (hết bộ nhớ)
- Lỗi đoạn mã code
- Mở một tệp không tồn tại
Phân cấp của ngoại lệ trong Java
Dưới đây là một sơ đồ đơn giản của hệ thống phân cấp ngoại lệ trong Java.
1. Lỗi (Error)
Các lỗi thể hiện rằng các điều kiện không thể khôi phục như máy ảo Java (JVM) hết bộ nhớ, rò rỉ bộ nhớ, lỗi tràn ngăn xếp, không tương thích thư viện, đệ quy vô hạn,…
Các lỗi sẽ thường nằm ngoài tầm kiểm soát của lập trình viên và chúng ta không nên cố gắng xử lý lỗi.
2. Ngoại lệ
Các ngoại lệ có thể được chương trình xử lý. Khi một ngoại lệ xảy ra trong một phương thức, nó sẽ tạo ra một đối tượng. Đối tượng này được gọi là đối tượng ngoại lệ. Nó chứa thông tin về ngoại lệ như tên và mô tả của ngoại lệ và trạng thái của chương trình khi ngoại lệ xảy ra. Chúng ta sẽ tìm hiểu cách xử lý các ngoại lệ này trong phần sau.
Các loại ngoại lệ trong Java
Hệ thống phân cấp ngoại lệ có hai nhánh gồm: RuntimeException và IOException.
1. RuntimeException
Một ngoại lệ thời gian chạy sẽ xảy ra là do lỗi lập trình. Chúng còn được gọi là ngoại lệ không được kiểm tra. Các ngoại lệ này không được kiểm tra tại thời điểm biên dịch mà trong thời gian chạy. Một số ngoại lệ phổ biến gồm:
- Sử dụng không đúng API – IllegalArgumentException.
- Truy cập tới con trỏ rỗng (không khởi tạo một biến) – NullPointerException.
- Truy cập ngoài giới hạn của mảng – ArrayIndexOutOfBoundsException.
- Chia một số cho giá trị là 0 – ArithmeticException.
Nếu một ngoại lệ xảy ra trong thời gian thực thi, thì nó là lỗi của người lập trình.
NullPointerException sẽ không xảy ra nếu ta đã kiểm tra xem biến đã được khởi tạo hay chưa trước khi sử dụng nó.
Một ArrayIndexOutOfBoundsException sẽ không xảy ra nếu bạn kiểm tra chỉ số mảng với giới hạn của mảng.
2. IOException
IOException còn được gọi là ngoại lệ đã kiểm tra. Chúng được trình biên dịch kiểm tra tại thời điểm biên dịch và lập trình viên được nhắc trước để xử lý các ngoại lệ này.
Ví dụ về các ngoại lệ đã kiểm tra bao gồm:
- Cố gắng mở một tệp không tồn tại dẫn đến FileNotFoundException.
- Cố gắng đọc qua phần cuối cùng của một tệp.
Xử lý ngoại lệ
Chúng ta biết rằng các trường hợp ngoại lệs sẽ chấm dứt việc thực thi một chương trình một cách bất thường. Đây là lý do tại sao cần phải phải xử lý các trường hợp ngoại lệ. Dưới đây là danh sách các cách tiếp cận khác nhau để xử lý các ngoại lệ trong Java.
- Khối lệnh Try … catch
- Khối lệnh Finally
- Từ khóa throw và throws
1. Khối lệnh try … catch
Để bắt và xử lý một trường hợp ngoại lệ, chúng ta sẽ đặt khối try … catch xung quanh đoạn mã có thể tạo ra một ngoại lệ.
Cú pháp:
1 2 3 4 5 6 7 |
try { // Đoạn mã } catch(Exception e) { // Đoạn mã 2 } |
Ở đây, đoạn mã có thể tạo ra một ngoại lệ được đặt trong khối try. Mỗi khối try được theo sau bởi một khối catch. Khi một ngoại lệ xảy ra, nó sẽ được bắt bởi khối catch. Ta sẽ không thể sử dụng khối catch nếu không có khối try.
Ví dụ:
1 2 3 4 5 6 7 8 9 10 |
class Main { public static void main(String[] args) { try { int phep_chia = 123 / 0; } catch (ArithmeticException e) { System.out.println(e.getMessage()); } } } |
Trong ví dụ trên, chúng ta đang cố gắng chia một số cho 0. Ở đây, đoạn mã này tạo ra một ngoại lệ. Để xử lý ngoại lệ, chúng ta đã đặt đoạn mã 123 / 0 bên trong khối try. Khi một ngoại lệ xảy ra, phần còn lại của đoạn mã bên trong khối try sẽ bị bỏ qua. Khối catch sẽ bắt được trường hợp ngoại lệ và các câu lệnh bên trong khối catch được thực thi. Nếu không có ngoại lệ xảy ra, khối catch sẽ bị bỏ qua.
2. Khối lệnh finally
Trong Java, khối finally luôn được thực thi bất kể có ngoại lệ hay không. Khối này được sử dụng tùy chọn. Đối với mỗi khối try, chỉ có thể có một khối finally.
Cú pháp:
1 2 3 4 5 6 7 8 9 10 11 |
try { //Đoạn mã 1 } catch (ExceptionType1 e1) { // Đoạn mã 2 } finally { // Đoạn mã 3 } |
Nếu một ngoại lệ xảy ra, khối lệnh finally sẽ được thực thi sau khối try … catch. Nếu không, nó được thực thi sau khối try. Đối với mỗi khối try, chỉ có thể có một khối finally.
Ví dụ:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Main { public static void main(String[] args) { try { int phep_chia = 123 / 0; } catch (ArithmeticException e) { System.out.println(e.getMessage()); } finally { System.out.println("Van duoc thuc thi!"); } } } |
Kết quả:
1 2 |
/ by zero Van duoc thuc thi! |
Trong ví dụ trên, chúng ta chia một số nguyên cho 0 bên trong khối try. Ở đây, đoạn mã này tạo ra một ngoại lệ là ArithmeticException. Ngoại lệ được chặn bởi khối catch. Và, sau đó khối finally được thực thi.
Chú ý: Nên sử dụng khối lệnh finally. Bởi nó có thể bao gồm các đoạn mã quan trọng như:
- Đoạn mã có thể vô tình bị bỏ qua bởi câu lệnh return; break; hay continue;
- Đóng một tệp hoặc ngắt kết nối.
3. Từ khóa throw và throws trong Java
Từ khóa throws Java được sử dụng để truyền rõ ràng một ngoại lệ. Khi chúng ta truyền một ngoại lệ, luồng chương trình sẽ di chuyển từ khối try sang khối catch.
Ví dụ:
1 2 3 4 5 6 7 8 |
class Main { public static void phep_tinh_sai() { throw new ArithmeticException(); } public static void main(String[] args) { phep_tinh_sai(); } } |
Trong ví dụ trên, chúng ta đang truyền ArithmeticException một cách rõ ràng bằng cách sử dụng từ khóa throw. Tương tự, từ khóa throws được sử dụng để khai báo loại ngoại lệ có thể xảy ra trong phương thức. Nó được sử dụng trong khai báo phương thức.
Ví dụ:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import java.io.*; class Main { public static void phep_tinh() throws ArithmeticException { int phep_chia = 432 / 0; } public static void main(String[] args) { try { phep_tinh(); } catch (ArithmeticException e) { System.out.println(e); } } } |
Kết quả:
1 |
java.lang.ArithmeticException: / by zero |
Phương thức phep_tinh() chỉ định rằng một ArithmeticException có thể được truyền. Phương thức main() gọi phương thức này và xử lý ngoại lệ nếu nó được truyền. Nếu một phương thức không xử lý các ngoại lệ, thì các kiểu ngoại lệ mà có thể xảy ra bên trong nó phải được chỉ định trong mệnh đề throws.
Trên đây là khái niệm và ví dụ cơ bản về Ngoại lệ trong Java. Hy vọng mọi người có thể áp dụng vào trong chương trình của mình. Mọi người hãy tiếp tục theo dõi các bài tiếp theo và cập nhật các bài mới nhất trên tek4 nhé!
P/s: Cảm ơn mọi người!