Tại sao người Java thường sử dụng các ngoại lệ một cách âm thầm?


81

Tôi chưa bao giờ viết mã Java nghiêm túc trước đây, nhưng tôi đã học cú pháp, thư viện và khái niệm dựa trên các kỹ năng hiện có của mình (Delphi & C #). Một điều tôi khó hiểu là tôi đã thấy quá nhiều mã âm thầm sử dụng các ngoại lệ sauprintStackTrace như thế này:

    public void process() {
        try {
            System.out.println("test");
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

Có những đoạn mã tương tự như đoạn mã này trong hầu hết các bài báo và dự án Java mà tôi đã chạy. Dựa trên kiến ​​thức của tôi thì điều này rất tệ. Ngoại lệ hầu như luôn luôn được chuyển tiếp đến ngữ cảnh bên ngoài như sau:

    public void process() {
        try {
            System.out.println("test");
        } catch(Exception e) {
            e.printStackTrace();
            throw new AssertionError(e);
        }
    }

Hầu hết thời gian ngoại lệ sẽ được xử lý ở vòng ngoài cùng thuộc về khung cơ bản (ví dụ: Java Swing). Tại sao nó giống như tiêu chuẩn để viết mã như thế này trong thế giới Java? Tôi đang phân vân.

Dựa trên nền tảng của tôi, tôi muốn loại bỏ printStackTrace hoàn toàn . Tôi chỉ đơn giản là sẽ ném lại dưới dạng một aka chưa được xử lý RuntimeException(hoặc, thậm chí tốt hơn, AssertionError), sau đó bắt và ghi lại nó ở nơi thích hợp nhất: vòng lặp ngoài cùng của khung.

    public void process() {
        try {
            System.out.println("test");
        } catch(Exception e) {
            throw new AssertionError(e);
        }
    }

56
Tôi muốn nói rằng nó không thực sự âm thầm khi dấu vết ngăn xếp được in :)
willcodejavaforfood

13
Tôi có thể trích dẫn câu nói của Isaac Waller "Nhưng chương trình của bạn không dừng lại và vẫn tiếp tục, hầu hết có thể ở trạng thái không xác định"
Sake

15
@willcodejavaforfood, Nhận xét của bạn nhận được rất nhiều phiếu bầu và điều đó càng khiến tôi khó hiểu hơn về tư duy java điển hình.
Sake

13
@willcodejavaforfood - nó im lặng theo quan điểm của người gọi. Người gọi không biết có chuyện gì đã xảy ra. Bây giờ nếu đó là những gì dự định, thì tốt thôi, nhưng nói chung, đó là một tin xấu. Ngoài ra - hãy nghĩ về những gì xảy ra trong một applet - người dùng sẽ không bao giờ nhìn thấy đầu ra (trừ khi họ tình cờ mở bảng điều khiển java, điều này không bao giờ). Ok, vì vậy không ai sử dụng applet nữa;) Tuy nhiên, tương tự cho servletm - người dùng cuối không biết bất cứ điều gì đã xảy ra - nó chỉ được lưu trữ trong nhật ký.
Scott Stanchfield

4
Việc ném ngoại lệ trong Java rất phổ biến đến nỗi hầu hết các lập trình viên Java bỏ qua chúng bởi vì nếu bạn thực hiện việc bắt lỗi chính xác thì bạn sẽ cần phải viết nhiều mã hơn ... vì vậy về cơ bản tất cả những lập trình viên java đó đều làm điều này bởi vì họ lười biếng.
Trevor Boyd Smith

Câu trả lời:


203

Tôi đã luôn nghĩ, điều đó tương tự như tình huống sau:

“Một người đàn ông bị bắn.

Anh ta nín thở và có đủ sức để đi xe buýt.

10 dặm sau, người đàn ông được ra khỏi xe buýt, đi một vài khối và chết."

Khi cảnh sát đến nơi xác minh, họ không có manh mối về những gì vừa xảy ra. Cuối cùng họ có thể có nhưng khó hơn nhiều.

Tốt hơn là:

"Một người đàn ông bị bắn và anh ta chết ngay lập tức, và thi thể nằm chính xác nơi án mạng vừa xảy ra."

Khi cảnh sát đến, tất cả các bằng chứng đã được đưa ra.

Nếu một hệ thống bị lỗi, tốt hơn là hỏng nhanh

Giải quyết câu hỏi:

  1. Sự ngu dốt.
      +
  2. Sự lười biếng

BIÊN TẬP:

Tất nhiên, phần bắt là hữu ích.

Nếu điều gì đó có thể được thực hiện với ngoại lệ, đó là nơi nó nên được thực hiện.

Có lẽ đó KHÔNG phải là ngoại lệ đối với mã đã cho, có lẽ nó là thứ được mong đợi (và theo cách ví von của tôi thì nó giống như một chiếc áo khoác chống đạn, và người đàn ông đã chờ phát súng ngay từ đầu).

Và vâng, lệnh bắt có thể được sử dụng để ném các ngoại lệ phù hợp với phần trừu tượng


2
Nuốt các trường hợp ngoại lệ không thất bại, đừng quên thất bại nhanh chóng. Mặt khác, nếu hợp đồng phương thức của bạn có tham số là một chuỗi các chữ số và bạn đang phân tích cú pháp nó thành một Số nguyên, bạn có thể nuốt ParseException. Nếu bạn đang viết một chức năng cụ thể, hãy xác định một ngoại lệ tùy chỉnh cho chức năng đó không thành công, sau đó bắt các ngoại lệ và ném ngoại lệ tùy chỉnh của bạn (với ngoại lệ ban đầu làm đối số). Sau đó, nó có thể không nhanh chóng đến một nơi nào đó mà hành vi có ý nghĩa có thể xảy ra (hiển thị cho người dùng, gửi email, chỉ cần đăng nhập, thoát khỏi chuỗi máy chủ này, v.v.).
JeeBee

21
Chờ một chút ... Tôi cần phải ngừng cười trước ... Được rồi ... Đó là câu trả lời thú vị nhất cho một câu hỏi mà tôi từng thấy cho đến nay. Nó rất nhiều vấn đề. Mặc dù có thể rất hữu ích khi chuyển ngoại lệ "lên chuỗi" để xử lý, nhưng tốt hơn là ít nhất hãy nắm bắt ngoại lệ trước rồi chuyển nó để có thể đưa vào một số thông tin hữu ích.
Ông Will

Ngoại lệ không phải là lỗi. Ném một trường hợp ngoại lệ là người đàn ông bị bắn, sau đó kể chi tiết sự việc trong một ghi chú và gắn nó vào một con chim bồ câu vận chuyển đã được huấn luyện để bay đến đội sát nhân gần nhất.
Jherico

13
@Oscar, +100 nếu tôi có thể. Việc xử lý ngoại lệ kém là kết quả của việc BỎ QUA và BỎ LỠ, không phải do lười biếng. Lười biếng = nỗ lực càng ít càng tốt. Sự ngu ngốc trong việc xử lý các trường hợp ngoại lệ gây ra nhiều công việc hơn chứ không phải giảm bớt nó ... tất nhiên, nó thường gây ra nhiều công việc hơn cho người khác, đó là lý do tại sao nó có vẻ "lười biếng".
James Schek

1
Tôi thấy điểm của bạn oscar, nhưng tất cả việc xử lý ngoại lệ hoặc ghi nhật ký nó thực hiện đều cho bạn thấy rằng ngoại lệ đã xảy ra. Nếu bạn hoàn toàn sao lưu chuỗi, nó sẽ cho bạn biết phương thức nào cuối cùng đã gọi ra ngoại lệ.
Mauro

33

Thông thường, đó là do IDE cung cấp một 'bản sửa lỗi nhanh' hữu ích giúp bao bọc mã vi phạm trong một khối try-catch với xử lý ngoại lệ đó. Ý tưởng là bạn thực sự LÀM được điều gì đó, nhưng những nhà phát triển lười biếng thì không.

Đây là hình thức xấu, không còn nghi ngờ gì nữa.


2
Đồng ý, ví dụ được đưa ra giống như bản sửa lỗi nhanh mặc định của Eclipse, với nhận xét // FIXME: đã bị xóa.
JeeBee

2
Vâng, bạn đã nói "Đây là hình thức xấu, không còn nghi ngờ gì nữa " và tôi 100% đồng ý. Tuy nhiên, một số câu trả lời và nhận xét khác khiến tôi rất tò mò về triết lý đằng sau phần rất lớn đó (tôi tin là vậy) của các nhà phát triển java.
Sake

4
Tôi thực sự ước Eclipse sẽ làm điều gì đó quyết liệt ở đó. Giống như, e.printStackTrace (); System.exit (1);
Adam Jaskiewicz

4
Tôi đã thay đổi cài đặt IDE của mình để tạo một nội dung bắt cài đặt lại nó dưới dạng RuntimeException. Đó là một mặc định tốt hơn nhiều.
Esko Luontola

6
Nếu tôi chắc chắn rằng ngoại lệ sẽ không bao giờ xảy ra, tôi thường ném một AssertionError thay vì RuntimeException như một cách để ghi lại ý định của tôi.
Esko Luontola

20

Đây là một lập luận cổ điển của người đàn ông rơm . printStackTrace()là một trợ giúp gỡ lỗi. Nếu bạn thấy nó trên blog hoặc trên tạp chí, đó là vì người viết quan tâm đến việc minh họa một điểm khác hơn là xử lý ngoại lệ. Nếu bạn nhìn thấy nó trong mã sản xuất, thì người phát triển mã đó đã thiếu hiểu biết hoặc lười biếng, không hơn gì cả. Nó không nên được coi là một ví dụ về thông lệ phổ biến trong "thế giới java".


1
Vì vậy, việc lập trình viên chuyên nghiệp viết mã xấu trên blog hoặc tạp chí và không được sản xuất có được không? id thay vì viết mã xấu trong sản xuất. nếu anh chàng thực sự thông minh đang sử dụng printStackTrace, anh ấy sẽ thuyết phục mọi người sử dụng nó. tôi hiểu rằng mã ngoại lệ dài hơn 8 ký tự, nhưng ...
IAdapter

8
@ABCDE: Tôi hoàn toàn không đồng ý. Bạn muốn viết mã xấu trong sản xuất? Đó là mã phải hoạt động! Các bài báo trên blog và tạp chí được cho là minh họa một điểm. Nếu việc xử lý lỗi không phải là điểm mấu chốt, thì nó có thể chỉ là một mớ hỗn độn. Rất nhiều người viết sử dụng cách xử lý lỗi được đơn giản hóa rất nhiều hoặc không có lỗi nào trong các bài báo. Đây không phải là một ví dụ để làm theo.
cho Lizard

19
  1. Java buộc bạn phải xử lý tất cả các Ngoại lệ một cách rõ ràng. Nếu một phương thức mà mã của bạn gọi được khai báo để ném FooException và BarException thì mã của bạn PHẢI xử lý (hoặc ném) những ngoại lệ đó. Ngoại lệ duy nhất cho điều này là RuntimeException , im lặng như một ninja.
  2. Rất nhiều lập trình viên lười biếng (bao gồm cả tôi), và rất dễ dàng để in dấu vết ngăn xếp.

7
Nhưng chương trình của bạn không thoát và vẫn tiếp tục, hầu hết có thể ở trạng thái không xác định.
Isaac Waller

5
Làm thế nào là trạng thái không xác định nếu bạn biết loại ngoại lệ? Đó là một câu nói nhỏ dễ thương, nhưng thường thì chương trình CÓ THỂ và CÓ THỂ tiếp tục diễn ra. Bạn không muốn toàn bộ ứng dụng bị lỗi vì một ngoại lệ - hãy nói với người dùng và yêu cầu họ làm lại bất cứ điều gì.
GreenieMeanie

2
Ứng dụng sẽ không bị lỗi nếu trường hợp ngoại lệ được xử lý đúng cách ở mức tốt nhất.
Sake

2
Ít nhất một nửa số ngoại lệ là "RuntimeExceptions" mà bạn làm cho nó giống như chỉ có một. Và nó không im lặng, và RutimeException không cẩn thận sẽ in dấu vết ngăn xếp và làm hỏng chương trình của bạn.
Bill K

@greenieMeanie Nói chung trong quá trình phát triển, bạn muốn nó gặp sự cố khó khăn với một vấn đề nhỏ nhất - đừng để các nhà phát triển làm khó một cuộc gọi - đừng tha thứ về BẤT CỨ ĐIỀU GÌ. Mặt khác, khi bạn gửi nó, bạn muốn đi ngược lại. Ghi nhật ký mọi ngoại lệ một cách vô hình và cố gắng hết sức để khôi phục và tiếp tục (Java thực sự khá giỏi).
Bill K

12

Tôi thấy thường có 2 lý do khiến việc này được thực hiện

  1. Lập trình viên lười biếng
  2. Lập trình viên muốn bảo vệ một điểm vào thành phần đó (đúng hay sai)

Tôi không tin rằng đây là một hiện tượng chỉ giới hạn ở Java. Tôi cũng đã thấy mã hóa như vậy thường xuyên trong C # và VB.Net.

Nhìn bề ngoài thì nó khá sốc và trông thật khủng khiếp. Nhưng thực sự nó không có gì mới. Nó xảy ra mọi lúc trong các ứng dụng C ++ sử dụng các giá trị trả về mã lỗi so với các ngoại lệ. Tuy nhiên, sự khác biệt là việc bỏ qua một giá trị trả về có khả năng gây tử vong không thực sự trông khác gì so với việc gọi một hàm trả về void.

Foo* pFoo = ...;
pFoo->SomeMethod(); // Void or swallowing errors, who knows?

Mã này trông đẹp hơn nhưng nếu SomeMethod () nói trả về một HResult, về mặt ngữ nghĩa nó sẽ không khác gì việc nuốt một ngoại lệ.


3
Tôi nghĩ Lý do 1 phổ biến hơn nhiều so với Lý do 2 cho câu hỏi này nói riêng
matt b

2
@matt b, đã đồng ý. Sự lười biếng là nguyên nhân dẫn đến một phần lỗi mà tôi sửa chữa những ngày này.
JaredPar

bỏ qua một mã lỗi là một cái nhìn chết tiệt nhanh hơn và sử dụng ít mã soạn sẵn hơn nhiều!
gbjbaanb

11

bởi vì Ngoại lệ được Kiểm tra là một thử nghiệm thất bại

(có thể printStackTrace () là vấn đề thực sự ? :)


17
Chúng không phải là một thử nghiệm thất bại. Trên thực tế, tôi thích chúng! Như đã chỉ ra trong các câu trả lời khác, các lập trình viên có xu hướng lười biếng và chỉ bỏ qua các trường hợp lỗi. Vì các trường hợp ngoại lệ được kiểm tra buộc bạn phải xử lý chúng nên về tổng thể, bạn sẽ có mã tốt hơn. (Nếu Lập trình viên không bỏ qua chúng một cách âm thầm). Ngoài ra, ngay cả khi bạn không lười biếng, bạn không thể quên bỏ qua một ngoại lệ quan trọng bởi vì trình biên dịch sẽ yêu cầu bạn làm như vậy ...
Malax

5
đã kiểm tra các ngoại lệ không thành công vì một số lý do: hãy kiểm tra một số trong số đó mindview.net/Etc/Discussions/CheckedExceptions
dfa

2
+1 không thể đồng ý hơn. Các ngoại lệ được kiểm tra thường làm cho mã kém an toàn hơn và đây là một ví dụ về cách làm.
cletus

2
Các trường hợp ngoại lệ đã được kiểm tra chắc chắn không hoàn hảo, nhưng chúng buộc bạn phải đối mặt với thực tế rằng đôi khi, mọi thứ không diễn ra theo kế hoạch. Không kiểm tra trường hợp ngoại lệ chỉ là cố gắng giả vờ rằng chúng ta đang sống trong một thế giới hoàn hảo, nơi không có gì trục trặc
mcjabberz

2
@Peter Lawrey: các trường hợp ngoại lệ được kiểm tra là một thất bại bởi vì những người rất thông minh như những người đến với khung Swing tuyệt vời đã phát điên lên và từ chối sử dụng chúng. Và tôi rất nghi ngờ rằng những người như Joshua Bloch hoặc những người đứng sau khuôn khổ Spring "không biết cách sử dụng chúng một cách chính xác". Chắc chắn, bạn có thể đối phó chính xác với một ngoại lệ đã kiểm tra. Sự đổ vỡ thực sự là một người nào đó, ở đâu đó, nghĩ rằng ném một cái là một việc làm có thể chấp nhận được.
Cú phápT3rr0r

6

Tôi phải nói rằng tôi hơi bực bội với giọng điệu ngụ ý loại hành vi xử lý lỗi lỏng lẻo này là điều cơ bản đối với các lập trình viên Java. Chắc chắn, các lập trình viên Java có thể lười biếng, giống như mọi lập trình viên khác và Java là một ngôn ngữ phổ biến, vì vậy có thể bạn sẽ thấy rất nhiều trường hợp ngoại lệ nuốt mã.

Ngoài ra, như đã được chỉ ra ở những nơi khác, có những sự thất vọng có thể hiểu được với tuyên bố có hiệu lực của Java về các ngoại lệ đã kiểm tra, mặc dù cá nhân tôi không gặp vấn đề với điều đó.

Tôi đoán rằng điều tôi gặp vấn đề là bạn đang lướt qua một loạt các bài báo và đoạn mã trên web mà không cần quan tâm đến bối cảnh. Sự thật là, khi bạn đang viết một bài báo kỹ thuật cố gắng giải thích cách hoạt động của một số API cụ thể hoặc cách bắt đầu với một thứ gì đó, thì bạn rất có thể bỏ qua một số khía cạnh của mã - việc xử lý lỗi không trực tiếp liên quan đến những gì bạn đang trình bày là một ứng cử viên có khả năng bị loại bỏ, đặc biệt nếu trường hợp ngoại lệ khó có thể xảy ra trong trường hợp ví dụ.

Những người viết các bài báo có tính chất đó phải duy trì tỷ lệ tín hiệu trên nhiễu hợp lý, và khá công bằng, tôi nghĩ, điều đó có nghĩa là họ phải cho rằng bạn biết một số điều cơ bản về ngôn ngữ bạn đang phát triển; làm thế nào để đối phó đúng với lỗi, và một loạt những thứ khác. Nếu bạn bắt gặp một bài báo và nhận thấy thiếu kiểm tra lỗi thích hợp, thì tốt thôi; chỉ cần đảm bảo rằng khi bạn kết hợp những ý tưởng đó (nhưng tất nhiên, không bao giờ là mã chính xác, đúng không?) vào mã sản xuất của bạn, bạn sẽ xử lý tất cả những bit và bob mà tác giả đã bỏ qua một cách hợp lý, theo cách mà hầu hết phù hợp với những gì bạn đang phát triển.

Tôi thực sự gặp vấn đề với các bài báo giới thiệu cấp cao nói về các vấn đề như vậy mà không bao giờ quay lại chúng, nhưng xin lưu ý rằng không có một số "tư duy" cụ thể của các lập trình viên Java về việc xử lý lỗi; Tôi biết rất nhiều lập trình viên C # yêu quý của bạn, những người cũng không bận tâm đến việc giải quyết tất cả các vấn đề của họ.


Nhưng viết một bài báo cấp cao bằng cách sử dụng try {...} catch (IOException e) {}là hoàn toàn ngu ngốc (và VIỆC CẦN LÀM KHÔNG giúp ích gì). Nó chắc chắn sẽ đánh lừa nhiều người mới bắt đầu làm điều tương tự. Hơn nữa, nó còn nhiều thứ để viết hơn throws IOExceptionlà chính xác hầu hết thời gian. Có thể một lần trên giấy, nó không được và sau đó viết throw wrap(e)(không nhàm chán để xác định wrap) là một giải pháp tốt. Một trong những điều đầu tiên tôi dựa vào Java, đó là việc âm thầm nuốt chửng là một điều khủng khiếp và tôi rất cẩn thận vì vậy tôi không làm điều đó ngay cả tạm thời (kể cả việc bị rơi cũng tốt hơn về lâu dài).
maaartinus

5

Bản in System.out hoặc e.printStackTrace () - ngụ ý việc sử dụng System.out thường là một dấu hiệu đỏ có nghĩa là ai đó đã không bận tâm đến việc làm một công việc siêng năng. Ngoại trừ Ứng dụng Java trên máy tính để bàn, hầu hết các ứng dụng Java tốt hơn nên sử dụng ghi nhật ký.

Nếu chế độ lỗi cho một phương thức là không hoạt động, bạn hoàn toàn có thể ăn một ngoại lệ, cho dù bạn có ghi lại lý do (và sự tồn tại) hay không. Tuy nhiên, điển hình hơn, mệnh đề bắt phải thực hiện một số loại hành động ngoại lệ.

Nộp lại một ngoại lệ là điều được thực hiện tốt nhất khi bạn sử dụng phương thức bắt để dọn dẹp một phần công việc ở mức mà thông tin cần thiết vẫn có sẵn hoặc khi bạn cần chuyển đổi ngoại lệ thành một loại ngoại lệ phù hợp hơn với người gọi.


3

Nó chỉ được tiêu thụ một cách âm thầm nếu khối bắt thực sự trống.

Theo như các bài báo, chúng có lẽ thú vị hơn trong việc chứng minh một số điểm khác ngoài cách đối phó với các trường hợp ngoại lệ. Họ chỉ muốn đi thẳng vào vấn đề và có mã ngắn nhất có thể.

Rõ ràng là bạn đúng, các ngoại lệ ít nhất nên được ghi lại nếu chúng sẽ bị 'bỏ qua'.


3

Như những người khác đã chỉ ra, lý do bạn thấy điều này là vì một trong ba lý do:

  1. Một IDE đã tạo khối try-catch
    • Mã đã được sao chép và dán
    • Nhà phát triển đưa stacktrace vào để gỡ lỗi nhưng không bao giờ quay lại để xử lý ngoại lệ đúng cách

Điểm cuối cùng ít có khả năng xảy ra nhất. Tôi nói điều này bởi vì tôi không nghĩ rằng có ai thực sự gỡ lỗi theo cách này. Bước qua mã với trình gỡ lỗi là một cách gỡ lỗi dễ dàng hơn nhiều.

Mô tả tốt nhất về những gì nên làm trong một khối bắt có thể được tìm thấy trong Chương 9 của Java hiệu quả của Joshua Bloch .


2

Trong C # tất cả các ngoại lệ đều là ngoại lệ thời gian chạy, nhưng trong Java bạn có các ngoại lệ thời gian chạy và các ngoại lệ đã kiểm tra, mà bạn phải bắt hoặc khai báo trong các phương thức của mình. Nếu bạn gọi bất kỳ phương thức nào có dấu "ném" ở cuối, bạn phải bắt các ngoại lệ được đề cập ở đó hoặc phương thức của bạn cũng phải khai báo các ngoại lệ đó.

Các bài báo Java thường chỉ in dấu vết ngăn xếp hoặc có nhận xét vì việc xử lý ngoại lệ không liên quan đến chủ đề của bài viết. Tuy nhiên, trong các dự án, điều gì đó nên được thực hiện với nó, tùy thuộc vào loại ngoại lệ.


2

Bạn sẽ thấy điều này rất thường xuyên nếu lập trình viên làm đúng công việc của mình. Bỏ qua Ngoại lệ là một thực hành tồi tệ, tồi tệ! Nhưng có một số lý do tại sao một số có thể làm điều này và các giải pháp bổ sung hơn:

  • "Điều này sẽ không xảy ra!" Chắc chắn, đôi khi bạn "biết" rằng Ngoại lệ này sẽ không xảy ra, nhưng vẫn chấp thuận hơn để tạo lại một ngoại lệ thời gian chạy trong khi Ngoại lệ xảy ra là "nguyên nhân" thay vì bỏ qua nó. Tôi cá rằng nó sẽ xảy ra vào một lúc nào đó trong tương lai. ;-)

  • Mã nguyên mẫu Nếu bạn chỉ nhập nội dung của mình xuống để xem liệu nó có hoạt động hay không, bạn có thể muốn bỏ qua tất cả các Ngoại lệ có thể xảy ra. Đây là trường hợp duy nhất tôi thực hiện một số bắt lười (Có thể ném). Nhưng nếu mã sẽ trở thành một cái gì đó hữu ích, tôi sẽ bao gồm xử lý ngoại lệ thích hợp.

  • "Tôi không biết phải làm gì!" Tôi đã thấy nhiều mã, đặc biệt là mã thư viện, nuốt chửng các ngoại lệ vì trong lớp này của ứng dụng không thể xử lý thích hợp được. KHÔNG LÀM ĐIỀU NÀY! Chỉ cần ném lại ngoại lệ (bằng cách thêm mệnh đề ném vào chữ ký phương thức hoặc gói ngoại lệ vào một thư viện cụ thể).


