มีข้อยกเว้นเกิดขึ้นในการจับและประโยคสุดท้าย


155

สำหรับคำถามเกี่ยวกับ Java ที่มหาวิทยาลัยมีตัวอย่างของรหัสนี้:

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();
        }
    }
}

ฉันถูกขอให้ส่งออก ผมตอบแต่คำตอบที่ถูกต้องคือ13Exception in thread main MyExc2 132Exception in thread main MyExc1ทำไมถึงเป็นอย่างนั้น? ฉันไม่เข้าใจว่าจะMyExc2ไปที่ไหน

คำตอบ:


167

จากการอ่านคำตอบของคุณและดูว่าคุณมีโอกาสเกิดขึ้นได้อย่างไรผมเชื่อว่าคุณคิดว่า โปรดทราบ:

เมื่อมีการโยนข้อยกเว้นใหม่ในบล็อก catch หรือบล็อกสุดท้ายที่จะกระจายออกจากบล็อกนั้นดังนั้นข้อยกเว้นปัจจุบันจะถูกยกเลิก (และลืม) เนื่องจากข้อยกเว้นใหม่ถูกเผยแพร่ออกไปด้านนอก ข้อยกเว้นใหม่เริ่มคลี่คลายสแต็กเช่นเดียวกับข้อยกเว้นอื่น ๆ ยกเลิกออกจากบล็อกปัจจุบัน (จับหรือในที่สุดบล็อก) และอาจมีการจับใด ๆ ที่ใช้บังคับหรือในที่สุดบล็อกไปพร้อมกัน

โปรดทราบว่าการใช้ catch หรือสุดท้ายบล็อกรวมถึง:

เมื่อมีข้อยกเว้นใหม่ถูกโยนลงในบล็อก catch ข้อยกเว้นใหม่จะยังคงอยู่ภายใต้บล็อกสุดท้ายของ catch นั้นถ้ามี

ตอนนี้ย้อนกลับการดำเนินการที่จำได้ว่าเมื่อใดก็ตามที่คุณกดthrowคุณควรยกเลิกการติดตามข้อยกเว้นปัจจุบันและเริ่มติดตามข้อยกเว้นใหม่


7
«ขึ้นอยู่กับการอ่านคำตอบของคุณและเห็นว่าคุณมีแนวโน้มที่ขึ้นมาพร้อมกับมันผมเชื่อว่าคุณคิดว่า 'ข้อยกเว้นในความคืบหน้า' มี 'ความสำคัญ'»ขอบคุณ ... ที่ตรงความคิดของฉัน :)
Jubstuff

39

นี่คือสิ่งที่Wikipediaพูดเกี่ยวกับประโยคสุดท้าย:

ที่พบบ่อยคือข้อที่เกี่ยวข้อง (ในที่สุดหรือให้แน่ใจว่า) ที่จะดำเนินการไม่ว่าจะเกิดข้อยกเว้นหรือไม่โดยทั่วไปจะปล่อยทรัพยากรที่ได้มาภายในร่างกายของบล็อกการจัดการข้อยกเว้น

มาผ่าโปรแกรมของคุณกัน

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

ดังนั้น1จะถูกส่งออกไปยังหน้าจอแล้วq()เรียกว่า ในq()ข้อยกเว้นจะถูกโยน ข้อยกเว้นนั้นถูกจับได้Exception yแต่ไม่ได้ทำอะไรเลย ที่สุดข้อจะถูกดำเนินการแล้ว (มันมี) ดังนั้น3จะถูกพิมพ์ไปที่หน้าจอ เพราะ (ในวิธีการที่q()มีข้อยกเว้นโยนในที่สุดข้อยังq()วิธีการผ่านข้อยกเว้นไปยังกองแม่ (โดยthrows Exceptionในการประกาศวิธีการ) new Exception()จะถูกโยนและจับโดยcatch ( Exception i ), MyExc2ยกเว้นจะถูกโยน (ตอนนี้เพิ่มไปยังกองการยกเว้น ) แต่ในที่สุดในmainบล็อกจะถูกดำเนินการก่อน

ดังนั้นใน

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

ที่สุดข้อที่เรียกว่า ... (จำเราได้จับเพียงException iและโยนMyExc2) ในสาระสำคัญ2จะมีการพิมพ์บนหน้าจอ ... และหลังจากที่2มีการพิมพ์บนหน้าจอเป็นMyExc1ยกเว้นจะโยน MyExc1ได้รับการจัดการโดยpublic static void main(...)วิธีการ

เอาท์พุท:

"132Exception ในเธรดหลัก MyExc1"

อาจารย์ถูกต้อง! :-)

