2013-02-26 34 views
18

Có an toàn để:Có an toàn để lưu trữ một trường hợp ngoại lệ và tái sử dụng không?

public class Widget { 

    private static final IllegalStateException LE_EXCEPTION 
      = new IllegalStateException("le sophisticated way"); 

    ... 

    public void fun() { 
     // some logic here, that may throw 
     throw LE_EXCEPTION; 
    } 

    .... 
} 
  1. giữ một thể hiện của một ngoại lệ
  2. sử dụng nó bất cứ khi nào cần thiết (ném)

thay vì ném một ngoại lệ new mỗi lần?

Tôi quan tâm nếu nó an toàn

Bằng cách an toàn tôi muốn nói: không có tham nhũng bộ nhớ, không có ngoại lệ thêm ném bởi JVM, bởi thiếu lớp học, không có lớp xấu nạp (...). Lưu ý: ngoại lệ sẽ được ném qua mạng (từ xa).

Các sự cố khác (khả năng đọc, chi phí giữ ví dụ) không quan trọng.

+2

Tôi nghĩ nó dư thừa. trừ khi bạn mã ném nó rất thường xuyên – PermGenError

+0

Nó luôn luôn có một trường hợp ngoại lệ, ngay cả mã của bạn không ném một ngoại lệ – nav0611

Trả lời

17

Tùy thuộc vào định nghĩa của bạn về "an toàn". Ngoại lệ sẽ cung cấp cho một dấu vết ngăn xếp gây hiểu lầm, mà tôi sẽ không gọi là "an toàn".Xem xét:

public class ThrowTest { 
    private static Exception e = new Exception("t1"); // Line 2 

    public static final void main(String[] args) { 
     ThrowTest tt; 

     tt = new ThrowTest(); 
     try { 
      tt.t1(); 
     } 
     catch (Exception ex) { 
      System.out.println("t1:"); 
      ex.printStackTrace(System.out); 
     } 
     try { 
      tt.t2();         // Line 16 
     } 
     catch (Exception ex) { 
      System.out.println("t2:"); 
      ex.printStackTrace(System.out); 
     } 
    } 

    private void t1() 
    throws Exception { 
     throw this.e; 
    } 

    private void t2() 
    throws Exception { 
     throw new Exception("t2");     // Line 31 
    } 
} 

Đó có sản lượng này:

$ java ThrowTest 
t1: 
java.lang.Exception: t1 
    at ThrowTest.<clinit>(ThrowTest.java:2) 
t2: 
java.lang.Exception: t2 
    at ThrowTest.t2(ThrowTest.java:31) 
    at ThrowTest.main(ThrowTest.java:16) 

Lưu ý cách phương pháp t1 là hoàn toàn mất tích từ stack trace trong trường hợp thử nghiệm đầu tiên. Không có thông tin ngữ cảnh hữu ích nào cả.

Bây giờ, bạn có thể sử dụng fillInStackTrace để điền vào thông tin mà ngay trước khi ném:

this.e.fillInStackTrace(); 
throw this.e; 

... nhưng đó chỉ là làm việc cho chính mình (công việc bạn sẽ quên đôi khi). Đơn giản là không có lợi ích gì cả. Và không phải tất cả ngoại lệ đều cho phép bạn làm điều đó (một số trường hợp ngoại lệ làm cho dấu vết ngăn xếp chỉ đọc).


Bạn đã nói ở nơi khác trong nhận xét rằng điều này là để tránh "sao chép mã". Bạn đang nhiều khấm khá hơn có một chức năng xây dựng ngoại lệ:

private IllegalStateException buildISE() { 
    return new IllegalStateException("le sophisticated way"); 
} 

(Có thể static final nếu bạn muốn.)

Và sau đó ném nó như thế này:

throw buildISE(); 

mà tránh sao chép mã mà không có dấu vết ngăn xếp gây hiểu lầm và các trường hợp ngoại lệ không cần thiết.

Đây là những gì trông giống như áp dụng các phần trên:

public class ThrowTest { 

    public static final void main(String[] args) { 
     ThrowTest tt; 

     tt = new ThrowTest(); 
     try { 
      tt.t1();         // Line 8 
     } 
     catch (Exception ex) { 
      System.out.println("t1:"); 
      ex.printStackTrace(System.out); 
     } 
     try { 
      tt.t2();         // Line 15 
     } 
     catch (Exception ex) { 
      System.out.println("t2:"); 
      ex.printStackTrace(System.out); 
     } 
    } 

    private static final Exception buildEx() { 
     return new Exception("t1");     // Line 24 
    } 

    private void t1() 
    throws Exception { 
     throw buildEx();        // Line 29 
    } 

    private void t2() 
    throws Exception { 
     throw new Exception("t2");      // Line 34 
    } 
} 
$ java ThrowTest 
t1: 
java.lang.Exception: t1 
    at ThrowTest.buildEx(ThrowTest.java:24) 
    at ThrowTest.t1(ThrowTest.java:29) 
    at ThrowTest.main(ThrowTest.java:8) 
t2: 
java.lang.Exception: t2 
    at ThrowTest.t2(ThrowTest.java:34) 
    at ThrowTest.main(ThrowTest.java:15)
+0

Tôi thích ý tưởng của bạn. Bạn có thể viết số dòng trong mã của bạn để stacktrace dễ hiểu ở đây không, trên SE? –

+0

Tôi nghĩ ví dụ ngăn xếp đầu tiên tốt hơn! –

+1