3
Nhìn thấy cú bắt được (Throwable) khiến tôi quặn lòng.
Bill the Lizard

2
Tuyệt đối. Tôi chỉ sử dụng bắt (Throwable) hoặc ném Throwable trong mã nếu tôi đang chơi xung quanh. Mã "thực" không được bao gồm nó! giai đoạn = Stage.
Malax

Đã đồng ý. Tôi co rúm người lại vì tôi đã từng thấy nó. Nếu một nhà phát triển khác thấy rằng bạn đã viết catch (Throwable), thì điều đó đã đi quá xa! :)
Bill the Lizard

1
Bạn muốn bắt (Có thể ném) ở cấp cao nhất, ngay cả khi mã tắt quá trình. Cố gắng tập tễnh khi thiếu một luồng, có thể không phải là ý tưởng tốt nhất.
Tom Hawtin - tackline

2

Bạn nên luôn chuyển tiếp hoặc xử lý nó một cách phù hợp trong bối cảnh thế giới thực. Mặc dù vậy, rất nhiều bài báo và hướng dẫn sẽ đơn giản hóa mã của họ để đạt được những điểm quan trọng hơn và một trong những điều dễ dàng nhất để đơn giản hóa là xử lý lỗi (trừ khi những gì bạn đang làm là viết một bài báo về xử lý lỗi :)). Vì mã java sẽ kiểm tra việc xử lý ngoại lệ, sau đó đặt một khối bắt im lặng (hoặc câu lệnh ghi nhật ký) đơn giản vào là phương pháp đơn giản nhất để cung cấp một ví dụ hoạt động.