ในสาระสำคัญหากคุณมีในที่สุดในประโยคลอง / จับในที่สุดก็จะถูกดำเนินการ ( หลังจากจับข้อยกเว้นก่อนที่จะโยนข้อยกเว้นที่จับออก)


catchจะถูกดำเนินการตั้งแต่q()โยนExceptionจากของตัวเองfinallyบล็อก
PéterTörök

"ใน q () ข้อยกเว้นถูกส่งออกไป แต่ก่อนที่ข้อยกเว้นจะถูกส่งออกไปอย่างสมบูรณ์ส่วนคำสั่งสุดท้ายจะถูกดำเนินการครั้งแรกดังนั้น 3 จะถูกพิมพ์ไปที่หน้าจอ" เอ่อ ... ไม่ข้อยกเว้นแรกที่ส่งออกqไป ที่ว่างเปล่าcatchกระชากq(ซึ่งกลืนข้อยกเว้นนี้) จากนั้นไปกระชากfinally qกล่าวในที่สุดก็ป้องกันการพิมพ์3แล้วโยนข้อยกเว้นใหม่ซึ่งต้องขอบคุณq's throws Exceptionจะถูกส่งขึ้นสแต็คเพื่อแม่
Powerlord

38

ข้อยกเว้นในที่สุดบล็อกแทนที่ข้อยกเว้นในบล็อกการจับ

การอ้างอิงจากJava Language Specification 14 edition :

หากบล็อก catch เสร็จสมบูรณ์อย่างกะทันหันด้วยเหตุผล R แล้วบล็อกสุดท้ายจะถูกดำเนินการ จากนั้นมีทางเลือกคือ:

  • หากการบล็อกสุดท้ายเสร็จสมบูรณ์ตามปกติคำสั่งลองจะเสร็จสมบูรณ์โดยทันทีด้วยเหตุผล R

  • หากการบล็อกในที่สุดเสร็จสมบูรณ์ในทันทีสำหรับเหตุผล S ดังนั้นคำสั่งลองจะเสร็จสมบูรณ์โดยทันทีด้วยเหตุผล S (และเหตุผล R ถูกยกเลิก)


21

ข้อสุดท้ายจะถูกดำเนินการแม้ในขณะที่มีการโยนข้อยกเว้นจากที่ใดก็ได้ในบล็อกลอง / catch

เพราะมันเป็นครั้งสุดท้ายที่จะถูกประหารชีวิตในmainและมันเป็นข้อยกเว้นนั่นคือข้อยกเว้นที่ผู้โทรเห็น

ดังนั้นความสำคัญของการทำให้แน่ใจว่าfinallyประโยคไม่ได้โยนอะไรเลยเพราะมันสามารถกลืนข้อยกเว้นจากtryบล็อก


5
มันจะถูกประหารชีวิตแม้ในกรณีที่ไม่มีข้อยกเว้นในการลอง / จับบล็อค
nanda

2
+1: ตรงและตรงประเด็นโดยไม่วกวนลงทั้งกองซึ่ง OP ดูเหมือนจะเข้าใจแล้ว
Powerlord

9

methodไม่สามารถthrowยกเว้นสองในเวลาเดียวกัน มันจะโยนโยนครั้งสุดท้ายเสมอexceptionซึ่งในกรณีนี้มันจะถูกโยนออกมาจากfinallyบล็อกเสมอ

เมื่อข้อยกเว้นแรกจากวิธีการq()โยนมันจะ catch'ed แล้วกลืนโดยข้อยกเว้นสุดท้ายโยนบล็อก

q () ->โยนnew Exception -> main catch Exception -> throw new Exception -> finallyโยนใหม่exception(และอันใหม่จากcatch"หายไป")


3

วิธีที่ง่ายที่สุดในการคิดสิ่งนี้คือจินตนาการว่ามีตัวแปรทั่วโลกสำหรับแอปพลิเคชันทั้งหมดที่มีข้อยกเว้นปัจจุบัน

Exception currentException = null;

