Ngoại lệ ném vào bắt và cuối cùng mệnh đề


155

Về câu hỏi cho Java tại trường đại học, có đoạn mã này:

class MyExc1 extends Exception {}
class MyExc2 extends Exception {}
class MyExc3 extends MyExc2 {}

public class C1 {
    public static void main(String[] args) throws Exception {
        try {
            System.out.print(1);
            q();
        }
        catch (Exception i) {
            throw new MyExc2();
        }
        finally {
            System.out.print(2);
            throw new MyExc1();
        }
    }

    static void q() throws Exception {
        try {
            throw new MyExc1();
        }
        catch (Exception y) {
        }
        finally {
            System.out.print(3);
            throw new Exception();
        }
    }
}

Tôi đã được yêu cầu để cung cấp đầu ra của nó. Tôi đã trả lời 13Exception in thread main MyExc2, nhưng câu trả lời đúng là 132Exception in thread main MyExc1. Tại sao? Tôi chỉ không thể hiểu nơi nào MyExc2đi.

Câu trả lời:


167

Dựa trên việc đọc câu trả lời của bạn và xem bạn có thể đưa ra câu trả lời như thế nào, tôi tin rằng bạn nghĩ rằng "ngoại lệ trong tiến trình" có "ưu tiên". Ghi nhớ:

Khi một ngoại lệ mới được ném vào một khối bắt hoặc cuối cùng là khối sẽ lan truyền ra khỏi khối đó, thì ngoại lệ hiện tại sẽ bị hủy bỏ (và bị lãng quên) vì ngoại lệ mới được truyền ra bên ngoài. Ngoại lệ mới bắt đầu mở ra ngăn xếp giống như bất kỳ ngoại lệ nào khác, loại bỏ khỏi khối hiện tại (khối bắt hoặc cuối cùng) và tuân theo bất kỳ khối bắt nào có thể áp dụng hoặc cuối cùng là chặn trên đường đi.

Lưu ý rằng các khối bắt hoặc cuối cùng có thể áp dụng bao gồm:

Khi một ngoại lệ mới được ném vào một khối bắt, ngoại lệ mới vẫn phải chịu khối cuối cùng đó, nếu có.

Bây giờ truy xuất thực thi ghi nhớ rằng, bất cứ khi nào bạn nhấn throw, bạn nên hủy bỏ truy tìm ngoại lệ hiện tại và bắt đầu truy tìm ngoại lệ mới.


7
«Dựa vào việc đọc câu trả lời của bạn và xem bạn có thể đưa ra câu trả lời như thế nào, tôi tin rằng bạn nghĩ rằng" ngoại lệ trong tiến trình "đã" được ưu tiên "» Cảm ơn bạn ... đó chính xác là suy nghĩ của tôi :)
Jub ware

39

Đây là những gì Wikipedia nói về mệnh đề cuối cùng:

Phổ biến hơn là một mệnh đề liên quan (cuối cùng hoặc đảm bảo) được thực thi cho dù có ngoại lệ xảy ra hay không, thường là để giải phóng các tài nguyên có được trong phần thân của khối xử lý ngoại lệ.

Hãy mổ xẻ chương trình của bạn.

try {
    System.out.print(1);
    q();
}

Vì vậy, 1sẽ được xuất ra màn hình, sau đó q()được gọi. Trong q(), một ngoại lệ được ném. Ngoại lệ sau đó bị bắt bởi Exception ynhưng nó không làm gì cả. Một mệnh đề cuối cùng sau đó được thực thi (nó phải), do đó, 3sẽ được in ra màn hình. Bởi vì (trong phương thức q()có một ngoại lệ được ném trong mệnh đề cuối cùng , q()phương thức cũng chuyển ngoại lệ cho ngăn xếp cha (bằng cách throws Exceptionkhai báo phương thức) new Exception()sẽ được ném và bắt bởi catch ( Exception i ), MyExc2ngoại lệ sẽ được ném (bây giờ thêm nó vào ngăn xếp ngoại lệ ), nhưng cuối cùng trong mainkhối sẽ được thực hiện đầu tiên.

Vì vậy, trong

catch ( Exception i ) {
    throw( new MyExc2() );
} 
finally {
    System.out.print(2);
    throw( new MyExc1() );
}

Một mệnh đề cuối cùng được gọi là ... (hãy nhớ rằng, chúng ta vừa bị bắt Exception ivà ném MyExc2) về bản chất, 2được in trên màn hình ... và sau khi 2được in trên màn hình, một MyExc1ngoại lệ được đưa ra. MyExc1được xử lý theo public static void main(...)phương pháp.

Đầu ra:

"132Exception trong chủ đề chính MyExc1"

Giảng viên chính xác! :-)

Về bản chất , nếu cuối cùng bạn có mệnh đề try / Catch, cuối cùng sẽ được thực thi ( sau khi bắt ngoại lệ trước khi ném ngoại lệ bị bắt ra)