Nếu bạn tìm thấy mã này trong bất kỳ thứ gì khác ngoài mã ví dụ, vui lòng chuyển tiếp mã sang TDWTF, mặc dù hiện tại họ có thể có quá nhiều ví dụ về nó :)


2

Tôi e rằng hầu hết các lập trình viên java không biết phải làm gì với Exceptions, và luôn coi nó như một sự phiền toái làm chậm quá trình viết mã của họ trong trường hợp "danh nghĩa". Tất nhiên là họ hoàn toàn sai, nhưng rất khó để thuyết phục họ rằng ĐÓ LÀ ĐIỀU quan trọng để giải quyết chính xác các trường hợp ngoại lệ. Mỗi khi tôi gặp một lập trình viên như vậy (nó thường xuyên xảy ra), tôi đưa cho anh ta hai mục đọc:

  • Suy nghĩ nổi tiếng trong java
  • Một bài báo ngắn và thú vị của Barry Ruzek có tại đây: www.oracle.com/technology/pub/articles/dev2arch/2006/11/effective-exceptions.html

Nhân tiện, tôi thực sự đồng ý rằng thật ngu ngốc khi bắt một ngoại lệ đã nhập để ném lại nó được nhúng trong RuntimeException:

  • nếu bạn nắm bắt được nó, hãy XỬ LÝ nó.
  • nếu không, hãy thay đổi chữ ký phương thức của bạn để thêm các ngoại lệ có thể có mà bạn sẽ / không thể xử lý, vì vậy người gọi của bạn sẽ có cơ hội tự làm điều đó.