@JoshuaMN: Bản cập nhật làm cho nó giống cơ chế đề xuất của bạn hơn (ngoại lệ 'tĩnh'). Tôi đã cập nhật với số dòng và ví dụ về cách chức năng của trình tạo cho phép theo dõi ngăn xếp dễ đọc hơn. –

1

Tôi nghĩ rằng điều này là sai, bởi vì nó sẽ khuyến khích bạn sử dụng các ngoại lệ để mô tả các tình huống bình thường, và không chỉ những trường hợp đặc biệt. Xem ấn bản Java hiệu quả thứ hai của J. Bloch, mục 57, trang 241.

Bạn cũng luôn giữ một đối tượng trong heap, vì vậy điều này là không cần thiết, vì việc tạo đối tượng rất nhanh trong các JVM vừa phải và cũng một lần ngoại lệ là ném nó sẽ có thể rất nhanh chóng thu thập rác thải.

Ngoài ra mã của bạn có thể trở nên rất gây hiểu lầm, điều này thêm rất nhiều chi phí cho một thứ gì đó khá eo biển về phía trước.

+0

Xin vui lòng bỏ qua phong cách mã hóa.Tôi cần các trường hợp để ** tránh ** mã trùng lặp (tôi cần phải xử lý rất nhiều trường hợp ngoại lệ và ném một khác nhau) –

+0

@JoshuaMN bạn nên ném ngoại lệ, nơi họ đang cần thiết, nếu bạn muốn bạn có thể tóm tắt mã đi trong một phương pháp làm điều đó, nếu bạn nghi ngờ rằng loại ngoại lệ của bạn sẽ thay đổi, nhưng hãy chắc chắn để làm điều này với các trường hợp ngoại lệ không được kiểm soát, để giúp bạn tiết kiệm rất nhiều thay đổi trong điều khoản bắt/ném. Nếu ném ngoại lệ là cái gì đó "bình thường" cho ứng dụng của bạn, bạn nên nghĩ đến cái gì đó khác để thay đổi luồng chương trình thay vì ném ngoại lệ, như tôi đã nói ngoại lệ chỉ nên sử dụng trong những tình huống đặc biệt. – comanitza

3

Một lý do nữa không làm là ngăn xếp ngăn xếp sẽ không phù hợp.

Không có vấn đề gì trong mã của bạn bạn ném ngoại lệ, khi dấu vết ngăn xếp của nó được in, nó sẽ hiển thị dòng nơi ngoại lệ được khởi tạo thay vì dòng đang được ném (trong trường hợp cụ thể này) của dự kiến ​​Widget.fun() theo dõi ngăn xếp sẽ chứa Widget.<clinit>).

Vì vậy, bạn (hoặc bất kỳ ai sử dụng mã của bạn) sẽ không thể xác định vị trí thực sự của lỗi.

2

Nó nghĩ rằng nó có thể gây ra vấn đề khi đọc dấu vết ngăn xếp của ngoại lệ. Dấu vết ngăn xếp được lấp đầy khi mã ném ngoại lệ nhưng được lưu trữ vào cá thể của ngoại lệ. Nếu bạn ném cùng một trường hợp ngoại lệ hai lần, tôi nghĩ rằng getStackTrace() trả về dấu vết ngăn xếp cuối cùng. Nếu vì lý do nào đó (ví dụ: trong môi trường đa luồng) ngoại lệ được ném hai lần từ các vị trí khác nhau trong mã và sau đó được in theo dõi ngăn xếp từ lần ném đầu tiên có thể sai.

Vì không có lý do gì để sử dụng lại các trường hợp ngoại lệ, tôi không khuyên bạn nên làm như vậy.

Nếu bạn muốn sử dụng ngoại lệ làm loại chứa thông tin bổ sung (ví dụ: thông báo lỗi, mã lỗi, v.v.), hãy sử dụng thực tế là ngoại lệ có thể tuần tự: sao chép nó trước khi ném. Vì vậy, mỗi khi bạn sẽ ném trường hợp duy nhất của ngoại lệ nhưng các trường của nó sẽ được sao chép từ mẫu được tạo trước.

4

Nó không phải là an toàn, trừ trường hợp ngoại lệ là bất biến (ví dụ: enableSuppression = writableStackTrace = false).

Nếu ngoại lệ không phải là bất biến, nó có thể được sửa đổi bởi trình tạo - thiết lập ngăn xếp mới hoặc thêm ngoại lệ bị loại bỏ. Nếu có nhiều người bắt muốn sửa đổi ngoại lệ, sẽ có sự hỗn loạn.

Thật đáng kinh ngạc, Throwable thực sự là an toàn cho chủ đề, vì bạn biết đấy. Vì vậy, ít nhất sẽ không có thất bại thảm khốc nếu một ngoại lệ được sửa đổi bởi nhiều chủ đề. Nhưng sẽ có lỗi logic.

Rò rỉ bộ nhớ cũng có thể xảy ra, nếu ứng dụng tiếp tục thêm ngoại lệ bị chặn vào ngoại lệ dài hạn này.

+0

Câu trả lời này cung cấp thông tin quan trọng nhất. Cảm ơn! –

2

Không an toàn để làm quen với bất kỳ mã xử lý lỗi nào. Giữ nó đơn giản, giữ cho nó rõ ràng, và không viết bất cứ điều gì bạn cần phải đặt câu hỏi về. Xử lý lỗi không phải là nơi phát sinh thêm lỗi.

Các vấn đề liên quan