ném ngoại lệ thời gian chạy trong ứng dụng Java


11

Tôi đang làm việc như một nhà thầu thiết kế ứng dụng Java doanh nghiệp cho khách hàng của mình trong vai trò lãnh đạo kỹ thuật. Ứng dụng sẽ được sử dụng bởi người dùng cuối và sẽ có một nhóm hỗ trợ sẽ hỗ trợ ứng dụng khi chúng tôi rời đi.

Các lãnh đạo kỹ thuật khác mà tôi đang làm việc có ấn tượng rằng việc xử lý ngoại lệ sẽ làm cho mã bị bẩn. Hệ thống chỉ nên ném các ngoại lệ được kiểm tra từ tầng Dịch vụ và phần còn lại của mã sẽ ném ngoại lệ thời gian chạy từ tất cả các tầng khác để không cần xử lý các ngoại lệ không được kiểm tra.

Cần bao giờ để ném một ngoại lệ không được kiểm soát trong một ứng dụng kinh doanh?

Từ kinh nghiệm của tôi trong quá khứ về các ngoại lệ thời gian chạy:

1) Các ngoại lệ không được kiểm tra làm cho mã không thể đoán trước được vì chúng không hiển thị ngay cả trong Javadoc.

2) Ném ngoại lệ không được kiểm tra trong ứng dụng kinh doanh là vô nghĩa bởi vì khi bạn ném nó và nó đi thẳng vào mặt người dùng, làm thế nào để bạn giải thích nó với người dùng? Tôi đã thấy đủ ứng dụng web hiển thị 500 -Internal Error. Contact Administratorkhông có nghĩa gì đối với người dùng cuối hoặc nhóm hỗ trợ quản lý ứng dụng.

3) Ném ngoại lệ thời gian chạy buộc người dùng của lớp ném ngoại lệ đi qua mã nguồn để gỡ lỗi và xem tại sao ngoại lệ được ném. Điều này chỉ có thể tránh được nếu Javadoc của ngoại lệ thời gian chạy xảy ra được ghi lại một cách xuất sắc mà tôi thấy không bao giờ là trường hợp.


Bạn có thể giải thích Cấp dịch vụ là gì không? Là lớp xa nhất từ ​​người dùng, hoặc gần nhất với người dùng?
Manoj R

Tại sao câu hỏi này không phù hợp với SO?
Bjarke Freund-Hansen

@Manoj R: Không. Tầng hoặc lớp dịch vụ là nơi các quy tắc kinh doanh và logic được thể hiện, tách biệt với cả hai chi tiết về công nghệ DB và trình bày máy khách.
Michael Borgwardt

6
Các ngoại lệ không được kiểm tra [...] không hiển thị ngay cả trong Javadoc. => chúng sẽ xuất hiện nếu bạn ghi lại chúng bằng @throwsthẻ, bằng cách này là một cách thực hành tốt.
assylias

4
@SureshKoya Java hiệu quả, Mục 62: Tài liệu tất cả các ngoại lệ được ném bởi mỗi phương thức. Trích xuất: Sử dụng @throwsthẻ Javadoc để ghi lại từng ngoại lệ không được kiểm tra mà phương thức có thể ném, nhưng không sử dụng từ khóa ném để bao gồm các ngoại lệ không được kiểm tra trong khai báo phương thức.
assylias

Câu trả lời:


12

Các ngoại lệ được kiểm tra là một ngoại lệ thất bại trong thiết kế ngôn ngữ. Họ buộc bạn phải có sự trừu tượng cực kỳ rò rỉ và mã bẩn. Họ nên tránh càng nhiều càng tốt.

Về điểm của bạn:

1) Các ngoại lệ được kiểm tra làm cho mã bị bẩn và không thể đoán trước được vì chúng xuất hiện ở mọi nơi .

2) Làm thế nào một ngoại lệ được kiểm tra được hiển thị cho người dùng tốt hơn? Sự khác biệt thực sự duy nhất giữa các ngoại lệ được kiểm tra và không được kiểm tra là về mặt kỹ thuật và chỉ ảnh hưởng đến mã nguồn.

3) Bạn đã bao giờ nghe nói về dấu vết ngăn xếp chưa? Họ cho bạn biết chính xác nơi ngoại lệ được ném, bất kể nó được kiểm tra hay không được kiểm tra. Trên thực tế, các trường hợp ngoại lệ được kiểm tra có xu hướng tồi tệ hơn khi gỡ lỗi vì chúng thường được bọc dẫn đến dấu vết ngăn xếp dài hơn và xấu hơn, hoặc thậm chí bị mất hoàn toàn vì việc gói sai.

