- [Java Core] B1: Tại sao nên học Java, syntax cơ bản
- [Java Core] B2: Class và Object (Lớp và đối tượng)
- [Java Core] B3: Cách Java quản lý dữ liệu
- [Java Core] B4: Tính chất đóng gói, kế thừa và đa hình trong Java
- [Java Core] B5: Tính chất trừu tượng, Interface và Abstract Class trong Java
- [Java Core] B6: Design Pattern Iterator. Iterable và Collection trong Java
- [Java Core] B7: Exception trong Java
1. Làm quen với Exception: Checked và Unchecked Exception, Error
1.1 Lời mở đầu
- Trong cuộc sống, không phải lúc nào mọi việc cũng diễn ra suôn sẻ, không phải lúc nào mọi thứ cũng theo đúng kế hoạch. Đôi khi, có những tình huống không mong muốn xảy ra, và chúng ta cần phải xử lý chúng.
- Khi một ứng dụng ngày càng lớn lên, chắc chắn ta không thể đảm bảo ứng dụng luôn hoạt động như cách ta muốn 100% mọi lúc được. Đến lúc nào đó, sẽ có lỗi xảy ra.
Ví dụ:
- Khi một ứng dụng cần đọc một file từ ổ cứng, nhưng file đó không tồn tại.
- Khi ứng dụng cần quyền chụp ảnh từ camera, nhưng người dùng không cho phép.
- Khi ứng dụng kết nối tới một server, nhưng server đang bị mất điện và không kết nối được.
Khi đó, hệ thống cần có những cách xử lý riêng cho người dùng biết vấn đề đang xảy ra, và có những cách xử lý riêng cho hệ thống có thể tiếp tục hoạt động mà không bị ảnh hưởng quá nhiều.
Ví dụ:
- Khi file không tồn tại, ứng dụng cần thông báo cho người dùng biết rằng file không tồn tại và yêu cầu người dùng chọn file khác, hoặc kiểm tra lại đường dẫn file.
- Khi người dùng không cho phép ứng dụng chụp ảnh, ứng dụng cần thông báo cho người dùng biết rằng không thể chụp ảnh và yêu cầu người dùng cấp quyền.
- Khi server bị mất điện, ứng dụng cần thông báo cho người dùng biết rằng không thể kết nối tới server và yêu cầu người dùng thử lại sau, hoặc kiểm tra xem đường truyền wifi, 3G, 4G có vấn đề gì không.
1.2 Exception là gì?
- Exception dịch ra tiếng Việt có nghĩa là ngoại lệ. Exception là một sự kiện xảy ra trong quá trình thực thi chương trình, làm ảnh hưởng đến luồng thực thi của chương trình.
1.3 Checked và Unchecked Exception
1.3.1 Checked Exception
- Ảnh ví dụ: Khi ta thử đọc 1 file, IDE ngay lập tức thông báo có thể xảy ra FileNotFoundException (Ngoại lệ file không tồn tại) và yêu cầu ta phải xử lý nó.
- Checked Exception thường là những lỗi phổ biến mà chúng ta có thể dự đoán được sẽ xảy ra cho một thao tác nào đó.
- Ví dụ, khi đi dã ngoại, để phòng ngừa việc có mưa, ta mang theo áo mưa. Việc trời mưa là một ngoại lệ mà ta đã dự đoán trước và lên trước kế hoạch để xử lý nó.
1.3.2 Unchecked Exception
- Đây là những Exception không được kiểm tra tại thời điểm biên dịch (compile time). Tức là chúng ta không nhận được thông báo lỗi từ trình biên dịch khi mà nó phát hiện ra rằng chúng ta chưa xử lý nó.
- Đây là các exception khá nguy hiểm, khi mà chúng ta không thể dự đoán được chúng sẽ xảy ra khi nào. Bởi vì Exception này xảy ra khi ứng dụng đang thực thi, nên nó còn có thể gọi là
Runtime Exception
. - Ví dụ, khi một ứng dụng chia một số cho 0, nó sẽ ném ra một ngoại lệ
ArithmeticException
(Ngoại lệ toán học).
- Trong đoạn code trên, nếu ta nhập vào 1 số không tồn tại trong mảng, nó sẽ bị một Exception
ArrayIndexOutOfBoundsException
(Ngoại lệ vượt quá giới hạn của mảng)., đây là một ví dụ về Unchecked Exception. Vì ta không thể biết được trước khi chạy chương trình, nó sẽ xảy ra khi nào.
1.4 Error
- Error là một dạng ngoại lệ mà chúng ta không thể xử lý được. Error thường xảy ra khi mà hệ thống gặp phải những vấn đề nghiêm trọng, không thể khắc phục được.
- Ví dụ, khi một ứng dụng chạy hết bộ nhớ, nó sẽ ném ra một ngoại lệ
OutOfMemoryError
(Lỗi hết bộ nhớ). Đây là một ví dụ về Error. Đây là một ngoại lệ ta không thể xử lý được, và nó thường xảy ra khi mà hệ thống gặp phải những vấn đề nghiêm trọng, không thể khắc phục được.
2. Bắt Exception với try-catch
Bài tập
- Hãy viết chương trình nhập vào 1 String, và sử dụng
Integer.parseInt
để chuyển đổi String đó sang kiểu int. Thử nếu nhập vào 1 chuỗi không phải là số, xem chương trình sẽ báo lỗi như thế nào.
- Khi chạy chương trình trên, ta sẽ nhận được một ngoại lệ
NumberFormatException
(Ngoại lệ không đúng định dạng số). Đây là một ví dụ vềUnchecked Exception
. Vì lúc compile, ta không nhận lỗi nào báo trước cả, do IDE không biết trước được String truyền vào khi chạy chương trình
2.1 Cách bắt Exception
- Để bắt Exception, ta sử dụng cấu trúc try-catch. Cấu trúc try-catch sẽ giúp chúng ta bắt ngoại lệ, và xử lý nó một cách an toàn.
- Cấu trúc try-catch có dạng như sau:
- Trong đó:
try
: Là một khối lệnh, chứa các câu lệnh có thể ném ra ngoại lệ.catch
: Là một khối lệnh, chứa các câu lệnh xử lý ngoại lệ. Trong đó,Exception e
là một biến, chứa thông tin về ngoại lệ xảy ra.
2.2 Ví dụ
- Ví dụ, ta sẽ sử dụng cấu trúc try-catch để bắt ngoại lệ
NumberFormatException
khi chuyển đổi một chuỗi không phải là số sang kiểu int.
- Khi chạy chương trình trên, ta sẽ nhận được thông báo “Chuỗi không phải là số” khi chuyển đổi chuỗi “4.5a” sang kiểu float.
2.3 catch nhiều Exception
- Ta cũng có thể bắt nhiều ngoại lệ trong cùng một cấu trúc try-catch, bằng cách sử dụng nhiều khối catch.
Bài tập
- Hãy viết chương trình nhập vào số n, sau đó nhập vào n string. Sau đó nhập vào 1 số m, và in ra string thứ m. Nếu truy cập ngoài mảng, hãy in ra thông báo “Truy cập ngoài mảng”, còn nếu không in ra số được chuyển đổi từ string đó sang kiểu int, nếu không thể chuyển đổi được, hãy in ra thông báo “Chuỗi không phải là số”.\
- Như ví dụ trên, ta thấy ta có thể catch nhiều Exception trong cùng một cấu trúc try-catch.
2.4 Catch Exception cha
- Ta cũng có thể bắt Exception cha của một Exception con. Ví dụ, ta có thể bắt Exception cha của
NumberFormatException
làException
.
Bài tập
- Tương tự bài tập trên, nhưng chỉ dùng 1 catch, và có bất kì lỗi gì xảy ra ta đều in ra “Lỗi xảy ra + tên lỗi”.
- Như ví dụ trên, ta thấy ta có thể bắt Exception cha của
NumberFormatException
làException
.
3. Sử dụng finally
3.1 finally là gì?
- finally là một khối lệnh, chứa các câu lệnh sẽ được thực thi sau khi khối lệnh try-catch kết thúc.
- finally sẽ được thực thi sau khi khối lệnh try-catch kết thúc, bất kể có ngoại lệ xảy ra hay không.
3.2 Ví dụ
- Ví dụ, ta sẽ sử dụng finally để đóng Scanner sau khi sử dụng xong.
- Trong đoạn code trên, ta sẽ đóng Scanner sau khi sử dụng xong, bất kể có ngoại lệ xảy ra hay không.
Tác dụng
- finally thường được sử dụng để giải phóng tài nguyên, như đóng file, đóng kết nối, đóng Scanner, … Đây là một practice nên làm đó là giải phóng tài nguyên sau khi sử dụng xong, tránh lãng phí tài nguyên.
3.3 Một số câu hỏi
3.3.1 Tại sao không sử dụng Exception là xong mà cần catch Exception cụ thể, có nhiều class Exception con
- Khi sử dụng Exception là xong, ta sẽ bắt được tất cả các Exception, nhưng đôi khi ta cần xử lý một cách cụ thể cho từng Exception. Vì khi có lỗi xảy ra, cứ chỉ báo là đã có lỗi xảy ra thì thứ nhất người dùng rất khó chịu, ta khi sửa lỗi cũng không biết lỗi xảy ra là gì, ở đâu. Thứ hai, ta cũng không thể xử lý lỗi một cách cụ thể.
- Việc catch đúng Exception cụ thể sẽ giúp ta xử lý lỗi một cách cụ thể. Ví dụ: Nếu lỗi là chia cho 0, ta báo cho người dùng rằng họ nhập sai để họ biết sai ở đâu và sửa lại, còn nếu lỗi là không tìm thấy file, ta báo cho người dùng biết rằng file không tồn tại để họ biết cần phải tạo file mới. Hoặc nếu là các lỗi người dùng không hiểu được, ta cần báo các lỗi cụ thể, có định danh rõ ràng để lập trình viên có thể biết và sửa lỗi
- Ví dụ, trong window đôi lúc ta bị màn hình xanh, ta có các mã lỗi kiểu như
0x0000000A
,0x0000000B
, … những mã lỗi này không phục vụ cho người dùng hiểu, nhưng người dùng có thể dùng nó để báo cáo, và lập trình viên có thể dùng nó để sửa lỗi.
3.3.2 Có thể Try không có Catch không?
- Có thể, nhưng ta cần có một khối finally để giải phóng tài nguyên.
- Ví dụ, ta có thể sử dụng try-finally để giải phóng tài nguyên, mà không cần catch Exception.
4. Cây phân cấp Exception
- Trong Java, Exception được phân cấp theo một cấu trúc cây, với
Throwable
là gốc của cây,Error
vàException
là 2 nhánh con củaThrowable
.
- Interface Iterable nhằm ám chỉ những class bên dưới kế thừa có tính “có thể ném ra được”
- Lí do tại sao cần 1 cây Exception sẽ được giải thích bên dưới, mục câu hỏi tại sao không catch mọi exception
4.1 Throw, tung ra ngoại lệ
- Đến hiện tại, ta đã biết các Exception có sẵn trong Java, và cách bắt nó bằng cấu trúc try-catch.
- Ta nhận thấy, các Exception đều triển khai từ
Throwable
, và có thể tung ra (throw) một Exception bằng cách sử dụng từ khóathrow
.
Ví dụ: Ta cần nhập vào một số là tuổi của người dùng, ta sử dụng nextInt
của Scanner
để nhập vào, nhưng nếu người dùng nhập vào một chuỗi không phải là số, ta sẽ tung ra một Exception InputMismatchException
(Ngoại lệ nhập không đúng).
- Tuy nhiên, người dùng vẫn có thể nhập vào
1 số âm
, lúc này ta có thể thêm các điều kiện của riêng mình và throw ra Exception nếu muốn
4.2 Throws
throws
là một từ khóa, dùng để khai báo một phương thức có thể ném ra một ngoại lệ.- Ví dụ: Ta chia 1 hàm riêng để nhập vào thông tin nhân viên, nếu nhập sai ta sẽ trả ra 1 số Exception như
InputMismatchException
,NumberFormatException
, …. Tuy nhiên ta muốn nơi gọi đến hàm này tự xử lý ngoại lệ, ta có thể sử dụngthrows
để khai báo phương thức có thể ném ra ngoại lệ.
- Như code trên, ở hàm
inputInfo
ta sử dụngthrows
để khai báo phương thức có thể ném ra ngoại lệ. Lúc này ở nơi gọi đến hàminputInfo
sẽ bị yêu cầu xử lý ngoại lệInputMismatchException
mà ta đã khai báo.
- Như ảnh trên, ta thấy ở bên phải là luồng gọi hàm, và bên trái là luồng Exception được quay ngược lên trên để tìm nơi xử lý nó.
4.3 Tạo ra Exception của riêng mình
- Đôi khi, ta cần tạo ra Exception của riêng mình, để phục vụ cho mục đích của riêng mình.
- Tên
Throwable
có nghĩa là “có thể ném ra”, nghĩa là các Exception bên dưới của chúng ta có thể sử dụng các từ khoáthrows, throw
để ném ra. - Để tạo ra Exception của riêng mình, ta cần tạo ra một class kế thừa từ
Exception
hoặcRuntimeException
. Để tạo 1Checked Exception
, ta kế thừa từException
, còn để tạo 1Unchecked Exception
, ta kế thừa từRuntimeException
. - Ví dụ, ta cần tạo ra 1 Exception
AgeException
(Ngoại lệ tuổi), nếu tuổi nhập vào nhỏ hơn 0, ta sẽ ném ra Exception này.
- Ví dụ, ta sẽ sử dụng
AgeException
để ném ra Exception khi tuổi nhập vào nhỏ hơn 0.
- Như code trên, ta sử dụng
AgeException
để ném ra Exception khi tuổi nhập vào nhỏ hơn 0.