catchđược thực hiện kể từ khi q()ném một khối Exceptiontừ chính nó finally.
Péter Török

"Trong q (), một ngoại lệ được ném nhưng trước khi ngoại lệ được ném hoàn toàn, mệnh đề cuối cùng được thực thi đầu tiên, vì vậy, 3 sẽ được in ra màn hình." Er ... không, ngoại lệ đầu tiên được ném vào qthực thi cho catchkhối trống trong q(nuốt ngoại lệ này), sau đó vào finallykhối trong q. Cuối cùng nói chặn in 3, sau đó ném một ngoại lệ mới, mà nhờ q's throws Exceptionđược truyền lên stack cho phụ huynh.
Powerlord

38

Các ngoại lệ trong khối cuối cùng thay thế ngoại lệ trong khối bắt.

Trích dẫn từ phiên bản Đặc tả ngôn ngữ Java 14 :

Nếu khối bắt hoàn thành đột ngột vì lý do R, thì khối cuối cùng được thực thi. Sau đó, có một sự lựa chọn:

  • Nếu khối cuối cùng hoàn thành bình thường, thì câu lệnh thử hoàn thành đột ngột vì lý do R.

  • Nếu khối cuối cùng hoàn thành đột ngột vì lý do S, thì câu lệnh thử hoàn thành đột ngột vì lý do S (và lý do R bị loại bỏ).


21

Cuối cùng mệnh đề được thực thi ngay cả khi ngoại lệ được ném từ bất kỳ đâu trong khối try / Catch.

Bởi vì đó là lần cuối cùng được thực thi mainvà nó ném ra một ngoại lệ, đó là ngoại lệ mà người gọi nhìn thấy.

Do đó tầm quan trọng của việc đảm bảo rằng finallymệnh đề không ném bất cứ thứ gì, bởi vì nó có thể nuốt ngoại lệ khỏi trykhối.


5
Nó cũng sẽ được thực thi NGAY nếu không có ngoại lệ được ném trong khối thử / bắt
nanda

2
+1: Trực tiếp và đến điểm mà không uốn khúc xuống toàn bộ ngăn xếp mà OP dường như đã hiểu.
Powerlord

9

Không methodthể có throwhai ngoại lệ cùng một lúc. Nó sẽ luôn ném cú ném cuối cùng exception, trong trường hợp này nó sẽ luôn là cú ném từ finallykhối.

Khi ngoại lệ đầu tiên từ phương thức q()được ném, nó sẽ bị bắt và sau đó bị nuốt bởi ngoại lệ ném khối cuối cùng.

q () -> ném new Exception -> main catch Exception -> throw new Exception -> finally ném mới exception(và một từ catchbị "mất")


3

Cách dễ nhất để nghĩ về điều này là tưởng tượng rằng có một biến toàn cục cho toàn bộ ứng dụng đang giữ ngoại lệ hiện tại.

Exception currentException = null;

Khi mỗi ngoại lệ được đưa ra, "currentException" được đặt thành ngoại lệ đó. Khi ứng dụng kết thúc, nếu currentException là! = Null thì thời gian chạy báo lỗi.

Ngoài ra, các khối cuối cùng luôn chạy trước khi phương thức thoát. Sau đó, bạn có thể yêu cầu đoạn mã thành:

public class C1 {

    public static void main(String [] argv) throws Exception {
        try {
            System.out.print(1);
            q();

        }
        catch ( Exception i ) {
            // <-- currentException = Exception, as thrown by q()'s finally block
            throw( new MyExc2() ); // <-- currentException = MyExc2
        }
        finally {
             // <-- currentException = MyExc2, thrown from main()'s catch block
            System.out.print(2);
            throw( new MyExc1() ); // <-- currentException = MyExc1
        }

    }  // <-- At application exit, currentException = MyExc1, from main()'s finally block. Java now dumps that to the console.

    static void q() throws Exception {
        try {
            throw( new MyExc1() ); // <-- currentException = MyExc1
        }
        catch( Exception y ) {
           // <-- currentException = null, because the exception is caught and not rethrown
        }
        finally {
            System.out.print(3);
            throw( new Exception() ); // <-- currentException = Exception
        }
    }
}

Thứ tự mà ứng dụng thực thi là:

main()
{
  try
    q()
    {
      try
      catch
      finally
    }
  catch
  finally
}

1

Mọi người đều biết rằng khối cuối cùng được thực thi sau khi thử và bắt và luôn luôn được thực thi .... Nhưng như bạn đã thấy, đôi khi hơi khó khăn khi kiểm tra các đoạn mã dưới đây và bạn sẽ trả lời và ném câu lệnh don Luôn luôn làm những gì họ nên làm theo thứ tự mà chúng tôi mong đợi chủ đề.

