Xử lý Exceptions


Chương trình nào cũng có lỗi, khác nhau ở chổ ít hay nhiều. Để viết một chương trình có ít lỗi nhất đến cho người dùng là điều mà các LTV luôn mong muốn. Bài viết này cung cấp một số thông tin về Exception. Exception là gì? Tại sao ta lại cần quan tâm? Làm thế nào khi bạn lần đầu tiên đối mặt với java exception?

1. Giới thiệu


Một điều mà các LTV cần nắm được đó là: Lỗi xuất hiện trong chương trình.
Chúng ta cần biết điều này. Nhưng cái gì sẽ xảy ra tiếp theo khi lỗi xuất hiện. Nó sẽ được điều hướng như thế nào? Ai quản lý việc này? Và chương trình sẽ được khôi phục, hay sẽ bị ngưng?

2. Tại sao ta cần quan tâm?


Từ Exception là chữ viết tắt của cụm từ "exceptional event." Và được định nghĩa như sau:
Một exception là một event xảy ra trong quá trình thực thi của một chương trình khi quá trình thực hiện không theo đúng luồng đã được xây dựng.
Có nhiều loại lỗi có thể là lí do của Exception – bao gồm từ lỗi của phần cứng, như hỏng ổ cứng, đến những lỗi do lập trình, như là truy cập đến một phần tử ngoài giới hạn đã khai báo. Khi một lỗi xảy ra trong một phương thức trong Java, nó sẽ tạo ra một đối tượng Exception và chuyển đến “runtime system”. Đối tượng Exception này mang thông tin về lỗi bao gồm kiểu và trạng thái của chương trình khi sảy ra lỗi. Runtime system chịu trách nhiệm tìm kiếm đoạn code để xử lý cho lỗi này. Trong Java, tạo một đối tượng Exception và điều hướng nó đến runtime system gọi là “throw exception”.
Những đối tượng xử lý exception(gọi là exception handler) là danh sách các phương thức đã thực hiện trước khi có lỗi. Runtime system tìm đến nơi gọi, bắt đầu với phương thức nơi gặp lỗi, cho đến nơi mà nó tìm thấy một nơi xử lý cho exception. Một exception handler thích hợp khi kiểu của exception tung ta cùng kiểu với kiểu của exception trong exception handler. Quá trình lựa chọn exception handler gọi là “catch exception”.
Nếu runtime system không thể tìm thấy nơi xử lý exception trong tất cả các phương thức, thì runtime system sẽ hủy thực thi.
Bằng cách xử dụng exceptions để quản lý lỗi, ngôn ngữ Java cung cấp một chiến lược quản lý lỗi rất tốt.

3. Lợi thế của Java


Lợi thế 1: Tách riêng phần code điều khiển lỗi


Theo truyền thống, việc tìm ra lỗi, báo cáo lỗi, điều khiển code khi có lỗi đểu gom lại một nơi define trong code. Ví dụ, giả sử bạn có một chức năng đọc một file lưu vào trong memory. Viết theo mã giả, code của bạn sẽ thế này:

readFile {
open the file;
determine its size;
allocate that much memory;
read the file into memory;
close the file;
}

Ta nhận thấy một số lỗi tiềm ẩn sau:
• File sẽ có thể không mở được?
• Không xác định được chiều dài của file?
• Không đủ memory để lưu trữ?
• File không thể đọc được?
• File không thể đóng được?
Để thực hiện được các việc này, thì trong chức năng readFile, bạn cần thêm một số code để thực hiện công việc khi có lỗi, và điều hướng. Có thể nhìn thấy như sau:


errorCodeType readFile {
initialize errorCode = 0;
open the file;
if (theFileIsOpen) {
determine the length of the file;
if (gotTheFileLength) {
allocate that much memory;
if (gotEnoughMemory) {
read the file into memory;
if (readFailed) {
errorCode = -1;
}
} else {
errorCode = -2;
}
} else {
errorCode = -3;
}
close the file;
if (theFileDidntClose && errorCode == 0) {
errorCode = -4;
} else {
errorCode = errorCode and -4;
}
} else {
errorCode = -5;
}
return errorCode;
}

