Một ngoại lệ bị đàn áp là gì?


76

Một nhận xét (của người dùng soc ) về câu trả lời cho câu hỏi về tối ưu hóa cuộc gọi đuôi đã đề cập rằng Java 7 có một tính năng mới được gọi là "ngoại lệ bị loại bỏ", vì "việc bổ sung ARM" (hỗ trợ cho CPU ARM?).

"Ngoại lệ bị đàn áp" trong bối cảnh này là gì? Trong các ngữ cảnh khác, một "ngoại lệ bị triệt tiêu" sẽ là một ngoại lệ bị bắt và sau đó bị bỏ qua (hiếm khi là một ý kiến ​​hay); đây rõ ràng là một cái gì đó khác nhau.


Tôi thấy không có đề cập đến nó trong "Java Programming Language Cải tiến" mô tả download.oracle.com/javase/7/docs/technotes/guides/language/...
Raedwald

18
ARM có nghĩa Quản lý tài nguyên tự động, ví dụ như infoq.com/news/2010/08/arm-blocks
daniel kullmann

5
ARM, trong bối cảnh này, là tên cũ của thử tài nguyên. Họ đã ngừng sử dụng ARM và bắt đầu sử dụng thử với các tài nguyên trước khi Java 7 xuất xưởng. Và @danielkullmann là ngay trong những gì ARM là viết tắt của
Brad Cupit

Câu trả lời:


55

Tôi tin rằng người bình luận đang đề cập đến là một ngoại lệ bị bỏ qua một nửa khi nó được ném vào trong finallykhối ẩn của khối try-with-resources , trong bối cảnh một ngoại lệ hiện có được ném ra khỏi trykhối:

Một ngoại lệ có thể được đưa ra từ khối mã được liên kết với câu lệnh try-with-resources. Trong ví dụ writeToFileZipFileContents, một ngoại lệ có thể được ném ra từ khối try và có thể ném tối đa hai ngoại lệ từ câu lệnh try-with-resources khi nó cố gắng đóng các đối tượng ZipFile và BufferedWriter. Nếu một ngoại lệ được ném ra từ khối try và một hoặc nhiều ngoại lệ được ném ra khỏi câu lệnh try-with-resources, thì những ngoại lệ đó được ném ra từ câu lệnh try-with-resources sẽ bị loại bỏ và ngoại lệ do khối ném ra là một được ném bởi phương thức writeToFileZipFileContents. Bạn có thể truy xuất các ngoại lệ bị loại bỏ này bằng cách gọi phương thức Throwable.getSuppressed từ ngoại lệ được ném bởi khối try.

(Đó là trích dẫn một phần có tên "Ngoại lệ bị Loại bỏ" từ trang được liên kết.)


2
Lệnh

@Raedwald: Chủ yếu là có, tôi nghĩ vậy.
Jon Skeet

6
@JonSkeet @Raedwald: Tôi tin rằng câu trả lời này không thể xem xét rằng các ngoại lệ bị chặn tồn tại trước Java 7 (tôi không nói về các ngoại lệ bị bỏ qua ): nếu một finallykhối ném một ngoại lệ khi trykhối cũng ném một ngoại lệ, thì ngoại lệ ban đầu từ trykhối bị mất hoặc bị "chặn" (xem http://accu.org/index.php/journals/236 để biết thêm thông tin). Java 7 vừa thêm một phương thức thuận tiện để lưu trữ ngoại lệ khỏi finallykhối vì finallynó được tạo ngầm bởi try-with-resources.
JBert

5
Nhưng một điểm cần lưu ý là nếu chúng tôi cung cấp rõ ràng khối cuối cùng trong câu lệnh try-with-resource và ngoại lệ được ném từ nó thì nó sẽ được ưu tiên hơn các ngoại lệ được đưa ra từ khối try hoặc try-with-resource.
Aniket Thakur

63

Để làm rõ câu trích dẫn trong câu trả lời của Jon, chỉ có một ngoại lệ có thể được đưa ra bởi một phương thức (mỗi lần thực hiện) nhưng trong trường hợp a try-with-resources, nhiều ngoại lệ có thể được ném ra. Ví dụ: một cái có thể được ném vào trong khối và cái khác có thể được ném từ hàm ngầm finallyđược cung cấp bởi try-with-resources.

Trình biên dịch phải xác định cái nào trong số này để ném "thực sự". Nó chọn ném ngoại lệ được nêu ra trong mã rõ ràng (mã trong trykhối) thay vì ngoại lệ được ném bởi mã ngầm ( finallykhối). Do đó (các) ngoại lệ được đưa vào khối ngầm định bị loại bỏ (bỏ qua). Điều này chỉ xảy ra trong trường hợp có nhiều ngoại lệ.


1
Vì vậy, trường hợp ngoại lệ ức chế là một hệ quả của các tính năng mới cho phương tiện chúng ta cần có thời gian để ghi rườm rà try... finallykhoản khi mở và close()ing file: stackoverflow.com/questions/3305405/...
Raedwald

@JohnB Nhưng chúng ta có tùy chọn hàm tạo của Exception (anotherExcetion e) rit?
Kanagavelu Sugumar

3
@KanagaveluSugumar Hàm Exception(Exception cause)tạo được sử dụng để bọc một ngoại lệ gây ra trong một ngoại lệ khác có thể mang tính mô tả hơn. Tuy nhiên, trong trường hợp này chúng ta đang nói về hai ngoại lệ khác biệt mà không có mối quan hệ nhân quả. Ngoại lệ A không gây ra Ngoại lệ B do đó sẽ không có ý nghĩa gì khi quấn cái này vào cái kia. Ngoài ra, bạn đang giả định rằng người lập trình đang ném ngoại lệ thứ hai một cách rõ ràng và có quyền truy cập vào ngoại lệ đầu tiên. Đó sẽ không phải là trường hợp nếu cả hai nơi được ném bởi các cuộc gọi thư viện.
John B

1
@JohnB Tôi nghĩ rằng tuyên bố của bạn là sai. "Trình biên dịch phải xác định cái nào trong số này để ném" thực sự ". Nó chọn ném ngoại lệ được nêu trong mã rõ ràng (mã trong khối thử)" Nhưng trình biên dịch sẽ chỉ chọn ngoại lệ cuối cùng và ngoại lệ thử sẽ bị loại bỏ.
Kanagavelu Sugumar

1
@KanagaveluSugumar Theo tài liệu:If an exception is thrown from the try block and one or more exceptions are thrown from the try-with-resources statement, then those exceptions thrown from the try-with-resources statement are suppressed
John B

21

Trước Java7; Có những ngoại lệ được đưa ra trong mã nhưng bằng cách nào đó đã bị bỏ qua.

ví dụ)