Chúc mừng.

/////////////Return dont always return///////

try{

    return "In Try";

}

finally{

    return "In Finally";

}

////////////////////////////////////////////


////////////////////////////////////////////    
while(true) { 

    try {

        return "In try";

   } 

   finally{

        break;     

    }          
}              
return "Out of try";      
///////////////////////////////////////////


///////////////////////////////////////////////////

while (true) {     

    try {            

        return "In try";    

     } 
     finally {   

         continue;  

     }                         
}
//////////////////////////////////////////////////

/////////////////Throw dont always throw/////////

try {

    throw new RuntimeException();

} 
finally {

    return "Ouuuups no throw!";

}
////////////////////////////////////////////////// 

1
class MyExc1 extends Exception {}
class MyExc2 extends Exception {}
class MyExc3 extends MyExc2 {}

public class C1 {
    public static void main(String[] args) throws Exception {
        try {
            System.out.print("TryA L1\n");
            q();
            System.out.print("TryB L1\n");
        }
        catch (Exception i) {
            System.out.print("Catch L1\n");                
        }
        finally {
            System.out.print("Finally L1\n");
            throw new MyExc1();
        }
    }

    static void q() throws Exception {
        try {
            System.out.print("TryA L2\n");
            q2();
            System.out.print("TryB L2\n");
        }
        catch (Exception y) {
            System.out.print("Catch L2\n");
            throw new MyExc2();  
        }
        finally {
            System.out.print("Finally L2\n");
            throw new Exception();
        }
    }

    static void q2() throws Exception {
        throw new MyExc1();
    }
}

Đặt hàng:

TryA L1
TryA L2
Catch L2
Finally L2
Catch L1
Finally L1        
Exception in thread "main" MyExc1 at C1.main(C1.java:30)

https://www.compilejava.net/


1
Mặc dù đoạn mã này có thể là giải pháp, bao gồm một lời giải thích thực sự giúp cải thiện chất lượng bài đăng của bạn. Hãy nhớ rằng bạn sẽ trả lời câu hỏi cho độc giả trong tương lai và những người đó có thể không biết lý do cho đề xuất mã của bạn
Rahul Gupta

1

Logic là rõ ràng cho đến khi hoàn thành in ra 13. Sau đó ném ngoại lệ trong q()được đánh bắt bằng catch (Exception i)trong main()new MyEx2()sẵn sàng để được ném. Tuy nhiên, trước khi ném ngoại lệ, finallykhối phải được thực thi trước. Sau đó, đầu ra trở thành 132finallyyêu cầu ném một ngoại lệ khác new MyEx1().

Là một phương pháp không thể ném nhiều hơn một Exception, nó sẽ luôn ném mới nhất Exception. Nói cách khác, nếu cả hai catchfinallycác khối cố gắng ném Exception, thì phần Exceptionbị bắt bị nuốt và chỉ có ngoại lệ trong finallysẽ bị ném.

Do đó, trong chương trình này, Ngoại lệ MyEx2bị nuốt và MyEx1bị ném. Ngoại lệ này được loại bỏ main()và không còn bị bắt, do đó JVM dừng lại và đầu ra cuối cùng là 132Exception in thread main MyExc1.

Về bản chất, nếu bạn có một finallytrong một try/catchkhoản tương ứng, một finallysẽ được thực hiện sau khi bắt ngoại lệ , nhưng trước khi ném bất kỳ ngoại lệ bắt , và CHỈ ngoại trừ mới nhất sẽ được ném cuối cùng .


0

Tôi nghĩ rằng bạn chỉ cần đi bộ các finallykhối:

  1. In "1".
  2. finallytrong qprint "3".
  3. finallytrong mainprint "2".

0

Để xử lý loại tình huống này tức là xử lý ngoại lệ được đưa ra bởi khối cuối cùng. Bạn có thể bao quanh khối cuối cùng bằng cách thử khối: Xem ví dụ bên dưới trong python:

try:
   fh = open("testfile", "w")
   try:
      fh.write("This is my test file for exception handling!!")
   finally:
      print "Going to close the file"
      fh.close()
except IOError:
   print "Error: can\'t find file or read data"

-1

Tôi nghĩ rằng điều này giải quyết vấn đề:

boolean allOk = false;
try{
  q();
  allOk = true;
} finally {
  try {
     is.close();
  } catch (Exception e) {
     if(allOk) {
       throw new SomeException(e);
     }
  }
}

3
Vấn đề gì bạn sẽ "giải quyết"? Bạn có nghĩa là câu hỏi trong kỳ thi? Vâng, nó đã trả lời. Nếu bạn có nghĩa là vấn đề của mã đã cho, vì nó chỉ là một câu hỏi thi, không có nghĩa gì để đổ lỗi cho nó.
Động cơ Trái đấ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.