Có phải là một thực hành xấu để bắt Throwable
?
Ví dụ một cái gì đó như thế này:
try {
// Some code
} catch(Throwable e) {
// handle the exception
}
Đây có phải là một thực hành xấu hay chúng ta nên càng cụ thể càng tốt?
Có phải là một thực hành xấu để bắt Throwable
?
Ví dụ một cái gì đó như thế này:
try {
// Some code
} catch(Throwable e) {
// handle the exception
}
Đây có phải là một thực hành xấu hay chúng ta nên càng cụ thể càng tốt?
Câu trả lời:
Bạn cần phải càng cụ thể càng tốt. Nếu không, những con bọ không lường trước có thể biến mất theo cách này.
Bên cạnh đó, các Throwable
trang bìa Error
cũng vậy và điều đó thường không có lợi . Bạn không muốn bắt / xử lý điều đó, bạn muốn chương trình của mình chết ngay lập tức để có thể sửa chữa nó đúng cách.
Đây là một ý tưởng tồi. Trong thực tế, thậm chí bắt Exception
thường là một ý tưởng tồi. Hãy xem xét một ví dụ:
try {
inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(Throwable e) {
inputNumber = 10; //Default, user did not enter valid number
}
Bây giờ, giả sử rằng getUserInput () chặn một lúc và một luồng khác dừng luồng của bạn theo cách tồi tệ nhất có thể (nó gọi thread.stop ()). Khối bắt của bạn sẽ gặp ThreadDeath
lỗi. Thật là tệ. Hành vi của mã của bạn sau khi bắt được Ngoại lệ đó phần lớn là không xác định.
Một vấn đề tương tự xảy ra với việc bắt Exception. Có lẽgetUserInput()
không thành công do InterruptException hoặc ngoại lệ bị từ chối cấp quyền trong khi cố gắng ghi lại kết quả hoặc tất cả các loại lỗi khác. Bạn không biết điều gì đã xảy ra, vì vậy, bạn cũng không biết làm thế nào để khắc phục sự cố.
Bạn có ba lựa chọn tốt hơn:
1 - Nắm bắt chính xác (các) Ngoại lệ mà bạn biết cách xử lý:
try {
inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(ParseException e) {
inputNumber = 10; //Default, user did not enter valid number
}
2 - Rethrow bất kỳ ngoại lệ nào mà bạn gặp phải và không biết cách xử lý:
try {
doSomethingMysterious();
} catch(Exception e) {
log.error("Oh man, something bad and mysterious happened",e);
throw e;
}
3 - Sử dụng một khối cuối cùng để bạn không phải nhớ đánh lại:
Resources r = null;
try {
r = allocateSomeResources();
doSomething(r);
} finally {
if(r!=null) cleanUpResources(r);
}
throw new Exception("Some additional info, eg. userId " + userId, e);
. Điều này sẽ được đăng nhập trong một ngoại lệ tốt đẹp với 10 nguyên nhân.
Cũng cần lưu ý rằng khi bạn bắt Throwable
, bạn cũng có thể bắt InterruptedException
được cần phải điều trị đặc biệt. Xem Xử lý ngoại lệ bị gián đoạn để biết thêm chi tiết.
Nếu bạn chỉ muốn nắm bắt các trường hợp ngoại lệ không được chọn, bạn cũng có thể xem xét mẫu này
try {
...
} catch (RuntimeException exception) {
//do something
} catch (Error error) {
//do something
}
Bằng cách này, khi bạn sửa đổi mã của mình và thêm một lệnh gọi phương thức có thể đưa ra một ngoại lệ đã được kiểm tra, trình biên dịch sẽ nhắc bạn về điều đó và sau đó bạn có thể quyết định phải làm gì cho trường hợp này.
trực tiếp từ javadoc của lớp Lỗi (khuyến nghị không mắc các lỗi này):
* An <code>Error</code> is a subclass of <code>Throwable</code>
* that indicates serious problems that a reasonable application
* should not try to catch. Most such errors are abnormal conditions.
* The <code>ThreadDeath</code> error, though a "normal" condition,
* is also a subclass of <code>Error</code> because most applications
* should not try to catch it.
* A method is not required to declare in its <code>throws</code>
* clause any subclasses of <code>Error</code> that might be thrown
* during the execution of the method but not caught, since these
* errors are abnormal conditions that should never occur.
*
* @author Frank Yellin
* @version %I%, %G%
* @see java.lang.ThreadDeath
* @since JDK1.0
Đó không phải là một thực tiễn tồi nếu bạn hoàn toàn không thể có bong bóng ngoại lệ trong một phương pháp.
Đó là một thực tiễn tồi nếu bạn thực sự không thể xử lý ngoại lệ. Tốt hơn nên thêm "ném" vào chữ ký phương thức hơn là chỉ bắt và ném lại hoặc tệ hơn, bọc nó trong một RuntimeException và ném lại.
Throwable
trường hợp - ví dụ: ghi nhật ký ngoại lệ tùy chỉnh.
Đôi khi, việc nắm bắt Throwable là cần thiết nếu bạn đang sử dụng các thư viện ném lỗi một cách quá nhiệt tình, nếu không thư viện của bạn có thể giết ứng dụng của bạn.
Tuy nhiên, tốt nhất là trong những trường hợp này, chỉ nên chỉ định các lỗi cụ thể do thư viện ném ra, thay vì tất cả các Bảng ném.
Có thể ném là lớp cơ sở cho tất cả các lớp có thể được ném (không chỉ ngoại lệ). Bạn chỉ có thể làm rất ít điều nếu bắt gặp lỗi OutOfMemoryError hoặc KernelError (xem Khi nào bắt được java.lang.Error? )
bắt Ngoại lệ phải là đủ.
nó phụ thuộc vào logic của bạn hoặc cụ thể hơn cho các tùy chọn / khả năng của bạn. Nếu có bất kỳ ngoại lệ cụ thể nào mà bạn có thể phản ứng theo cách có ý nghĩa, bạn có thể nắm bắt nó trước và làm như vậy.
Nếu không có và bạn chắc chắn rằng bạn sẽ làm điều tương tự đối với tất cả các ngoại lệ và lỗi (ví dụ: thoát với thông báo lỗi), thì không vấn đề gì khi bắt được tệp có thể ném.
Thường thì trường hợp đầu tiên giữ được và bạn sẽ không bắt được vật ném được. Nhưng vẫn có rất nhiều trường hợp bắt nó hoạt động tốt.
Mặc dù nó được mô tả là một thực hành rất xấu, đôi khi bạn có thể tìm thấy những trường hợp hiếm hoi mà nó không chỉ hữu ích mà còn bắt buộc. Đây là hai ví dụ.
Trong một ứng dụng web, nơi bạn phải hiển thị trang lỗi đầy đủ có ý nghĩa cho người dùng. Mã này đảm bảo điều này xảy ra vì nó là một vấn đề lớn try/catch
xung quanh tất cả các trình xử lý yêu cầu của bạn (các servlet, hành động thanh chống hoặc bất kỳ bộ điều khiển nào ...)
try{
//run the code which handles user request.
}catch(Throwable ex){
LOG.error("Exception was thrown: {}", ex);
//redirect request to a error page.
}
}
Ví dụ khác, hãy xem xét bạn có một lớp dịch vụ phục vụ hoạt động kinh doanh chuyển tiền. Phương thức này trả về TransferReceipt
nếu quá trình chuyển được thực hiện hoặc NULL
nếu không thể.
String FoundtransferService.doTransfer( fundtransferVO);
Bây giờ hình ảnh bạn nhận được một List
khoản chuyển tiền từ người dùng và bạn phải sử dụng dịch vụ trên để thực hiện tất cả.
for(FundTransferVO fundTransferVO : fundTransferVOList){
FoundtransferService.doTransfer( foundtransferVO);
}
Nhưng điều gì sẽ xảy ra nếu có ngoại lệ xảy ra? Bạn không nên dừng lại, vì một lần chuyển có thể thành công và một lần không, bạn nên tiếp tục thông qua tất cả người dùng List
và hiển thị kết quả cho mỗi lần chuyển. Vì vậy, bạn kết thúc với mã này.
for(FundTransferVO fundTransferVO : fundTransferVOList){
FoundtransferService.doTransfer( foundtransferVO);
}catch(Throwable ex){
LOG.error("The transfer for {} failed due the error {}", foundtransferVO, ex);
}
}
Bạn có thể duyệt qua nhiều dự án mã nguồn mở để thấy rằng dự án đó throwable
thực sự được lưu trữ và xử lý. Ví dụ ở đây là một tìm kiếm của tomcat
, struts2
và primefaces
:
https://github.com/apache/tomcat/search?utf8=%E2%9C%93&q=catch%28Throwable https://github.com/apache/struts/search?utf8=%E2%9C%93&q=catch % 28Throwable https://github.com/primefaces/primefaces/search?utf8=%E2%9C%93&q=catch%28Throwable
throwable
, đó là câu hỏi này về là gì
Câu hỏi hơi mơ hồ; bạn đang hỏi "có được không để bắt Throwable
", hoặc "có được không để bắt một Throwable
và không làm gì cả"? Nhiều người ở đây đã trả lời câu sau, nhưng đó là một vấn đề phụ; 99% thời gian bạn không nên "tiêu thụ" hoặc loại bỏ các ngoại lệ, cho dù bạn đang đánh bắt Throwable
hoặc IOException
hoặc bất cứ điều gì.
Nếu bạn tuyên truyền ngoại lệ, câu trả lời (giống như câu trả lời cho rất nhiều câu hỏi) là "nó phụ thuộc". Nó phụ thuộc vào những gì bạn đang làm với ngoại lệ — tại sao bạn lại nắm bắt được nó.
Một ví dụ điển hình về lý do tại sao bạn muốn nắm bắt Throwable
là cung cấp một số loại dọn dẹp nếu có bất kỳ lỗi nào. Ví dụ: trong JDBC, nếu lỗi xảy ra trong một giao dịch, bạn sẽ muốn khôi phục giao dịch:
try {
…
} catch(final Throwable throwable) {
connection.rollback();
throw throwable;
}
Lưu ý rằng ngoại lệ không bị loại bỏ mà được truyền lại.
Nhưng như một chính sách chung, bắt Throwable
bởi vì bạn không có lý do và quá lười biếng để xem những ngoại lệ cụ thể nào đang được đưa ra là một hình thức tồi và một ý tưởng tồi.
Nói chung, bạn muốn tránh bị bắt Error
nhưng tôi có thể nghĩ đến (ít nhất) hai trường hợp cụ thể khi làm như vậy là thích hợp:
AssertionError
những vô hại.Nếu chúng ta sử dụng có thể ném , thì nó cũng bao gồm Lỗi và thế là xong.
Thí dụ.
public class ExceptionTest {
/**
* @param args
*/
public static void m1() {
int i = 10;
int j = 0;
try {
int k = i / j;
System.out.println(k);
} catch (Throwable th) {
th.printStackTrace();
}
}
public static void main(String[] args) {
m1();
}
}
Đầu ra:
java.lang.ArithmeticException: / by zero
at com.infy.test.ExceptionTest.m1(ExceptionTest.java:12)
at com.infy.test.ExceptionTest.main(ExceptionTest.java:25)
Có thể ném là lớp cha của tất cả các lỗi và loại trừ. Nếu bạn sử dụng Throwable trong một mệnh đề bắt, nó sẽ không chỉ bắt tất cả các ngoại lệ mà còn bắt tất cả các lỗi. Các lỗi được đưa ra bởi JVM để chỉ ra các vấn đề nghiêm trọng mà một ứng dụng không có ý định xử lý. Ví dụ điển hình cho điều đó là OutOfMemoryError hoặc StackOverflowError. Cả hai đều do các tình huống nằm ngoài tầm kiểm soát của ứng dụng và không thể xử lý được. Vì vậy, bạn không nên bắt Throwable trừ khi bạn khá tự tin rằng nó sẽ chỉ là một ngoại lệ nằm bên trong Throwable.
Mặc dù việc bắt Throwable nói chung là một phương pháp không tốt (như đã được giải thích bằng rất nhiều câu trả lời cho câu hỏi này), các tình huống mà việc bắt Throwable
là hữu ích là khá phổ biến. Hãy để tôi giải thích một trường hợp mà tôi sử dụng trong công việc của mình, với một ví dụ đơn giản.
Hãy xem xét một phương pháp thực hiện phép cộng hai số và sau khi cộng thành công, nó sẽ gửi thông báo qua email đến một số người nhất định. Giả sử rằng số được trả về là quan trọng và được sử dụng bởi phương thức gọi.
public Integer addNumbers(Integer a, Integer b) {
Integer c = a + b; //This will throw a NullPointerException if either
//a or b are set to a null value by the
//calling method
successfulAdditionAlert(c);
return c;
}
private void successfulAdditionAlert(Integer c) {
try {
//Code here to read configurations and send email alerts.
} catch (Throwable e) {
//Code to log any exception that occurs during email dispatch
}
}
Mã để gửi cảnh báo qua email đọc rất nhiều cấu hình hệ thống và do đó, có thể có nhiều trường hợp ngoại lệ được đưa ra từ khối mã đó. Nhưng chúng tôi không muốn bất kỳ ngoại lệ nào gặp phải trong quá trình gửi cảnh báo truyền đến phương thức người gọi, vì phương thức đó chỉ quan tâm đến tổng của hai giá trị Số nguyên mà nó cung cấp. Do đó, mã để gửi thông báo qua email được đặt trong một try-catch
khối, nơi Throwable
được bắt và mọi trường hợp ngoại lệ chỉ được ghi lại, cho phép phần còn lại của quy trình tiếp tục.
Exception
s bằng mọi cách, nhưng không Throwable
.