Là một nhà phát triển Java, bạn nên có kiến thức tốt về các ngoại lệ và xử lý ngoại lệ của Java.
Hướng dẫn này cung cấp những kiến thức cơ bản mà mọi lập trình viên phải có khi làm việc với các chương trình Java. Để bắt đầu, hãy bắt đầu với việc hiểu chính xác Java Exceptions là gì.
Trường hợp ngoại lệ của Java là gì
Một chương trình Java có thể gặp sự cố dẫn đến chương trình bị chấm dứt đột ngột trong quá trình thực thi. Những vấn đề này được gọi là ngoại lệ.
Một lập trình viên giỏi phải có khả năng nhận ra các lỗi có thể xảy ra trong quá trình thực thi và cung cấp các lộ trình thay thế để chương trình thực hiện trong trường hợp ngoại lệ đó. Thực hành này được gọi là xử lý ngoại lệ.
Bây giờ bạn có thể tự hỏi tại sao chúng ta cần xử lý ngoại lệ. Tại sao không viết các chương trình sẽ không có ngoại lệ?
Tại sao Chúng tôi Cần Xử lý Ngoại lệ
Hóa ra, việc viết các chương trình không có ngoại lệ không dễ dàng như người ta vẫn tưởng. Hầu hết thời gian, những lỗi không thể tránh khỏi này nằm ngoài tầm kiểm soát của lập trình viên.
Các chương trình chấp nhận đầu vào của người dùng dễ rơi vào trường hợp ngoại lệ vì đầu vào không hợp lệ mà người dùng cung cấp. Việc đọc các tệp bên ngoài cũng vậy khi xem xét khả năng chúng đã bị di chuyển, đổi tên hoặc xóa bởi một nguồn bên ngoài mà lập trình viên không biết.
Trong những trường hợp như vậy, chương trình phải có khả năng xử lý ngoại lệ một cách duyên dáng mà không phải chấm dứt việc thực thi.
Hệ thống phân cấp của Java Exceptions
Tất cả các ngoại lệ trong Java phải là con của Exception
lớp, chính nó là con của Throwable
lớp học.
Hai lớp con chính của Exception
lớp là RuntimeException
và IOException
.
Ngoại lệ so với Lỗi
Một lớp con khác của Throwable
lớp là Error
lớp. Tuy nhiên, các lỗi khác với các trường hợp ngoại lệ.
Lỗi chỉ ra các vấn đề mà JVM có thể gặp phải trong quá trình thực thi. Những vấn đề này thường nghiêm trọng và không thể khắc phục được. Rò rỉ bộ nhớ và các vấn đề không tương thích với thư viện là những lý do phổ biến gây ra lỗi trong chương trình.
StackOverflowError
và OutOfMemoryError
là hai ví dụ về Lỗi Java.
Ngoại lệ được Kiểm tra và Bỏ chọn
Chúng ta có thể chia Ngoại lệ Java thành hai loại chính: đã kiểm tra và bỏ chọn ngoại lệ.
Các ngoại lệ được kiểm tra là các ngoại lệ cần được xử lý trong chương trình trước khi biên dịch. Nếu những ngoại lệ này không được xử lý, chương trình sẽ không được trình biên dịch Java biên dịch. Do đó, chúng còn được gọi là các ngoại lệ thời gian biên dịch. IOExceptions
là những ví dụ điển hình về các trường hợp ngoại lệ đã được kiểm tra.
Các ngoại lệ không được kiểm tra là các ngoại lệ mà trình biên dịch bỏ qua khi biên dịch chương trình. Việc chúng tôi có xử lý các ngoại lệ này trong chương trình hay không không quan trọng khi chương trình được biên dịch. Vì xử lý ngoại lệ không được áp dụng cho các ngoại lệ này, chương trình của chúng tôi có thể chạy vào RuntimeExceptions
dẫn đến việc chấm dứt chương trình.
Tất cả các lớp mở rộng RuntimeException
lớp là những ngoại lệ không được kiểm tra. Hai ví dụ về các lớp như vậy là NullPointerException
và ArrayIndexOutOfBoundsException
.
Các phương thức thường được sử dụng trong lớp ngoại lệ
Chúng ta sẽ xem xét một số phương pháp thường được sử dụng trong Java Exception
lớp:
-
getMessage
:trả về một thông báo chứa thông tin chi tiết về trường hợp ngoại lệ đã xảy ra. -
printStackTrace
:trả về dấu vết ngăn xếp của ngoại lệ đã xảy ra. -
toString
:trả về tên lớp và thông báo được trả về bằnggetMessage
phương pháp.
Cách Xử lý Ngoại lệ
Hãy xem cách chúng tôi có thể xử lý các ngoại lệ trong Java:
thử bắt
Chúng tôi có thể nắm bắt các trường hợp ngoại lệ và xử lý chúng đúng cách bằng cách sử dụng try-catch khối trong Java.
Trong cú pháp này, phần mã dễ bị ném ngoại lệ được đặt bên trong khối try và khối / khối bắt bắt ngoại lệ / ngoại lệ đã ném và xử lý chúng theo logic mà chúng tôi cung cấp.
Cú pháp cơ bản của khối try-catch như sau:
try {
//exception-prone code
}
catch(Exception e) {
//error handling logic
}
Với cách tiếp cận này, chương trình không tạm dừng thực thi khi chương trình đưa ra một ngoại lệ, thay vào đó, nó được xử lý một cách duyên dáng.
Chúng ta sẽ xem cách xử lý IOExceptions
được ném bởi FileReader
trong một chương trình Java.
Ví dụ:
import java.io.FileReader;
public class TryCatchBlockExample {
public static void main(String[] args) {
try {
FileReader file = new FileReader("source.txt");
file.read();
}
catch(Exception e) {
e.printStackTrace();
}
}
}
Ở đây, chúng tôi đã sử dụng một khối bắt duy nhất để xử lý FileNotFoundException
ném khi khởi tạo FileReader
lớp và IOException
được ném bởi read()
phương thức của FileReader
lớp học.
Cả hai ngoại lệ này đều là con của Exception
lớp học.
Chúng ta cũng có thể sử dụng nhiều câu lệnh catch để bắt các loại lỗi khác nhau do mã bên trong câu lệnh try đơn. Đối với ví dụ trước, chúng ta có thể sử dụng một khối bắt để bắt FileNotFoundException
và một khối bắt khác cho IOException
như đoạn mã sau hiển thị:
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class TryCatchBlockExample {
public static void main(String[] args) {
try {
FileReader file = new FileReader("source.txt");
file.read();
file.close();
}
catch(FileNotFoundException e) {
e.printStackTrace();
}
catch(IOException e) {
e.printStackTrace();
}
}
}
Nếu ngoại lệ được ném khớp với ngoại lệ được xử lý bởi câu lệnh catch đầu tiên, thì nó sẽ được xử lý bởi logic bên trong câu lệnh catch đầu tiên.
Nếu các ngoại lệ không khớp, nó được chuyển đến câu lệnh bắt thứ hai. Nếu có nhiều hơn hai câu lệnh catch, quá trình này sẽ tiếp tục cho đến khi ngoại lệ đạt đến một câu lệnh catch bắt được loại của nó.
Kể từ FileNotFoundException
là một kiểu con của IOException
, sử dụng câu lệnh catch thứ 2 để bắt một FileNotFoundException
sẽ không hoạt động. Nó sẽ được xử lý bởi câu lệnh bắt đầu tiên và không bao giờ đến câu lệnh thứ hai.
cuối cùng
Khi chúng tôi sử dụng try-catch khối để bắt các ngoại lệ trong chương trình của chúng tôi, có những trường hợp chúng tôi muốn triển khai một số logic bất chấp thực tế là liệu một ngoại lệ có được bắt hay không. Trong những trường hợp như vậy, chúng tôi có thể sử dụng try-catch-last thay vì chỉ thử bắt khối.
Sau đó, mã bên trong finally
câu lệnh được thực hiện cho dù có ngoại lệ xảy ra hay không. finally
câu lệnh phải luôn ở cuối khối try-catch-last.
Ví dụ:khi chúng tôi sử dụng FileReader
để đọc một tệp, điều cần thiết là phải đóng tệp đã mở khi kết thúc quá trình xử lý cho dù có ngoại lệ xảy ra hay không. Để đảm bảo điều này, chúng tôi có thể đặt mã để đóng tệp bên trong finally
tuyên bố.
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class TryCatchFinallyBlockExample {
public static void main(String[] args) {
FileReader file = null;
try {
file = new FileReader("source.txt");
file.read();
}
catch(FileNotFoundException e) {
e.printStackTrace();
}
catch(IOException e) {
e.printStackTrace();
}
finally {
file.close();
}
}
}
Tuy nhiên, nếu bạn cố gắng biên dịch mã trên, mã sẽ không được biên dịch do IOException
không được xử lý . Điều này là do close()
phương thức của FileReader
lớp cũng có thể ném IOExceptions
. Vì vậy, chúng ta phải đặt phần này bên trong một khối thử khác như thế này:
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class TryCatchFinallyBlockExample {
public static void main(String[] args) {
FileReader file = null;
try {
file = new FileReader("source.txt");
file.read();
}
catch(FileNotFoundException e) {
e.printStackTrace();
}
catch(IOException e) {
e.printStackTrace();
}
finally {
try {
file.close();
}
catch(IOException e) {
e.printStackTrace();
}
}
}
}
ném
Xử lý lỗi bằng cách sử dụng throws
từ khóa trong Java rất đơn giản. Trên thực tế, trong cách tiếp cận này, bạn không thực sự xử lý ngoại lệ tại nơi nó xảy ra. Thay vào đó, chúng ta ném ngoại lệ ra khỏi phương thức hiện tại cho phương thức được gọi là phương thức hiện tại. Sau đó, việc xử lý lỗi trở thành trách nhiệm của phương pháp bên ngoài.
Để loại bỏ một ngoại lệ ra khỏi một phương thức, bạn chỉ cần khai báo rằng phương thức này có thể ném ngoại lệ được xem xét. Hãy xem cách chúng tôi có thể xử lý IOExceptions
được ném bởi FileReader
sử dụng cách tiếp cận này.
Ví dụ:
import java.io.FileReader;
import java.io.IOException;
public class ThrowsExample {
public void readFile throws IOException {
FileReader file = new FileReader("source.txt");
file.read();
file.close();
}
}
ném
Không giống như các cách tiếp cận khác trong danh sách này, throws
từ khóa không được sử dụng để xử lý lỗi. Nhưng vì hầu hết mọi người nhầm lẫn giữa throws
từ khóa có throws
từ khóa, chúng tôi nghĩ tốt nhất nên thảo luận ở đây.
throws
từ khóa được sử dụng để gọi một cách rõ ràng một ngoại lệ. Chúng ta có thể đưa ra một ngoại lệ mới được khởi tạo hoặc một ngoại lệ đã bị bắt bên trong phương thức.
public class ThrowExample {
public void invalidate(int amount) throws Exception {
if (amount < 500) {
throw new Exception("Amount not sufficient");
}
}
}
Ngoại lệ do người dùng xác định
Ngoài việc sử dụng các ngoại lệ Java cài sẵn, bạn có thể xác định các ngoại lệ của riêng mình. Bạn có thể xác định chúng là ngoại lệ được chọn hoặc không được chọn. Để tạo một ngoại lệ đã kiểm tra mới, ngoại lệ mới của bạn phải mở rộng Exception
lớp học.
Để tạo không được chọn ngoại lệ, mở rộng RuntimeException
lớp học.
Trong ví dụ mã sau, chúng tôi đã tạo một ngoại lệ đã kiểm tra do người dùng xác định:
public class InvalidLengthException extends Exception {
private int length;
private String message;
public InvalidLengthException(int length, String message) {
this.length=length;
this.message=message;
}
public int getAmount() {
return this.length;
}
public String getMessage() {
return this.message;
}
}
Bây giờ chúng ta có thể sử dụng ngoại lệ trên bên trong logic chương trình của chúng ta như sau:
public class InputChecker {
private int minLength;
private int maxLength;
public InputChecker(int minLength, int maxLength) {
this.minLength=minLength;
this.maxLength=maxLength;
}
public void checkStringLength(String strInput) throws InvalidLengthException {
int strLength = strInput.length();
if (strLength < minLength) {
throw new InvalidLengthException(strLength, "Input should have minimum "+minLength+" characters");
}
else if (strLength > maxLength){
throw new InvalidLengthException(strLength, "Input should have maximum "+maxLength+" character");
}
}
}
Nếu chúng tôi kiểm tra độ dài của một chuỗi bằng InputChecker
, nó sẽ ném ra một InvalidLengthException
nếu độ dài chuỗi dưới độ dài tối thiểu hoặc trên độ dài tối đa.
public class Main {
public static void main(String[] args) {
InputChecker ic = new InputChecker(2, 7);
try {
ic.checkStringLength("longer than the maximum length");
}
catch(InvalidLengthException e) {
e.printStackTrace();
}
}
}
Khi chúng tôi chạy đoạn mã trên, nó sẽ ném ra một InvalidLengthException
và chúng tôi sẽ nhận được kết quả sau:
InvalidLengthException: Input should have maximum 7 character
at InputChecker.checkStringLength(InputChecker.java:17)
at Main.main(Main.java:6)
Kết luận
Trong hướng dẫn này, chúng tôi đã giới thiệu nhanh và ngắn gọn cho bạn về Java Exceptions. Chúng tôi hy vọng rằng bây giờ bạn đã hiểu rõ về những ngoại lệ là gì và cách xử lý chúng trong chương trình Java của bạn.