Tôi 100% sẽ không tin Bruce Eckel về vấn đề này :)
CurtainDog

2

Sự kết hợp của các ngoại lệ và giao diện đã được kiểm tra dẫn đến tình huống mã phải xử lý các ngoại lệ không bao giờ được ném ra. (Điều này cũng áp dụng cho kế thừa thông thường, nhưng nó phổ biến hơn và dễ giải thích hơn với các giao diện)

Lý do: Việc triển khai một giao diện không được ném (đã kiểm tra) các ngoại lệ ngoài những ngoại lệ được xác định trong đặc tả giao diện. Vì lý do đó, những người tạo giao diện, không biết phương thức nào của một lớp triển khai giao diện có thể thực sự cần đưa ra ngoại lệ, có thể xác định rằng tất cả các phương thức có thể ném ít nhất một loại ngoại lệ. Ví dụ: JDBC, nơi mọi thứ và bà của nó được khai báo để ném SQLException.

Nhưng trong thực tế, nhiều phương pháp triển khai thực đơn giản không thể thất bại, vì vậy trong mọi trường hợp, chúng không bao giờ có ngoại lệ. Mã gọi các phương thức này bằng cách nào đó vẫn phải "xử lý" ngoại lệ và cách dễ nhất là làm điều đó nuốt ngoại lệ. Không ai muốn làm lộn xộn mã của mình với việc xử lý lỗi dường như vô dụng mà không bao giờ được thực thi.