public class SuppressedExceptions {
  public static void main(String[] args) throws Exception {
    try {
        callTryFinallyBlock();
    } catch (Exception e) {
        e.printStackTrace(); **//Only Finally Exception is Caught**
    }
  }

  private static void callTryFinallyBlock() throws Exception {
    try 
    {
        throw new TryException(); **//This is lost**
    }
    finally
    {
        FinallyException fEx = new FinallyException();
        throw fEx;
    }
  }
}

class TryException extends Exception {
}

class FinallyException extends Exception {
}

Một hàm tạo mới và hai phương thức mới đã được thêm vào lớp Throwable trong JDK 7. Chúng như sau:

Throwable.getSupressed(); // Returns Throwable[]
Throwable.addSupressed(aThrowable);

với cách tiếp cận mới này, chúng tôi cũng có thể xử lý những ngoại lệ bị triệt tiêu đó.

public class SuppressedExceptions {
  public static void main(String[] args) throws Exception {
    try {
        callTryFinallyBlock();
    } catch (Exception e) {
        e.printStackTrace();
        for(Throwable t: e.getSuppressed())
        {
            t.printStackTrace();
        }
    }
  }

  private static void callTryFinallyBlock() throws Exception {
    Throwable t = null;
    try 
    {
        throw new TryException();
    }
    catch (Exception e) {
        t = e;
    }
    finally
    {
        FinallyException fEx = new FinallyException();
        if(t != null)fEx.addSuppressed(t);
        throw fEx;
    }
  }
}

class TryException extends Exception {
}

class FinallyException extends Exception {
}

Trong Java7 try-with-resources; ngoại lệ tại AutoClosable :: close () được thêm làm ngoại lệ bị loại bỏ theo mặc định cùng với ngoại lệ thử.

Cũng cần biết rằng điều này khác với các ngoại lệ theo chuỗi (đã được giới thiệu với JDK 1.4 và nhằm mục đích giúp bạn có thể dễ dàng theo dõi các mối quan hệ nhân quả giữa các ngoại lệ.)


11

Chuyển đổi mã bên dưới:

public class MultipleExceptionsExample {

   static class IOManip implements Closeable{
       @Override
       public void close() {
           throw new RuntimeException("from IOManip.close");
       }
   }

   public static void main(String[] args) {
       try(IOManip ioManip = new IOManip()){
           throw new RuntimeException("from try!");
       }catch(Exception e){
           throw new RuntimeException("from catch!");
       }finally{
           throw new RuntimeException("from finally!");
       }
   }
}

Với tất cả các dòng, bạn sẽ nhận được: java.lang.RuntimeException: from finally!

Xóa finallykhối bạn sẽ nhận được:java.lang.RuntimeException: from catch!

Xóa catchkhối bạn sẽ nhận được:

Exception in thread "main" java.lang.RuntimeException: from try!
    Suppressed: java.lang.RuntimeException: from IOManip.close

9

Các ngoại lệ bị loại bỏ là các ngoại lệ bổ sung xảy ra trong câu lệnh try-with-resources (được giới thiệu trong Java 7 ) khi AutoCloseabletài nguyên bị đóng. Bởi vì nhiều ngoại lệ có thể xảy ra trong khi đóng AutoCloseabletài nguyên, các ngoại lệ bổ sung được gắn vào một ngoại lệ chính như là các ngoại lệ bị triệt tiêu .