Có hai loại ngoại lệ: những trường hợp xảy ra "bình thường" và thường được xử lý rất gần với nơi chúng xảy ra và những trường hợp đặc biệt thực sự và có thể được xử lý chung trong một lớp rất cao (chỉ cần hủy bỏ hành động hiện tại và ghi lại / hiển thị một lỗi).

Các ngoại lệ được kiểm tra là một nỗ lực để đưa sự khác biệt này vào cú pháp ngôn ngữ tại thời điểm các ngoại lệ được xác định. Vấn đề với điều này là

  • Sự khác biệt thực sự phụ thuộc vào người gọi , không phải là mã ném ngoại lệ
  • Nó hoàn toàn trực giao với ý nghĩa ngữ nghĩa của một ngoại lệ, nhưng việc buộc nó vào hệ thống phân cấp lớp buộc bạn phải kết hợp cả hai
  • Toàn bộ điểm của các trường hợp ngoại lệ là bạn có thể quyết định ở mức nào để bắt chúng mà không có nguy cơ mất một lỗi âm thầm hoặc phải làm ô nhiễm mã ở cấp độ trung gian hoặc; kiểm tra ngoại lệ mất lợi thế thứ hai.

1. Trong trường hợp ngoại lệ không được kiểm tra, bạn thậm chí sẽ không biết liệu cuộc gọi có thể dẫn đến việc không được kiểm tra hay không. 2. Khi bạn nói "Nó hoàn toàn trực giao với ý nghĩa ngữ nghĩa của một ngoại lệ, nhưng việc buộc nó vào hệ thống phân cấp lớp buộc bạn phải kết hợp cả hai", tôi cho rằng bạn có nghĩa là gọi thứ bậc thay vì phân cấp lớp.
randominstanceOfLivingThing

2
@Suresh Koya: Không, ý tôi là hệ thống phân cấp lớp, thực tế là một khai báo SQLException extends Exceptioncó nghĩa là chọn ném SQLExceptionvì một lỗi phải thực hiện với truy cập DB tự động có nghĩa là ngoại lệ được kiểm tra. Và bạn có thể ghi lại các trường hợp ngoại lệ không được kiểm tra trong các bình luận Javadoc. Hoạt động tốt cho tất cả các ngôn ngữ khác.
Michael Borgwardt

1
@MichaelBorgwardt "Các trường hợp ngoại lệ được kiểm tra là một sự thất bại trong thiết kế ngôn ngữ", đó có phải là ý kiến ​​cá nhân của bạn không nếu tôi muốn đọc thêm về nó. Bất kỳ liên kết xác thực sẽ được giúp đỡ tuyệt vời.
Vipin

2
@Vipin: Đó là ý kiến ​​cá nhân của tôi, nhưng một ý kiến ​​mà nhiều người khác chia sẻ, ví dụ Bruce Eckel: mindview.net/Etc/Discussions/CheckedExceptions - và thực tế là không có ngôn ngữ nào khác áp dụng các ngoại lệ được kiểm tra trong 20 năm kể từ khi Java được giới thiệu cho chính nó ...
Michael Borgwardt

2

Quan điểm của tôi là loại ngoại lệ nào được ném phụ thuộc vào mã của bạn đang làm gì.

Tôi ném các ngoại lệ được kiểm tra khi tôi cho rằng chúng có thể xảy ra khá thường xuyên (ví dụ người dùng nhập dữ liệu tinh ranh) và tôi hy vọng mã cuộc gọi sẽ xử lý tình huống.

Tôi ném các ngoại lệ không được kiểm tra / thời gian chạy (không thường xuyên như đã kiểm tra) khi tôi gặp một tình huống hiếm gặp mà tôi không mong đợi mã cuộc gọi sẽ xử lý. Một ví dụ có thể là một số lỗi liên quan đến bộ nhớ kỳ lạ mà tôi không bao giờ nghĩ sẽ xảy ra. Không được kiểm tra là các loại lỗi mà bạn mong đợi để đưa ứng dụng xuống.

Không có ngoại lệ sẽ xuất hiện trước mặt người dùng mà không có một số mức hỗ trợ. Ngay cả khi đó chỉ là "vui lòng cắt và dán lỗi này vào email". Không có gì khó chịu với người dùng hơn là được thông báo là có lỗi, nhưng không được cung cấp thông tin chi tiết hoặc hành động nào để bắt đầu sửa lỗi.

Tôi đoán là triết lý mà bạn đề cập đến từ một trong hai nguồn:

  • Lập trình viên lười biếng cố gắng tránh làm việc.
  • Hoặc các nhà phát triển đã phải hỗ trợ mã đi theo cách khác. tức là xử lý lỗi quá mức. Loại mã chứa số lượng lớn xử lý ngoại lệ, phần lớn không có gì, hoặc thậm chí tệ hơn, được sử dụng để kiểm soát luồng và các mục đích không chính xác khác.