Với code xử lý lỗi, từ 7 dòng trở thành 29 dòng code – tỷ lệ là 400 %. Tệ hơn, trong đó có nhiều dòng chỉ để tìm ra lỗi, báo lỗi, trong 7 dòng code chính, làm cho ta khó đọc code, và vì vậy việc chỉnh sửa sau này sẽ rất khó khăn. Một vấn đề khác nữa là: Có chắc rằng file đã được đóng nếu memory không đủ để lưu file?
Exceptions cho phép bạn viết luồng chính của code, và ứng xử với lỗi trong một đoạn xử lý khác.
Do đó, code của bạn sẽ thế này:


readFile {
try {
open the file;
determine its size;
allocate that much memory;
read the file into memory;
close the file;
} catch (fileOpenFailed) {
doSomething;
} catch (sizeDeterminationFailed) {
doSomething;
} catch (memoryAllocationFailed) {
doSomething;
} catch (readFailed) {
doSomething;
} catch (fileCloseFailed) {
doSomething;
}
}

Lưu ý là exceptions không giúp bạn tìm ra lỗi, đưa ra thông tin lỗi, và xử lý lỗi. Những gì exceptions làm cho bạn là đưa ra một phương pháp để tách biệt tất cả các xử lý cho exception ra khỏi luồng chính của bạn.
Thêm nữa, nó còn làm cho tổng cộng code xử lý của bạn giảm đáng kế, chỉ còn 250% - so với 400% trong ví dụ trước.

Lợi thế 2: Tung lỗi chồng


Lợi thế thứ 2 của exception là khả năng truyền lỗi bằng phương pháp gọi chồng. Giả xử phương thức readFile là phương thức thứ 4 trong chuổi các phương thức trong luồng chính: phương thức 1 gọi phương thức 2, phương thức này lại gọi phương thức 3, và cuối cùng là gọi phương thức readFile.

method1 {
call method2;
} method2 {
call method3;
} method3 {
call readFile;
}

Giả xử rằng chỉ có method1 là quan tâm đến lỗi sảy ra trong readFile. Theo như cách truyền thống thì method2 và method3 chịu trách nhiệm truyền mã của error được trả về bởi readFile lên đến method1 – phương thức duy nhất quan tâm đến xử lý cho lổi này:

method1 {
errorCodeType error = call method2;
if (error)
doErrorProcessing;
else
proceed;
}

errorCodeType method2 {
errorCodeType error = call method3;
if (error)
return error;
else
proceed;
}
errorCodeType method3 {
errorCodeType error = call readFile;
if (error)
return error;
else
proceed;
}

Như đã biết ở trên, Java runtime sẽ tìm kiếm trong quá trình gọi, để tìm ra nơi xử lý cho lỗi nó bắt gặp. Một phương thức Java có thể tổng hợp xử lý nhiều exceptions được tung ra trong nó, theo cách đó, ta chỉ cần định nghĩa một phương thức để có thể xử lý cho các exception. Vì vậy chỉ phương thức có xử lý cho lỗi là ta quan tâm.

method1 {
try {
call method2;
} catch (exception) {
doErrorProcessing;
}
}
method2 throws exception {
call method3;
}
method3 throws exception {
call readFile;
}

Trong đoạn code giả trên, để nhúng một exception vào một phương thức trung gian thì ta cần chỉ nó ra trong phương thức đó. Do đó, khi định nghĩa một phương thức thì cần định nghĩa luôn những exeption có thể có xảy ra trong nó, họ sẽ có cách xử lý cho các exceptions này một cách tốt nhất.

Lợi thế 3: Gom nhóm và phân biệt lỗi