Nhìn vào bytecode của một đoạn mã mẫu try-with-resources, các trình xử lý ngoại lệ JVM tiêu chuẩn được sử dụng để phù hợp với ngữ nghĩa try-with-resources.


0

Bạn cũng có thể loại bỏ các Ngoại lệ trong Java 6 (một thủ thuật nhỏ liên quan),

Tôi đã tạo một tiện ích xử lý minh bạch ngoại lệ ngăn chặn trong Java 1.6 và Java 1.7. Bạn có thể tìm thấy cách triển khai tại đây

Tất cả những gì bạn cần là gọi:

public static <T extends Throwable> T suppress(final T t, final Throwable suppressed) 

để áp dụng một ngoại lệ, và

public static Throwable [] getSuppressed(final Throwable t) {

để có được các ngoại lệ bị triệt tiêu của một Ngoại lệ, trong trường hợp ai đó vẫn sử dụng Java 1.6


0

ARM - Quản lý tài nguyên tự động (Được giới thiệu từ Java 7)

Lấy một ví dụ rất đơn giản

static String readFirstLineFromFileWithFinallyBlock(String path)
                                                     throws IOException {
    BufferedReader br = new BufferedReader(new FileReader(path));
    try {
        return br.readLine();
    } finally {
        if (br != null) br.close();
    }
}

Bây giờ nếu readLine()hàm ném Ngoại lệ và sau đó close()hàm chẵn [trong khối cuối cùng] ném ngoại lệ thì cái sau được ưu tiên hơn và được ném trở lại hàm đang gọi. Trong trường hợp này,Exception thrown by the readLine() method is ignored/suppressed . Bạn có thể liên kết ngoại lệ gây ra ngoại lệ trong ngoại lệ của mình và lặp lại ngoại lệ từ khối cuối cùng.

java 7chức năng đã được cung cấp để truy xuất các Ngoại lệ bị chặn. Bạn có thể gọi public final java.lang.Throwable[] getSuppressed()hàm trên đối tượng có thể ném được đã bắt để xem các Ngoại lệ bị triệt tiêu.

Ví dụ:

static String readFirstLineFromFileWithFinallyBlock(String path)
        throws Exception {
    try (BufferedReader br = new BufferedReader(new FileReader(path));) {
        return br.readLine();
    }
}

Bây giờ nếu br.readLine();dòng ném Exception1và sau đó cho phép nói Exception2được ném ra trong khi đóng tài nguyên [Hãy tưởng tượng điều này xảy ra trong một khối cuối cùng ngầm định mà câu lệnh try-with-resource tạo ra] thì Exception1 loại bỏ Exception2.

Một số điểm cần lưu ý ở đây -

  1. Nếu khối try-with-resource ném ra ngoại lệ, tức là trong khi tài nguyên khởi tạo thì khối try sẽ không thực thi và cùng một ngoại lệ sẽ được ném ra.
  2. Nếu việc khởi tạo tài nguyên thành công, hãy thử khối ném một ngoại lệ và ngoại lệ được ném ra trong khi đóng tài nguyên thì ngoại lệ được ném ra trong khi đóng tài nguyên sẽ bị loại bỏ bởi ngoại lệ được ném từ khối thử.
  3. Nếu bạn cung cấp khối cuối cùng rõ ràng và ngoại lệ được ném từ khối đó, nó sẽ ngăn chặn tất cả các ngoại lệ khác. (Khối cuối cùng rõ ràng này thực thi sau khi tài nguyên bị đóng)

Tôi đã biên soạn hầu hết các tình huống có thể xảy ra với các đoạn mã và đầu ra trong bài đăng sau.

Đã loại bỏ các ngoại lệ trong java 7

Hy vọng rằng sẽ giúp.


1
Tuy nhiên, trong trường hợp này, khối Exceptionbắt nguồn từ khối try {} sẽ không tự động bị chặn. Lập trình viên có thể chọn làm như vậy, nhưng bạn thì không. Chỉ thử với tài nguyên, khi cần thiết, mới tự động loại bỏ các ngoại lệ. Và khi anh ta làm như vậy, nó là ngoại lệ từ khối cuối cùng tương đương của bạn sẽ trở nên bị đàn áp.
Martin Andersson

1
@MartinAndersson Bạn nói đúng. Có một số nhầm lẫn khi tôi đã viết câu trả lời. Tôi hy vọng câu trả lời được chỉnh sửa cung cấp thông tin chi tiết tốt hơn.
Aniket Thakur

-1

Tôi nghĩ rằng điều này phải làm với "cơ sở ngoại lệ chuỗi". Nó sẽ ảnh hưởng đến cách một ngoại lệ được xử lý bởi cơ sở này khi dấu vết ngăn xếp phát triển. Theo thời gian, các ngoại lệ nằm trong nhóm ngoại lệ được xâu chuỗi có thể bị loại bỏ. Xem tài liệu Throwable để biết thêm chi tiết.

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.