เมื่อมีการโยนข้อยกเว้นแต่ละรายการ "currentException" จะถูกตั้งค่าเป็นข้อยกเว้นนั้น เมื่อแอปพลิเคชันสิ้นสุดลงถ้า currentException คือ! = null จากนั้นรันไทม์รายงานข้อผิดพลาด

นอกจากนี้บล็อกสุดท้ายจะรันเสมอก่อนที่เมธอดจะออก จากนั้นคุณสามารถขอข้อมูลโค้ดเพื่อ:

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
        }
    }
}

ลำดับที่แอปพลิเคชันดำเนินการคือ:

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

1

เป็นที่ทราบกันดีว่าในที่สุดบล็อกจะถูกประหารชีวิตหลังจากการลองและจับได้และจะถูกดำเนินการเสมอ .... แต่อย่างที่คุณเห็นมันเป็นเรื่องยากเล็กน้อยในบางครั้งตรวจสอบข้อมูลโค้ดเหล่านั้นด้านล่างและคุณจะทราบว่า ไม่ควรทำสิ่งที่ควรทำตามลำดับที่เราคาดหวังไว้

ไชโย

/////////////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();
    }
}

ใบสั่ง:

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
ในขณะที่ข้อมูลโค้ดนี้อาจเป็นโซลูชันรวมถึงคำอธิบายช่วยปรับปรุงคุณภาพการโพสต์ของคุณ จำไว้ว่าคุณกำลังตอบคำถามสำหรับผู้อ่านในอนาคตและผู้คนเหล่านั้นอาจไม่ทราบสาเหตุของการแนะนำรหัสของคุณ
Rahul Gupta

1

13ตรรกะที่ชัดเจนจนเสร็จสิ้นการพิมพ์ออกมา จากนั้นข้อยกเว้นที่โยนเข้ามาq()จะถูกจับโดยcatch (Exception i)ในmain()และnew MyEx2()พร้อมที่จะถูกโยนทิ้ง อย่างไรก็ตามก่อนที่จะทำการยกเว้นfinallyจะต้องดำเนินการบล็อกก่อน แล้วออกจะกลายเป็น132และขอให้โยนข้อยกเว้นอีก finallynew MyEx1()

เป็นวิธีที่ไม่สามารถโยนมากกว่าหนึ่ง Exceptionมันจะเป็นการโยนครั้งล่าสุดExceptionเสมอ กล่าวอีกนัยหนึ่งถ้าทั้งสองcatchและfinallyบล็อกพยายามที่จะโยนExceptionจากนั้นExceptionจับในจะถูกกลืนและมีเพียงข้อยกเว้นในfinallyจะถูกโยน

ดังนั้นในโปรแกรมนี้ข้อยกเว้นMyEx2จะถูกกลืนและMyEx1ถูกโยน ข้อยกเว้นนี้จะถูกโยนออกมาจากmain()และไม่นานก็ถูกจับได้จึง JVM 132Exception in thread main MyExc1หยุดและผลลัพธ์สุดท้ายคือ

ในสาระสำคัญถ้าคุณมีfinallyในtry/catchประโยคfinallyจะถูกดำเนินการหลังจากจับยกเว้นแต่ก่อนที่จะโยนข้อยกเว้นใด ๆ ที่จับ , และมีเพียงข้อยกเว้นล่าสุดจะถูกโยนในท้ายที่สุด


0

ฉันคิดว่าคุณต้องเดิน finallyบล็อก:

  1. พิมพ์ "1"
  2. finally ใน qพิมพ์ "3"
  3. finallyในการmainพิมพ์ "2"

0

เพื่อจัดการกับสถานการณ์เช่นนี้คือการจัดการข้อยกเว้นที่เกิดจากการบล็อกในที่สุด คุณสามารถล้อมรอบบล็อกในที่สุดโดยลองบล็อก: ดูตัวอย่างด้านล่างในหลาม:

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

ฉันคิดว่านี่แก้ปัญหา:

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

3
คุณกำลังจะแก้ปัญหาอะไร คุณหมายถึงคำถามในการสอบหรือไม่? มันตอบแล้วดี หากคุณหมายถึงปัญหาของรหัสที่ให้เนื่องจากเป็นเพียงคำถามสอบไม่มีเหตุผลที่จะตำหนิมัน
Earth Engine
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.