Thường thì exceptions đều thuộc về categories hay groups. Giả xử rằng ta có một group exceptions, mỗi một lỗi sẽ có một lý do khác nhau, lấy ví dụ như thao tác trên một array: khi giá trị truy xuất ra khỏi phạm vi của array, thành phần đã thêm vô array không đúng kiểu, hay thành phần tìm kiếm không thấy trong array. Cũng có thể gom nhóm to hơn bằng cách: chi làm 2 loại là Array exception, và một exceptions định sẵn cho trường hợp vượt ra ngoài phạm vi truy xuất.
Java exceptions phải là một hiện thực của Throwable hay Throwable. Cho các class khác, bạn có thể tạo một subclasses của Throwable. Mỗi "nhánh" class (một class không có subclasses) miêu tả một kiểu của exception và mỗi "nút" class (một class với một hoặc nhiều subclasses) miêu tả một group exceptions đã khai báo.
Ví dụ, trong diagram sau, ArrayException là subclass của Exception (một subclass của Throwable) và có 3 subclasses.

InvalidIndexException, ElementTypeException, và NoSuchElementException là các leaf classes. Mỗi class là một thể hiện của một lỗi có thể xuất hiện khi thao tác trên một array. Một cách để một phương thức có thể catch exceptions là chỉ catch những instances của một leaf class. Ví dụ, một exception handler điều khiển chỉ invalid index exceptions, phương thức đó sẽ như sau:


catch (InvalidIndexException e) {
. . .
}

ArrayException là một node class và thể hiện của các lỗi có thể xuất hiện khi tương tác với một array, bao gồm các errors đã được khai báo trong các subclasses của nó. Một phương thức có thể catch một exception trong một group hay cũng có thể catch nhiều exception con.
Ví dụ, để catch tất cả array exceptions mà không quan tâm đến kiểu được khai báo, một exception handler sẽ khai báo một ArrayException:


catch (ArrayException e) {
. . .
}


Đoạn handler trên sẽ catch tất cả array exceptions bao gồm InvalidIndexException, ElementTypeException, và NoSuchElementException. Bạn có thể tìm kiếm chính xác exception nào xuất hiện bằng cách tìm exception handler với tham số e. Bạn cũng có thể cài đặt một exception handler để handles tất cả exception với đoạn handler sau:


catch (Exception e) {
. . .
}

Exception handlers là quá chung chung, như ta thấy ở trên, bạn có thể làm cho code bạn hoàn toàn không có lỗi bằng cách catching và handling exceptions mà bạn không biết trước và không có một xử lý cụ thể cho exception. Tôi thì không khuyên dùng exception handlers như thế.
Như bạn đã thấy, bạn có thể tạo một groups exceptions và handle exceptions chung, hay có thể khai báo exception riêng rẽ.

Cao Trong Hien

4 Responses to "Xử lý Exceptions"

22:00 10 tháng 2, 2009
Bài dịch khá tốt .
Keep up the good work !
tienlong said :
01:34 20 tháng 4, 2009
Anh cho em hỏi: Exceptions và Error khác nhau thế nào, cái nào nằm trong cái nào
Hiendt said :
11:41 21 tháng 4, 2009
Java có 3 loại exception:
1. Checked exception:
Đây là trường hợp hay gặp nhất, và dễ nhận biết nhất. Lấy ví dụ một chương trình viết chạy trên command line, cần người dùng nhập một file. Đúng thì không sao, nhưng khi file này không tồn tại sẽ có một java.io.FileNotFoundException được tung ra.
2. Error:
Là những tác nhân bên ngoài ứng dụng ta tác động đến, thường thì ta không biết trước. Ví dụ bình thường ta nhập đúng file, nó sẽ thể hiện nội dung, nhưng nội dung của file lại không thể đọc vì lý do phần cứng hay hệ thống khác. Khi đó thì một java.io.IOError sẽ được tung ra.
3. Runtime exception:
Đây cũng là trường hợp khó biết trước. Thường là những lỗi về phía lập trình như logic hay dùng những API không thích hợp.
vietutd said :
14:06 1 tháng 6, 2009
Ôi lập trình! :(

Đăng nhận xét