Câu trả lời:
Nói chung, không bao giờ.
Tuy nhiên, đôi khi bạn cần phải bắt lỗi cụ thể.
Nếu bạn đang viết mã khung-ish (tải các lớp của bên thứ 3), có thể nên nắm bắt LinkageError
(không tìm thấy lớp def, liên kết không thỏa mãn, thay đổi lớp không tương thích).
Tôi cũng đã thấy một số mã lớp thứ ba ngu ngốc ném các lớp con Error
, vì vậy bạn cũng sẽ phải xử lý chúng.
Nhân tiện, tôi không chắc là không thể phục hồi OutOfMemoryError
.
Không bao giờ. Bạn không bao giờ có thể chắc chắn rằng ứng dụng có thể thực thi dòng mã tiếp theo. Nếu bạn nhận được một OutOfMemoryError
, bạn không có gì đảm bảo rằng bạn sẽ có thể làm bất cứ điều gì đáng tin cậy . Bắt RuntimeException và kiểm tra Ngoại lệ, nhưng không bao giờ Lỗi.
boolean assertionsEnabled = false; assert assertionsEnabled = true;
Nói chung bạn nên luôn luôn bắt java.lang.Error
và ghi nó vào một bản ghi hoặc hiển thị nó cho người dùng. Tôi làm việc trong bộ phận hỗ trợ và thấy hàng ngày rằng các lập trình viên không thể biết chuyện gì đã xảy ra trong một chương trình.
Nếu bạn có một chủ đề daemon thì bạn phải ngăn chặn nó bị chấm dứt. Trong các trường hợp khác, ứng dụng của bạn sẽ hoạt động chính xác.
Bạn chỉ nên bắt java.lang.Error
ở mức cao nhất.
Nếu bạn nhìn vào danh sách các lỗi bạn sẽ thấy rằng hầu hết có thể được xử lý. Ví dụ: ZipError
xảy ra khi đọc các tệp zip bị hỏng.
Các lỗi phổ biến nhất là OutOfMemoryError
và NoClassDefFoundError
, cả hai trong hầu hết các trường hợp vấn đề thời gian chạy.
Ví dụ:
int length = Integer.parseInt(xyz);
byte[] buffer = new byte[length];
có thể tạo ra OutOfMemoryError
nhưng đó là một vấn đề thời gian chạy và không có lý do gì để chấm dứt chương trình của bạn.
NoClassDefFoundError
xảy ra hầu hết nếu không có thư viện hoặc nếu bạn làm việc với phiên bản Java khác. Nếu nó là một phần tùy chọn trong chương trình của bạn thì bạn không nên chấm dứt chương trình của mình.
Tôi có thể đưa ra nhiều ví dụ nữa về lý do tại sao nên nắm bắt Throwable
ở cấp cao nhất và tạo ra một thông báo lỗi hữu ích.
OutOfMemoryError
không phải là lỗi thời gian chạy, không có gì đảm bảo rằng ứng dụng có thể phục hồi từ nó. Nếu bạn may mắn, bạn có thể nhận được OOM new byte[largeNumber]
nhưng nếu phân bổ đó không đủ để gây ra OOM, nó có thể được kích hoạt trong dòng tiếp theo hoặc chuỗi tiếp theo. Đây là vấn đề thời gian chạy vì nếu length
đầu vào không đáng tin cậy thì nó phải được xác nhận trước khi gọi new byte[]
.
NoClassDefFoundError
có thể xảy ra ở bất cứ đâu , vì nó được gọi khi mã java được biên dịch không thể tìm thấy một lớp. Nếu JDK của bạn bị định cấu hình sai, nó có thể kích hoạt việc cố gắng sử dụng java.util.*
lớp và thực tế không thể lập trình chống lại nó. Nếu bạn tùy chọn bao gồm một phụ thuộc, bạn nên sử dụng ClassLoader
để kiểm tra xem nó có tồn tại hay không ClassNotFoundException
.
ZipError
chỉ ra rằng tệp jar chứa các lớp là một tệp zip bị hỏng. Đây là một vấn đề khá nghiêm trọng và tại thời điểm này, bạn không thể tin tưởng bất kỳ mã nào được thực thi và đó là điều vô trách nhiệm khi cố gắng "phục hồi" từ nó.
java.lang.Error
hoặc java.lang.Throwable
ở cấp cao nhất và cố gắng làm điều gì đó với nó - nói rằng đăng nhập một thông báo lỗi. Nhưng tại thời điểm đó, không có gì đảm bảo rằng điều này sẽ được thực thi. Nếu JVM của bạn là OOMing, cố gắng đăng nhập có thể phân bổ nhiều String
s hơn để kích hoạt một OOM khác.
Trong môi trường đa luồng, bạn thường muốn bắt nó nhất! Khi bạn bắt được nó, đăng nhập nó và chấm dứt toàn bộ ứng dụng! Nếu bạn không làm điều đó, một số chủ đề có thể đang thực hiện một phần quan trọng sẽ bị chết và phần còn lại của ứng dụng sẽ nghĩ rằng mọi thứ đều bình thường. Trong số đó, nhiều tình huống không mong muốn có thể xảy ra. Một vấn đề nhỏ nhất là bạn sẽ không thể dễ dàng tìm ra gốc rễ của vấn đề, nếu các luồng khác bắt đầu đưa ra một số ngoại lệ vì một luồng không hoạt động.
Ví dụ: vòng lặp thường là:
try {
while (shouldRun()) {
doSomething();
}
}
catch (Throwable t) {
log(t);
stop();
System.exit(1);
}
Ngay cả trong một số trường hợp, bạn sẽ muốn xử lý các Lỗi khác nhau, ví dụ, trên OutOfMemoryError, bạn có thể đóng ứng dụng thường xuyên (thậm chí có thể giải phóng một số bộ nhớ và tiếp tục), đối với một số người khác, bạn không thể làm gì nhiều.
OutOfMemoryError
và tiếp tục thay vì hiện tại kịp thời là không khôn ngoan vì chương trình của bạn sau đó ở trạng thái không xác định .
Một Error
thường không nên bị bắt , vì nó chỉ ra một tình trạng bất thường không bao giờ nên xảy ra .
Từ Đặc tả API Java cho Error
lớp:
An
Error
là một lớp conThrowable
chỉ ra những vấn đề nghiêm trọng mà một ứng dụng hợp lý không nên cố gắng nắm bắt. Hầu hết các lỗi như vậy là điều kiện bất thường. [...]Một phương thức không bắt buộc phải khai báo trong mệnh đề ném của nó bất kỳ lớp con Lỗi nào có thể bị ném trong quá trình thực thi phương thức nhưng không bị bắt, vì các lỗi này là điều kiện bất thường không bao giờ xảy ra.
Như đặc tả đề cập, một Error
chỉ được ném trong các trường hợp là Cơ hội, khi Error
xảy ra, có rất ít ứng dụng có thể làm được và trong một số trường hợp, chính Máy ảo Java có thể ở trạng thái không ổn định (như VirtualMachineError
)
Mặc dù an Error
là một lớp con Throwable
có nghĩa là nó có thể bị bắt bởi một try-catch
mệnh đề, nhưng có lẽ nó không thực sự cần thiết, vì ứng dụng sẽ ở trạng thái bất thường khi Error
bị JVM ném.
Cũng có một phần ngắn về chủ đề này trong Phần 11.5 Phân cấp ngoại lệ của Đặc tả ngôn ngữ Java, Phiên bản 2 .
Và có một vài trường hợp khác nếu bạn gặp Lỗi, bạn phải điều chỉnh lại . Ví dụ ThreadDeath không bao giờ bị bắt, điều này có thể gây ra vấn đề lớn là bạn bắt nó trong một môi trường chứa (ví dụ: một máy chủ ứng dụng):
Một ứng dụng chỉ nên bắt các thể hiện của lớp này nếu nó phải dọn sạch sau khi bị chấm dứt không đồng bộ. Nếu ThreadDeath bị bắt bởi một phương thức, điều quan trọng là nó phải được lưu lại để luồng thực sự chết.
Error
s.
Rất, rất hiếm khi.
Tôi đã làm nó chỉ cho một trường hợp rất cụ thể được biết đến. Ví dụ: java.lang.UnsatisfiedLinkError có thể bị ném nếu hai ClassLoader độc lập tải cùng một DLL. (Tôi đồng ý rằng tôi nên chuyển JAR sang trình nạp lớp chung)
Nhưng trường hợp phổ biến nhất là bạn cần đăng nhập để biết điều gì đã xảy ra khi người dùng đến khiếu nại. Bạn muốn một tin nhắn hoặc một cửa sổ bật lên cho người dùng, sau đó âm thầm chết.
Ngay cả lập trình viên trong C / C ++, họ cũng gặp lỗi và thông báo điều gì đó mà mọi người không hiểu trước khi thoát (ví dụ như lỗi bộ nhớ).
Trong một ứng dụng Android, tôi bắt gặp java.lang.VerifyError . Một thư viện mà tôi đang sử dụng sẽ không hoạt động trong các thiết bị có phiên bản cũ của HĐH và mã thư viện sẽ gây ra lỗi như vậy. Tất nhiên tôi có thể tránh lỗi bằng cách kiểm tra phiên bản HĐH khi chạy, nhưng:
Tốt nhất chúng ta không nên xử lý / bắt lỗi. Nhưng có thể có những trường hợp chúng ta cần phải làm, dựa trên yêu cầu của khung hoặc ứng dụng. Giả sử tôi có một trình nền XML Parser thực hiện DOM Parser tiêu tốn nhiều Bộ nhớ hơn. Nếu có một yêu cầu như luồng Parser thì không nên chết khi nhận được OutOfMemoryError , thay vào đó, nó sẽ xử lý nó và gửi tin nhắn / mail cho quản trị viên của ứng dụng / khung.
Có một lỗi khi JVM không hoạt động như mong đợi hoặc đang trên bờ vực. Nếu bạn bắt lỗi, không có gì đảm bảo rằng khối bắt sẽ chạy, và thậm chí ít hơn là nó sẽ chạy cho đến hết.
Nó cũng sẽ phụ thuộc vào máy tính đang chạy, trạng thái bộ nhớ hiện tại, vì vậy không có cách nào để kiểm tra, thử và làm hết sức mình. Bạn sẽ chỉ có một kết quả xấu.
Bạn cũng sẽ hạ cấp khả năng đọc mã của bạn.