1
IMHO, nên có một ngôn ngữ viết tắt để nắm bắt và xử lý lại các trường hợp ngoại lệ như lỗi thời gian chạy. Ký hiệu viết tắt như vậy sẽ làm rõ ràng cả về mã và hành vi rằng các ngoại lệ như vậy nên được coi là đại diện cho các điều kiện "không mong muốn". Nuốt là không tốt, vì nếu một ngoại lệ xảy ra, thì có gì đó đã xảy ra sai nghiêm trọng và mã gọi điện nên tìm hiểu về nó.
supercat

supercat: Tôi thích ý tưởng đó, nhưng nó có thể hủy bỏ lợi ích (có vấn đề về IMO) của các ngoại lệ đã kiểm tra.
Erich Kitzmueller

Sẽ tốt hơn nếu kiểm tra là một đặc điểm của các trang web bắt / ném và các trường hợp ngoại lệ chứ không phải là các lớp ngoại lệ, để các ngoại lệ được kiểm tra có thể phổ biến như các ngoại lệ không được kiểm tra của cùng một lớp, nhưng thậm chí bao gồm và quay lại sẽ là một cải tiến lớn đối với hiện trạng và nó có thể dễ dàng được thêm vào như một tính năng mới. Nó sẽ đặc biệt hữu ích trong trường hợp một phương thức có thể ném một ngoại lệ đã được kiểm tra trên chính nó gọi các phương thức khác được khai báo là ném cùng một ngoại lệ đó nhưng không được mong đợi sẽ thực sự làm như vậy . Nếu một trong những phương pháp sau ném ...
supercat