Nếu một cái gì đó có thể xảy ra khá thường xuyên, nó không phải là ngoại lệ. Nhưng đồng ý rằng lỗi không nên được ném, mà người dùng không có manh mối về.
Manoj R

Tôi đồng ý về triết lý của bạn. Trên một ứng dụng mà bạn đang phát triển cho người dùng, cần gì để ném ngoại lệ Runtime?
randominstanceOfLivingThing

Ngoài ra, ngay cả khi một ngoại lệ Runtime được ném ra, tôi thích quy ước @assylias được trỏ đến.
randominstanceOfLivingThing

1

Nếu bạn không muốn ngoại lệ thời gian chạy không được kiểm tra, vậy tại sao bạn sử dụng java? Có khả năng các ngoại lệ thời gian chạy hầu như ở mọi nơi - đáng chú ý là các ngoại lệ NullPulumException, ArithaturesException, ArrayIndexOutOfBound, v.v.

Và khi bạn đã từng đọc các tệp nhật ký của một số hệ thống J2EE, chẳng hạn như cài đặt SAP NetWeaver, bạn sẽ thấy các ngoại lệ như vậy xảy ra theo nghĩa đen mọi lúc.


Từ đặc tả ngôn ngữ Java: "Các lớp ngoại lệ thời gian chạy (RuntimeException và các lớp con của nó) được miễn kiểm tra thời gian biên dịch bởi vì, theo đánh giá của các nhà thiết kế ngôn ngữ lập trình Java, việc phải khai báo các ngoại lệ như vậy sẽ không hỗ trợ đáng kể trong việc thiết lập tính chính xác của nhiều chương trình. Nhiều hoạt động và cấu trúc của ngôn ngữ lập trình Java có thể dẫn đến ngoại lệ thời gian chạy. "
randominstanceOfLivingThời gian

1
Ngoại lệ thời gian chạy mà bạn đang nói là những ngoại lệ phát sinh từ Java do hệ thống thời gian chạy Java không có manh mối về lý do tại sao bạn đang cố gắng thực hiện cuộc gọi cụ thể. Ví dụ: Một NullPulumException sẽ phát sinh nếu bạn gọi một phương thức là null. Trong trường hợp đó, hệ thống thời gian chạy Java không có một từ đúng cho các lập trình viên một cách điên rồ và sẽ tát người gọi bằng một NullPulumException. Điều đáng buồn là người gọi đã bán sản phẩm Netweaver cho bạn và bạn với tư cách là một người dùng hiện đang là nạn nhân của chương trình kém. Những người kiểm tra cũng đổ lỗi không kém vì họ không làm tất cả các bài kiểm tra ranh giới.
randominstanceOfLivingThing

1

Có một vài quy tắc xử lý ngoại lệ mà bạn nên ghi nhớ. Nhưng trước tiên, bạn cần nhớ rằng các ngoại lệ là một phần của giao diện được hiển thị bởi mã; tài liệu cho họ . Điều này đặc biệt quan trọng khi giao diện là một giao diện công cộng, tất nhiên, nhưng đó cũng là một ý tưởng rất tốt trong giao diện riêng tư.

Các ngoại lệ chỉ nên được xử lý tại điểm mà mã có thể làm điều gì đó hợp lý với chúng. Tùy chọn xử lý tồi tệ nhất là không làm gì cả về chúng, điều này chỉ nên được thực hiện khi đó chính xác là tùy chọn chính xác. (Khi tôi gặp tình huống như vậy trong mã của mình, tôi bao gồm một nhận xét về hiệu ứng đó để tôi biết không phải lo lắng về cơ thể trống rỗng.)

Tùy chọn tồi tệ thứ hai là ném một ngoại lệ không liên quan mà không có nguyên nhân kèm theo. Vấn đề ở đây là thông tin trong ngoại lệ ban đầu cho phép chẩn đoán vấn đề bị mất; bạn đang tạo ra thứ gì đó mà không ai có thể làm được gì (ngoài việc phàn nàn rằng, nó không hoạt động, và tất cả chúng ta đều biết chúng ta ghét những báo cáo lỗi đó như thế nào ).