... một người gọi đang mong đợi xử lý ngoại lệ được ném ra từ phương thức bên ngoài sẽ không bắt được một ngoại lệ được đưa ra bởi các phương thức bên trong, vì nó sẽ không đại diện cho điều kiện mà người gọi mong đợi. Nếu tôi thiết kế một khung thời gian chạy từ đầu, tôi sẽ sử dụng một cơ chế khác cho các ngoại lệ được kiểm tra và không được kiểm tra, chẳng hạn như các ngoại lệ được kiểm tra sẽ thêm chi phí nhẹ cho một cuộc gọi hàm thành công nhưng có chi phí ít hơn nhiều khi ném so với các ngoại lệ không được kiểm tra (trong số những thứ khác , một ngoại lệ đã được kiểm tra sẽ không có dấu vết ngăn xếp).
supercat

1

Như đã chỉ ra, việc gọi printStackTrace () không thực sự xử lý im lặng.

Lý do cho kiểu "nuốt" ngoại lệ này là vì nếu bạn tiếp tục chuyển ngoại lệ lên chuỗi, bạn vẫn phải xử lý ngoại lệ ở đâu đó hoặc để ứng dụng gặp sự cố. Vì vậy, xử lý nó ở cấp độ nó xảy ra với một kết xuất thông tin không tệ hơn xử lý nó ở cấp cao nhất với một kết xuất thông tin.


1

Thực hành lười biếng của nó - không có gì thiếu nó thực sự.

Nó thường được thực hiện khi bạn thực sự không quan tâm đến ngoại lệ - thay vì tăng công việc của bạn.


1

Tôi không đồng ý rằng việc ném lại một ngoại lệ đã kiểm tra là một ý tưởng tốt hơn. Bắt có nghĩa là xử lý; nếu bạn phải ném lại, bạn không nên bắt. Tôi sẽ thêm mệnh đề ném vào chữ ký phương thức trong trường hợp đó.

Tôi sẽ nói rằng việc bao bọc một ngoại lệ đã kiểm tra trong một ngoại lệ không được kiểm tra (ví dụ: cách Spring bao bọc SQLException đã kiểm tra thành một thể hiện của hệ thống phân cấp không được kiểm tra của nó) là chấp nhận được.

Ghi nhật ký có thể được coi là xử lý. Nếu ví dụ được thay đổi để ghi lại dấu vết ngăn xếp bằng cách sử dụng log4j thay vì ghi vào bảng điều khiển, điều đó có làm cho nó được chấp nhận không? Không có nhiều thay đổi, IMO.

Vấn đề thực sự là những gì được coi là ngoại lệ và một quy trình khôi phục có thể chấp nhận được. Nếu bạn không thể khôi phục từ trường hợp ngoại lệ, tốt nhất bạn có thể làm là báo cáo lỗi.


1

Xin đừng bao giờ, bao giờ, bao giờ bọc một ngoại lệ đã kiểm tra trong một ngoại lệ chưa kiểm tra.

Nếu bạn thấy mình phải đối mặt với những trường hợp ngoại lệ mà bạn không nghĩ là mình nên làm thì lời khuyên của tôi là có thể bạn đang làm việc ở mức độ trừu tượng sai.

Tôi sẽ giải thích thêm: các ngoại lệ được kiểm tra và không được kiểm tra là hai con thú rất khác nhau. Các ngoại lệ được kiểm tra tương tự như phương pháp cũ để trả lại mã lỗi ... vâng, chúng có phần khó xử lý hơn mã lỗi nhưng chúng cũng có lợi thế. Các ngoại lệ không được kiểm tra là lỗi lập trình và các lỗi hệ thống nghiêm trọng ... nói cách khác là các ngoại lệ đặc biệt. Khi cố gắng giải thích ngoại lệ là gì, rất nhiều người rơi vào tình trạng hỗn độn hoàn toàn bởi vì họ không thừa nhận sự khác biệt trong hai trường hợp rất khác nhau này.


Gói trong AssertionError là có thể nghe được đối với tôi. AssertionError là phần trừu tượng áp dụng ở bất kỳ đâu, IMO.
Sake

Tôi sẽ giải thích thêm: các ngoại lệ được kiểm tra và không được kiểm tra là hai con thú rất khác nhau. Các ngoại lệ được kiểm tra tương tự như phương pháp cũ để trả lại mã lỗi ... vâng, chúng có phần khó xử lý hơn mã lỗi nhưng chúng cũng có lợi thế. Các ngoại lệ không được kiểm tra là lỗi lập trình và lỗi hệ thống nghiêm trọng ... nói cách khác là các ngoại lệ đặc biệt. Khi cố gắng giải thích ngoại lệ là gì, rất nhiều người rơi vào tình trạng hỗn độn hoàn toàn bởi vì họ không thừa nhận sự khác biệt trong hai trường hợp rất khác nhau này.
CurtainDog

Có lẽ tôi là một trong số những người đang rơi vào một mớ hỗn độn hoàn toàn, như bạn nói. Tuy nhiên, quan điểm thực sự của tôi là tại sao lại cho phép quá trình thực thi tiếp tục trong khi bạn có lựa chọn để ngắt nó. (với một trong hai AssertionError hoặc, nếu bạn thích, thay đổi giao diện của bạn để bao gồm thêm ngoại lệ.
Sake

1
Không thể không đồng ý với khẳng định chính của câu trả lời này hơn. Công phu có công.
Lawrence Dol,

@CurtainDog: Các ngoại lệ được kiểm tra dành cho các sự cố "dự kiến". Nếu phương thức x ném (kiểm tra) FooException trong một số trường hợp và phương thức y, gọi x, không mong đợi những trường hợp như vậy phát sinh, thì nếu những trường hợp đó phát sinh, chúng đại diện cho một vấn đề không mong muốn ; ngay cả khi có thứ gì đó xa hơn trong ngăn xếp cuộc gọi đang mong đợi a FooException, tình huống hiện tại sẽ không khớp với những gì mà người gọi ngược dòng mong đợi khi a FooExceptionđược ném.
supercat

1

Bởi vì họ chưa học được mẹo này:

class ExceptionUtils {
    public static RuntimeException cloak(Throwable t) {
        return ExceptionUtils.<RuntimeException>castAndRethrow(t);
    }

    @SuppressWarnings("unchecked")
    private static <X extends Throwable> X castAndRethrow(Throwable t) throws X {
        throw (X) t;
    }
}

class Main {
    public static void main(String[] args) { // Note no "throws" declaration
        try {
            // Do stuff that can throw IOException
        } catch (IOException ex) {
            // Pretend to throw RuntimeException, but really rethrowing the IOException
            throw ExceptionUtils.cloak(ex);
        }
    }
}

đây là đẹp lừa, nhưng cá nhân tôi muốn để ném một ngoại lệ cụ thể hơn với một nguyên nhân cái gì đó như 'ném ServiceUnavailableException mới (ví dụ)
Greg

phương thức rethrow của Apache's ExceptionUtils thực hiện chính xác điều này. Đó là một mẹo hay. github.com/apache/commons-lang/blob/master/src/main/java/org/…
Lluis Martinez

1

Điểm thực sự của các ngoại lệ là đơn giản hóa việc xử lý lỗi và tách nó ra khỏi việc phát hiện lỗi. Điều này trái ngược với việc biểu diễn lỗi bằng mã lỗi, trong đó mã xử lý lỗi nằm rải rác khắp nơi và mọi cuộc gọi có thể thất bại sẽ được kiểm tra mã trả về.

Nếu ngoại lệ đại diện cho một lỗi (đó là hầu hết các trường hợp), cách hợp lý nhất để xử lý nó là cứu trợ và để việc xử lý cho một số lớp trên. Việc lặp lại các ngoại lệ khác nhau nên được xem xét nếu một số ngữ nghĩa có ý nghĩa được thêm vào nó, tức là, lỗi này là lỗi hệ thống bất thường / sự cố (mạng) tạm thời / đây là lỗi phía máy khách hoặc máy chủ, v.v.

Trong tất cả các chiến lược xử lý lỗi, điều thiếu hiểu biết nhất là ẩn hoặc đơn giản là in thông báo lỗi và tiếp tục như không có gì xảy ra.


Sun folks muốn mã rõ ràng hơn và buộc các lập trình viên phải viết những ngoại lệ nào có thể được ném theo phương thức nào. Đó dường như là một bước đi đúng đắn - bất kỳ ai cũng sẽ biết điều gì sẽ xảy ra để đổi lại từ bất kỳ lệnh gọi phương thức nào khi nó là nguyên mẫu (nó có thể trả về giá trị kiểu này hoặc ném một thể hiện của một trong các lớp được chỉ định (hoặc đó là lớp con)).

Nhưng hóa ra với rất nhiều lập trình viên Java thiếu hiểu biết, giờ đây họ coi việc xử lý ngoại lệ như thể đó là lỗi ngôn ngữ / "tính năng" cần một giải pháp thay thế và viết mã theo cách tồi tệ nhất hoặc gần như tồi tệ nhất có thể:

  • Lỗi được xử lý ngay lập tức trong bối cảnh không phù hợp để quyết định phải làm gì với nó.
  • Nó được hiển thị hoặc bị bỏ qua một cách âm thầm và quá trình tính toán vẫn tiếp tục ngay cả khi mã khác không có cơ hội chạy đúng cách.
  • Người gọi phương thức không thể phân biệt nó đã hoàn tất thành công hay chưa.

Làm thế nào để viết "đúng cách" hơn?

  • Chỉ ra mọi ngoại lệ của lớp cơ sở có thể được đưa vào tiêu đề phương thức. AFAICR Eclipse có thể làm điều đó tự động.
  • Làm cho danh sách ném trong nguyên mẫu phương thức có ý nghĩa. Danh sách dài là vô nghĩa và "ném Ngoại lệ" là lười biếng (nhưng hữu ích khi bạn không bận tâm nhiều về các ngoại lệ).
  • Khi viết "sai cách", đơn giản "throw Exception" tốt hơn nhiều và tốn ít byte hơn so với "try {...} catch (Exception e) {e.printStackTrace ();}".
  • Rethrow ngoại lệ chuỗi nếu cần.

0

Nếu bạn muốn ngoại lệ của mình được xử lý bên ngoài phạm vi của phương thức hiện tại, bạn không cần phải bắt nó thực sự, thay vào đó bạn thêm câu lệnh 'throws' vào chữ ký phương thức.

Các câu lệnh try / catch mà bạn đã thấy chỉ có trong đoạn mã mà lập trình viên đã quyết định rõ ràng xử lý ngoại lệ tại chỗ và do đó không ném nó thêm.


1
Không đúng - nếu bạn đang triển khai một phương thức giao diện hoặc ghi đè một phương thức lớp cha không có khai báo 'ném', thì bạn không thể chỉ thêm một phương thức. Trong những trường hợp đó, bạn phải bọc ngoại lệ trong RuntimeException hoặc nuốt nó.
Martin McNulty

0

Tôi nghĩ rằng các nhà phát triển cũng cố gắng xem xét tầm quan trọng của việc "làm điều đúng đắn" trong một bối cảnh cụ thể. Thông thường, khi vứt bỏ mã hoặc khi tuyên truyền ngoại lệ trở lên sẽ không mua được gì vì ngoại lệ rất nguy hiểm, bạn cũng có thể tiết kiệm thời gian và công sức bằng cách "làm sai".


0

Bạn thường nuốt phải một ngoại lệ khi bạn không thể phục hồi nó nhưng nó không nghiêm trọng. Một ví dụ điển hình là IOExcetion có thể được ném khi đóng kết nối cơ sở dữ liệu. Bạn có thực sự muốn sụp đổ nếu điều này xảy ra? Đó là lý do tại sao các DBUtils của Jakarta có các phương thức closeSilently.

Bây giờ đối với ngoại lệ đã kiểm tra mà bạn không thể khôi phục nhưng rất quan trọng (thường là do lỗi lập trình), đừng nuốt chúng. Tôi nghĩ rằng các ngoại lệ nên được ghi lại gần nhất có thể với nguồn gốc của sự cố, vì vậy tôi không khuyên bạn nên xóa lệnh gọi printStackTrace (). Bạn sẽ muốn biến chúng thành RuntimeException với mục đích duy nhất là không phải khai báo những ngoại lệ này trong phương thức kinh doanh của bạn. Thực sự không có ý nghĩa gì khi có các phương thức nghiệp vụ cấp cao như createClientAccount () ném ProgrammingErrorException (đọc SQLException), bạn không thể làm gì nếu mắc lỗi chính tả trong sql hoặc truy cập vào các chỉ mục không hợp lệ.


0

Theo kinh nghiệm, Nuốt một ngoại lệ chủ yếu có hại khi nó không được in. Nó giúp thu hút sự chú ý nếu bạn gặp sự cố và đôi khi tôi sẽ cố tình làm điều đó, nhưng chỉ cần in ngoại lệ và tiếp tục cho phép bạn tìm ra vấn đề và khắc phục nó, nhưng thường không ảnh hưởng tiêu cực đến những người khác làm việc trên cùng một cơ sở mã .

Tôi thực sự thích Fail-Fast / Fail-HARD, nhưng ít nhất hãy in nó ra. Nếu bạn thực sự "Ăn" một ngoại lệ (nghĩa là thực sự không làm gì cả: {}) Ai đó sẽ mất HÀNG NGÀY để tìm thấy nó.

Vấn đề là Java buộc bạn phải nắm bắt rất nhiều thứ mà nhà phát triển biết rằng sẽ không được ném, hoặc không quan tâm nếu chúng có. Phổ biến nhất là Thread.sleep (). Tôi nhận ra rằng có những lỗ hổng có thể cho phép các vấn đề phân luồng ở đây, nhưng nói chung bạn biết rằng bạn không làm gián đoạn nó. Giai đoạn = Stage.


0

Có thể có nhiều lý do tại sao người ta sử dụng catch Exception. Trong nhiều trường hợp, đó là một ý tưởng tồi vì bạn cũng bắt RuntimeExceptions - và bạn cũng không biết các đối tượng cơ bản sẽ ở trạng thái nào sau khi điều này xảy ra? Đó luôn là điều khó khăn với những điều kiện bất ngờ: bạn có thể tin tưởng rằng phần còn lại của đoạn mã sẽ không bị lỗi sau đó.

Ví dụ của bạn in stacktrace để ít nhất bạn sẽ biết nguyên nhân gốc rễ có thể là gì. Trong các dự án phần mềm lớn hơn, tốt hơn là ghi nhật ký những thứ này. Và chúng ta hãy hy vọng rằng thành phần nhật ký không ném ra ngoại lệ hoặc của chúng tôi, bạn có thể kết thúc trong một vòng lặp vô hạn (có thể sẽ giết JVM của bạn).


0

Nếu bạn có một ngoại lệ đã được kiểm tra và bạn không muốn xử lý nó trong một phương thức, bạn chỉ nên đặt phương thức ném ngoại lệ. Chỉ nắm bắt và xử lý các trường hợp ngoại lệ nếu bạn định làm điều gì đó hữu ích với nó. Chỉ ghi nhật ký nó không hữu ích lắm trong sách của tôi vì người dùng hiếm khi có thời gian đọc nhật ký tìm kiếm ngoại lệ hoặc biết phải làm gì nếu một ngoại lệ được đưa ra.

Mặc dù gói ngoại lệ là một tùy chọn, tôi sẽ không đề nghị bạn làm điều này trừ khi; bạn đang ném một ngoại lệ khác để phù hợp với một giao diện tồn tại hoặc thực sự không có cách nào một ngoại lệ như vậy được ném ra.

BTW: Nếu bạn muốn ném lại một ngoại lệ đã chọn, bạn có thể làm điều này với

try {
   // do something
} catch (Throwable e) {
   // do something with the exception
   Thread.currentThread().stop(e); // doesn't actually stop the current thread, but throws the exception/error/throwable
}

Lưu ý: nếu bạn làm điều này, bạn nên đảm bảo rằng khai báo ném cho phương thức là chính xác vì trình biên dịch không thể thực hiện việc này cho bạn trong tình huống này.


Just FTR: Thread#stop()đã không được chấp nhận và bị vô hiệu hóa trong thời gian chờ đợi (nó ném UnsupportedOperationException).
maaartinus

-2

bởi vì nó là một thực hành tốt nhất. Tôi nghĩ mọi người đều biết.
nhưng sự thật lạnh lùng là không ai thực sự hiểu cách làm việc với các trường hợp ngoại lệ. Cách xử lý lỗi C có ý nghĩa hơn nhiều.


1
Chỉ vì một số lượng lớn các nhà phát triển không tìm kiếm các ngoại lệ không làm cho việc "trả lại lỗi" tốt hơn. Cách C xử lý lỗi đã và vẫn là một cơ chế dễ xảy ra lỗi (ý định chơi chữ) khủng khiếp. Việc nuốt các ngoại lệ (ví dụ này thực hiện, không phải là âm thầm) cũng giống như việc gọi một hàm trong C mà không kiểm tra giá trị trả về. Kết quả cuối cùng là như nhau - chương trình "sai lầm" trên. Ví dụ này chỉ là một trường hợp làm cho việc xử lý lỗi Java trở thành tồi tệ C.
Lawrence Dol
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.