Tốt hơn nhiều là đăng nhập ngoại lệ. Điều đó cho phép ai đó tìm ra vấn đề là gì và khắc phục nó, nhưng bạn chỉ nên đăng nhập ngoại lệ tại điểm mà nếu không nó sẽ bị mất hoặc được báo cáo qua kết nối bên ngoài. Đó không phải là vì đăng nhập thường xuyên là một vấn đề lớn như vậy, mà là vì đăng nhập quá nhiều có nghĩa là bạn chỉ cần nhật ký tiêu tốn nhiều dung lượng hơn mà không chứa nhiều thông tin hơn. Khi bạn đã đăng nhập ngoại lệ, bạn có thể báo cáo một PRECIS cho người dùng / khách hàng với một lương tâm tốt (miễn là bạn cũng bao gồm thời điểm phát sinh - hoặc nhận dạng mối tương quan khác - trong báo cáo đó để phiên bản ngắn có thể được xuất hiện lên với các chi tiết nếu cần thiết).

Tất nhiên, lựa chọn tốt nhất là xử lý hoàn toàn ngoại lệ, xử lý toàn bộ tình huống lỗi. Nếu bạn có thể làm điều này, bằng mọi cách hãy làm điều đó! Nó thậm chí có thể có nghĩa là bạn có thể tránh phải đăng nhập ngoại lệ.

Một cách để xử lý một ngoại lệ là đưa ra một ngoại lệ khác cung cấp mô tả vấn đề ở cấp độ cao hơn (ví dụ, failed to initializetrong thời gian thay vì phạm lỗi index out of bounds). Đây là một mô hình tốt miễn là bạn không bị mất thông tin về nguyên nhân của ngoại lệ; sử dụng ngoại lệ chi tiết để khởi tạo causengoại lệ cấp cao hơn hoặc ghi nhật ký chi tiết (như đã thảo luận ở trên). Ghi nhật ký là thích hợp nhất khi bạn sắp vượt qua một ranh giới giữa các quá trình, như cuộc gọi IPC, bởi vì không có gì đảm bảo rằng lớp ngoại lệ cấp thấp sẽ xuất hiện ở đầu kia của kết nối. Giữ như một nguyên nhân kèm theo là thích hợp nhất khi vượt qua một ranh giới nội bộ.

Một mô hình khác mà bạn thấy là bắt và phát hành:

try {
    // ...
} catch (FooException e) {
    throw e;
}

Đây là một kiểu chống trừ khi bạn có các ràng buộc kiểu từ các catchmệnh đề khác, điều đó có nghĩa là bạn không thể để ngoại lệ tự đi qua. Sau đó, nó chỉ là một tính năng xấu của Java.

Không có sự khác biệt thực sự giữa các ngoại lệ được kiểm tra và các ngoại lệ được kiểm tra ngoài thực tế là bạn phải khai báo các ngoại lệ được kiểm tra có ranh giới phương thức chéo. Vẫn là một ý tưởng tốt để ghi lại các trường hợp ngoại lệ không được kiểm tra (với @throwsnhận xét javadoc) nếu bạn biết rằng chúng bị ném một cách có chủ ý bởi mã của bạn. Đừng cố tình ném java.lang.Errorhoặc các lớp con của nó (trừ khi bạn đang viết một triển khai JVM).

Ý kiến: Một trường hợp lỗi không mong muốn luôn thể hiện lỗi trong mã của bạn. Các ngoại lệ được kiểm tra là một cách để quản lý mối đe dọa này và khi các nhà phát triển cố tình sử dụng các ngoại lệ không được kiểm tra như một cách để tránh rắc rối xử lý các trường hợp lỗi, bạn đang xây dựng nhiều khoản nợ kỹ thuật mà bạn sẽ phải dọn dẹp một thời gian nếu bạn muốn mã mạnh mẽ. Xử lý lỗi cẩu thả là không chuyên nghiệp (và nhìn vào xử lý lỗi là một cách tốt để xác định một lập trình viên thực sự giỏi như thế nào).


0

Tôi nghĩ bạn chỉ nên ném ngoại lệ được kiểm tra khi ứng dụng có thể phục hồi. Vì vậy, người dùng thư viện của bạn phải nắm bắt những ngoại lệ đó và làm những gì cần thiết để khôi phục. Bất cứ điều gì khác nên được bỏ chọn.

Ví dụ

Nếu ứng dụng của bạn tải dữ liệu từ tệp hoặc cơ sở dữ liệu. Sau đó,..

try {
  File data = new File(...);
  // parse file here
} catch (Exception ex) {
  throw new MissingDataFileException("data file not found");
}

người gọi luôn có thể bắt được MissingDataFileException đã kiểm tra và sau đó thử tải dữ liệu từ cơ sở dữ liệu.

try {
  Connection con = DriverManager.getConnection( host, username, password );
  // query data here
} catch (Exception ex) {
  throw new RuntimeException("better call Saul!");
}

1
Sau-lơ chết trong một vụ tai nạn xe hơi 3 năm trước. Thất bại! ;-)
Donal Fellows
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.