ในที่สุดบล็อกจะถูกเรียกใช้งานใน Java เสมอหรือไม่


2382

เมื่อพิจารณาถึงรหัสนี้ฉันสามารถมั่นใจได้อย่างแน่นอนว่าfinallyบล็อกจะดำเนินการเสมอไม่ว่าsomething()จะเกิดอะไรขึ้น

try {  
    something();  
    return success;  
}  
catch (Exception e) {   
    return failure;  
}  
finally {  
    System.out.println("I don't know if this will get printed out");
}

473
หากไม่เป็นเช่นนั้นควรตั้งชื่อคำหลักprobablyแทน
เที่ยงไหม

4
มีความเป็นไปได้ที่ซ้ำกันใน Java, ทรัมป์คืนค่าในที่สุด
polygenelubricants


2
จาวาที่มีประสิทธิภาพบอกว่าเป็นอย่างอื่นinformit.com/articles/article.aspx?p=1216151&seqNum=7
Binoy Babu

27
@BinoyBabu ผู้เข้ารอบสุดท้าย ! = finally; finalizer == finalize()วิธีการ
jaco0646

คำตอบ:


2697

ใช่finallyจะถูกเรียกใช้หลังจากการดำเนินการของtryหรือcatchบล็อกรหัส

เวลาเท่านั้นที่finallyจะไม่ถูกเรียกคือ:

  1. หากคุณวิงวอน System.exit()
  2. หากคุณวิงวอน Runtime.getRuntime().halt(exitStatus)
  3. หาก JVM ขัดข้องก่อน
  4. หาก JVM มาถึงการวนซ้ำไม่สิ้นสุด (หรือคำสั่งที่ไม่สามารถขัดจังหวะและไม่สิ้นสุดอื่น ๆ ) ในบล็อกtryหรือcatch
  5. หากระบบปฏิบัติการบังคับให้ยุติกระบวนการ JVM เช่นkill -9 <pid>บน UNIX
  6. หากระบบโฮสต์ตาย เช่นไฟฟ้าขัดข้องฮาร์ดแวร์ผิดพลาด OS ตื่นตระหนกและอื่น ๆ
  7. หากfinallyบล็อกกำลังถูกเรียกใช้งานโดยเธรด daemon และเธรดที่ไม่ใช่ daemon อื่นทั้งหมดออกก่อนที่จะfinallyถูกเรียกใช้

44
ที่จริงแล้วthread.stop()ไม่จำเป็นต้องป้องกันfinallyการถูกบล็อก
Piotr Findeisen

181
วิธีการเกี่ยวกับที่เราบอกว่าfinallyบล็อกจะถูกเรียกว่าหลังจากtryบล็อกและก่อนที่จะควบคุมการส่งผ่านไปยังงบดังต่อไปนี้ ที่สอดคล้องกับลองบล็อกที่เกี่ยวข้องกับการวนซ้ำไม่ จำกัด และสุดท้ายบล็อกไม่ถูกเรียกจริง
Andrzej Doyle

9
นอกจากนี้ยังมีอีกกรณีหนึ่งเมื่อเราใช้บล็อกลองจับในที่สุดซ้อนกัน
ruhungry

7
นอกจากนี้ในที่สุดบล็อกจะไม่ถูกเรียกในกรณีที่เกิดข้อยกเว้นโดยเธรด daemon
Amrish Pandey

14
@BinoyBabu - มันเกี่ยวกับ finalizer ไม่ใช่บล็อกในที่สุด
avmohan

567

รหัสตัวอย่าง:

public static void main(String[] args) {
    System.out.println(Test.test());
}

public static int test() {
    try {
        return 0;
    }
    finally {
        System.out.println("finally trumps return.");
    }
}

เอาท์พุท:

finally trumps return. 
0

18
FYI: ใน C # พฤติกรรมนั้นเหมือนกันนอกเหนือจากข้อเท็จจริงที่ว่าไม่อนุญาตให้แทนที่คำสั่งในfinally-clause ด้วยreturn 2;(Compiler-Error)
Alexander Pacha

15
นี่คือรายละเอียดที่สำคัญที่ควรระวัง: stackoverflow.com/a/20363941/2684342
WoodenKitty

18
คุณสามารถเพิ่มคำสั่ง return ในที่สุดบล็อกตัวเองซึ่งจะแทนที่ค่าส่งคืนก่อนหน้า สิ่งนี้ยังทิ้งข้อยกเว้นที่ไม่สามารถจัดการได้อย่างน่าอัศจรรย์ ณ จุดนี้คุณควรพิจารณาปรับเปลี่ยนรหัสของคุณอีกครั้ง
Zyl

8
นั่นไม่ได้พิสูจน์ว่าจริง ๆ แล้วในที่สุดปั้นกลับมา ค่าส่งคืนถูกพิมพ์จากรหัสผู้โทร ดูเหมือนจะไม่ได้พิสูจน์อะไรมาก
Trimtab

20
ขออภัยนี่เป็นเพียงการสาธิตไม่ใช่ข้อพิสูจน์ มันเป็นเพียงข้อพิสูจน์ถ้าคุณสามารถแสดงให้เห็นว่าตัวอย่างนี้จะทำงานในลักษณะนี้ในทุกแพลตฟอร์ม Java และตัวอย่างที่คล้ายกันก็มักจะทำงานในลักษณะนี้
สตีเฟ่นซี

391

นอกจากนี้แม้ว่าจะเป็นแนวปฏิบัติที่ไม่ดีหากมีคำสั่งส่งคืนภายในบล็อกสุดท้าย แต่จะดีกว่าผลตอบแทนอื่น ๆ จากบล็อกปกติ นั่นคือบล็อกต่อไปนี้จะคืนค่าเท็จ:

try { return true; } finally { return false; }

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


95
นี่คือการปฏิบัติที่ไม่ดีจริงๆ ดูstackoverflow.com/questions/48088/…สำหรับข้อมูลเพิ่มเติมเกี่ยวกับสาเหตุที่ไม่ดี
John Meagher

23
ตกลง ในที่สุดก็กลับมาภายใน {} ละเว้นข้อยกเว้นใด ๆ ที่เกิดขึ้นในลอง {} น่ากลัว!
neu242

8
@ dominicbri7 ทำไมคุณคิดว่ามันเป็นการปฏิบัติที่ดีกว่า และทำไมจึงแตกต่างเมื่อฟังก์ชั่น / วิธีการเป็นโมฆะ?
corsiKa

8
ด้วยเหตุผลเดียวกับที่ฉันไม่เคยใช้ goto ในรหัส C ++ ของฉัน ฉันคิดว่าผลตอบแทนหลายรายการทำให้การอ่านยากขึ้นและยากขึ้นในการดีบัก (แน่นอนในกรณีง่าย ๆ ที่ไม่ได้ใช้) ฉันเดาว่าเป็นเพียงบุคคลากรนิยมของบุคคลและในที่สุดคุณก็สามารถบรรลุสิ่งเดียวกันโดยใช้วิธีการใดวิธีหนึ่ง
dominicbri7

16
ฉันมักจะใช้ผลตอบแทนเป็นจำนวนมากเมื่อมีกรณีพิเศษบางประเภทเกิดขึ้น ชอบถ้า (มีเหตุผลที่จะไม่ดำเนินการต่อ) ผลตอบแทน;
iHearGeoff

257

นี่คือคำที่เป็นทางการจากข้อกำหนดภาษา Java

14.20.2 การดำเนินการในที่สุดลองและลองจับในที่สุด

tryคำสั่งกับfinallyบล็อกจะถูกดำเนินการโดยการดำเนินการครั้งแรกtryบล็อก จากนั้นมีทางเลือกคือ:

  • หากการดำเนินการtryบล็อกเสร็จสมบูรณ์ตามปกติ [... ]
  • หากการดำเนินการของtryบล็อกเสร็จสมบูรณ์ในทันทีเนื่องจากthrowค่าV , [... ]
  • หากการดำเนินการของtryบล็อกเสร็จสิ้นลงอย่างกะทันหันด้วยเหตุผลอื่น ๆRแล้วfinallyบล็อกจะถูกดำเนินการ จากนั้นมีทางเลือกคือ:
    • หากบล็อกสุดท้ายเสร็จสมบูรณ์ตามปกติแล้วtryคำสั่งเสร็จสิ้นลงอย่างกะทันหันด้วยเหตุผลR
    • หากfinallyบล็อกเสร็จสมบูรณ์อย่างกะทันหันด้วยเหตุผลStryคำสั่งนั้นจะเสร็จสมบูรณ์ทันทีสำหรับเหตุผลS ( และเหตุผลRถูกยกเลิก )

ข้อกำหนดสำหรับreturnทำให้จริงชัดเจนนี้:

JLS 14.17 คำชี้แจงการคืนสินค้า

ReturnStatement:
     return Expression(opt) ;

returnคำสั่งโดยไม่มีExpression ความพยายามที่จะถ่ายโอนการควบคุมไปยังทรงของวิธีการหรือสร้างที่มีมัน

returnคำสั่งที่มีExpression ความพยายามในการถ่ายโอนการควบคุมไปยังทรงของวิธีการที่มีมัน; ค่าของการExpressionกลายเป็นค่าของการภาวนาวิธี

คำอธิบายก่อนหน้านี้บอกว่า " ความพยายามที่จะควบคุมการถ่ายโอน " มากกว่าเพียงแค่ " การถ่ายโอนการควบคุม " เพราะถ้ามีtryงบภายในวิธีการหรือคอนสตรัคซึ่งtryบล็อกมีreturnคำสั่งแล้วใด ๆfinallyข้อของผู้ที่tryงบจะถูกดำเนินการในการสั่งซื้อด้านในสุดจะนอกสุด ก่อนที่การควบคุมจะถูกถ่ายโอนไปยังผู้เรียกใช้ของเมธอดหรือตัวสร้าง การfinallyขัดจังหวะโดยสมบูรณ์ของประโยคสามารถขัดขวางการถ่ายโอนการควบคุมที่เริ่มต้นโดยreturnคำสั่ง


163

นอกเหนือจากการตอบกลับอื่น ๆ สิ่งสำคัญคือต้องชี้ให้เห็นว่า 'ในที่สุด' มีสิทธิ์แทนที่ข้อยกเว้น / ค่าส่งคืนโดยบล็อก try..catch ตัวอย่างเช่นรหัสต่อไปนี้จะคืนค่า 12:

public static int getMonthsInYear() {
    try {
        return 10;
    }
    finally {
        return 12;
    }
}

ในทำนองเดียวกันวิธีการต่อไปนี้ไม่ส่งข้อยกเว้น:

public static int getMonthsInYear() {
    try {
        throw new RuntimeException();
    }
    finally {
        return 12;
    }
}

ในขณะที่วิธีการต่อไปนี้จะโยนมัน:

public static int getMonthsInYear() {
    try {
        return 12;          
    }
    finally {
        throw new RuntimeException();
    }
}

63
ควรสังเกตว่ากรณีตรงกลางเป็นเหตุผลที่แม่นยำว่าทำไมการมีคำสั่งส่งคืนภายในบล็อกในที่สุดก็น่ากลัวอย่างยิ่ง (มันสามารถซ่อน Throwable ใด ๆ )
Dimitris Andreou

2
ใครไม่ต้องการถูกบีบอัดOutOfMemoryError? ;)
RecursiveExceptionException

ฉันทดสอบแล้วและจะระงับข้อผิดพลาดดังกล่าว (yipes!) มันยังสร้างคำเตือนเมื่อฉันรวบรวมมัน (yay!) และคุณสามารถหลีกเลี่ยงได้โดยการกำหนดตัวแปรผลตอบแทนแล้วใช้return retVal หลังจากfinallyบล็อก แต่ที่แน่นอนอนุมานว่าคุณระงับข้อยกเว้นอื่น ๆ บางเพราะรหัสจะไม่ทำให้รู้สึกเป็นอย่างอื่น
Maarten Bodewes

120

ฉันลองตัวอย่างข้างต้นด้วยการแก้ไขเล็กน้อย -

public static void main(final String[] args) {
    System.out.println(test());
}

public static int test() {
    int i = 0;
    try {
        i = 2;
        return i;
    } finally {
        i = 12;
        System.out.println("finally trumps return.");
    }
}

เอาท์พุทรหัสข้างต้น:

ในที่สุดปั้นกลับมา
2

นี่เป็นเพราะเมื่อreturn i;มีการดำเนินการiมีค่า 2 หลังจากนี้finallyบล็อกจะถูกดำเนินการที่ 12 ได้รับมอบหมายให้iและจากนั้นSystem.outจะถูกดำเนินการออก

หลังจากดำเนินการfinallyบล็อกtryบล็อกส่งคืน 2 แทนที่จะส่งคืน 12 เนื่องจากคำสั่งส่งคืนนี้จะไม่ถูกดำเนินการอีกครั้ง

หากคุณจะแก้ปัญหารหัสนี้ใน Eclipse แล้วคุณจะได้รับความรู้สึกว่าหลังจากดำเนินการSystem.outของfinallyบล็อกreturnคำสั่งของtryบล็อกจะถูกดำเนินการอีกครั้ง แต่นี่ไม่ใช่กรณี มันแค่คืนค่า 2


10
ตัวอย่างนี้ยอดเยี่ยมเพิ่มบางสิ่งที่ไม่ได้กล่าวถึงในหลายสิบกระทู้ที่เกี่ยวข้องในที่สุด ฉันคิดว่านักพัฒนาซอฟต์แวร์แทบจะไม่รู้อะไรเลย
หวังว่าจะช่วยได้

4
จะเกิดอะไรขึ้นถ้าiไม่ใช่วัตถุดึกดำบรรพ์ แต่เป็นวัตถุจำนวนเต็ม
Yamcha

ฉันมีปัญหาในการทำความเข้าใจกรณีนี้ docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.17กล่าวว่า "คำสั่งส่งคืนที่มีนิพจน์พยายามโอนการควบคุมไปยังผู้เรียกใช้ของเมธอดหรือแลมบ์ดาที่ประกอบด้วย มัน .... หากการประเมินผลของนิพจน์เสร็จสมบูรณ์ตามปกติการสร้างค่า V .. "สิ่งที่ฉันสามารถเดาได้จากคำสั่งนี้คือ - ดูเหมือนว่าผลตอบแทนที่ไม่ได้ประเมินการแสดงออกอีกครั้งเมื่อมันประเมินค่า V นั่นคือเหตุผลที่ฉันเปลี่ยน ไม่ส่งผลกระทบต่อค่าที่ส่งคืนแก้ไขให้ฉัน
meexplorer

แต่ฉันไม่พบหลักฐานใด ๆ เกี่ยวกับเรื่องนี้ที่กล่าวถึงว่าการกลับมาไม่ได้ประเมินการแสดงออกอีกครั้ง
meexplorer

1
@meexplorer ช้าไปหน่อย แต่มีการอธิบายในJLS 14.20.2 การดำเนินการของลองและในที่สุดลองจับในที่สุด - worded ซับซ้อนเล็กน้อย14.17 ต้องอ่านคำชี้แจงการคืนสินค้า
85421

117

นี่คือรายละเอียดของคำตอบของเควิน สิ่งสำคัญคือต้องรู้ว่านิพจน์ที่จะส่งคืนนั้นถูกประเมินมาก่อนfinallyแม้ว่าจะถูกส่งคืนก็ตาม

public static void main(String[] args) {
    System.out.println(Test.test());
}

public static int printX() {
    System.out.println("X");
    return 0;
}

public static int test() {
    try {
        return printX();
    }
    finally {
        System.out.println("finally trumps return... sort of");
    }
}

เอาท์พุท:

X
finally trumps return... sort of
0

8
สิ่งสำคัญที่ควรรู้
Aminadav Glickshtein

ดีใจที่ได้รู้จักและเข้ากันได้ดี ดูเหมือนว่าที่จริงแล้วการคืนค่าตอบแทนคือสิ่งที่เกิดขึ้นหลังจากfinallyนั้น การคำนวณค่าส่งคืน ( printX()ที่นี่) ยังมาก่อน
อัลเบิร์ต

เป็นตัวอย่างที่ดีด้วยคะแนน "กลับมา" 3 คะแนน!
radistao

Nope รหัสข้างต้นควรแทนที่System.out.println("finally trumps return... sort of");ด้วยSystem.out.print("finally trumps return in try"); return 42;
Pacerier

54

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

ในที่สุดก็ถูกเรียกโดยไม่คำนึงถึงสิ่งที่เกิดขึ้นในช่วงลอง ( ยกเว้นว่าคุณโทรSystem.exit(int)หรือ Java Virtual Machine เริ่มต้นด้วยเหตุผลอื่น)


นั่นคือคำตอบที่อ่อนแอมาก stackoverflow.com/a/65049/715269
Gangnus

42

วิธีคิดอย่างมีเหตุผลเกี่ยวกับสิ่งนี้คือ:

  1. รหัสที่วางในบล็อกในที่สุดจะต้องถูกดำเนินการสิ่งที่เกิดขึ้นภายในบล็อกลอง
  2. ดังนั้นหากรหัสในบล็อกลองพยายามส่งคืนค่าหรือส่งข้อยกเว้นรายการจะถูกวางไว้ 'บนชั้นวาง' จนถึงบล็อกสุดท้ายสามารถดำเนินการได้
  3. เนื่องจากรหัสในบล็อกสุดท้ายมี (ตามคำจำกัดความ) มีลำดับความสำคัญสูงจึงสามารถส่งคืนหรือโยนสิ่งที่ชอบได้ ในกรณีนี้สิ่งที่เหลืออยู่ 'บนหิ้ง' จะถูกยกเลิก
  4. ข้อยกเว้นเพียงอย่างเดียวคือถ้า VM ปิดตัวลงอย่างสมบูรณ์ในช่วงลองบล็อกเช่นโดย 'System.exit'

10
นี่เป็นเพียง "วิธีคิดอย่างมีเหตุผล" หรือเป็นวิธีที่บล็อกสุดท้ายตั้งใจทำงานตามข้อกำหนดหรือไม่ ลิงค์ไปยังแหล่งข้อมูลของซันน่าสนใจมากที่นี่
matias

21

ในที่สุดก็จะถูกดำเนินการเสมอเว้นแต่จะมีการยกเลิกโปรแกรมที่ผิดปกติ (เช่นการเรียก System.exit (0) .. ) ดังนั้น sysout ของคุณจะถูกพิมพ์



18

ในที่สุดก็จะถูกดำเนินการบล็อกเสมอจนกว่าจะมีการยกเลิกโปรแกรมที่ผิดปกติอย่างใดอย่างหนึ่งที่เกิดจากความผิดพลาด JVM System.exit(0)หรือจากการเรียก

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


18

ไม่ไม่ทุกกรณียกเว้นคือ // System.exit (0); ก่อนที่บล็อกสุดท้ายจะป้องกันไม่ให้ถูกดำเนินการในที่สุด

  class A {
    public static void main(String args[]){
        DataInputStream cin = new DataInputStream(System.in);
        try{
            int i=Integer.parseInt(cin.readLine());
        }catch(ArithmeticException e){
        }catch(Exception e){
           System.exit(0);//Program terminates before executing finally block
        }finally{
            System.out.println("Won't be executed");
            System.out.println("No error");
        }
    }
}

และนั่นเป็นหนึ่งในเหตุผลที่คุณไม่ควรเรียกว่า System.exit () ...
Franz D.

13

ในที่สุดก็มักจะเรียกใช้นั่นคือจุดรวมเพียงเพราะมันปรากฏในรหัสหลังจากที่ผลตอบแทนไม่ได้หมายความว่ามันเป็นวิธีการใช้งาน Java runtime มีความรับผิดชอบในการเรียกใช้รหัสนี้เมื่อออกจากtryบล็อก

ตัวอย่างเช่นหากคุณมีสิ่งต่อไปนี้:

int foo() { 
    try {
        return 42;
    }
    finally {
        System.out.println("done");
    }
}

รันไทม์จะสร้างสิ่งนี้:

int foo() {
    int ret = 42;
    System.out.println("done");
    return 42;
}

หากมีข้อยกเว้นที่ไม่ถูกตรวจจับfinallyบล็อกจะทำงานและข้อยกเว้นจะยังคงเผยแพร่ต่อไป


11

นี่เป็นเพราะคุณกำหนดค่าของ i เป็น 12 แต่ไม่ได้ส่งคืนค่าของ i ไปยังฟังก์ชัน รหัสที่ถูกต้องมีดังนี้:

public static int test() {
    int i = 0;
    try {
        return i;
    } finally {
        i = 12;
        System.out.println("finally trumps return.");
        return i;
    }
}


10

โดยสังเขปในเอกสาร Java อย่างเป็นทางการ (คลิกที่นี่ ) มันเขียนว่า -

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


10

คำตอบนั้นง่ายใช่

INPUT:

try{
    int divideByZeroException = 5 / 0;
} catch (Exception e){
    System.out.println("catch");
    return;    // also tried with break; in switch-case, got same output
} finally {
    System.out.println("finally");
}

เอาท์พุท:

catch
finally

1
คำตอบนั้นง่าย ๆ
Christophe Roussy

1
@ChristopheRoussy อย่างไร คุณช่วยอธิบายได้มั้ย
พบ

1
อ่านคำตอบที่ได้รับการยอมรับคำถามดั้งเดิมเกี่ยวกับ 'มันจะดำเนินการเสมอ' และมันจะไม่เสมอไป ในกรณีของคุณมันจะ แต่ไม่ตอบคำถามเดิมและอาจทำให้ผู้เริ่มเข้าใจผิด
Christophe Roussy

ถ้าเช่นนั้นในกรณีใด
พบ

ในทุกกรณีที่กล่าวถึงในคำตอบอื่น ๆ โปรดดูคำตอบที่ยอมรับพร้อมด้วยการโหวตมากกว่า 1,000 ครั้ง
Christophe Roussy

9

ใช่มันจะถูกเรียก นั่นคือจุดรวมของการมีคำหลักในที่สุด หากการกระโดดออกจากบล็อก try / catch สามารถข้ามบล็อกสุดท้ายได้เหมือนกับการวาง System.out.println นอก try / catch


9

ใช่ในที่สุดบล็อกก็จะถูกเรียกใช้งานเสมอ นักพัฒนาส่วนใหญ่ใช้บล็อกนี้ในการปิดการเชื่อมต่อฐานข้อมูลวัตถุ resultset วัตถุคำสั่งและยังใช้ใน java hibernate เพื่อย้อนกลับการทำธุรกรรม


9

ไม่เสมอ

ข้อกำหนด Java Language อธิบายวิธีtry- catch- finallyและtry- catchบล็อกทำงานที่14.20.2
ในสถานที่ที่ไม่มีจะระบุว่าfinallyบล็อกจะถูกดำเนินการเสมอ แต่สำหรับกรณีที่try- catch- finallyและtry- finallyบล็อกเสร็จสมบูรณ์มันไม่ระบุว่าก่อนที่จะเสร็จสิ้นfinallyจะต้องดำเนินการ

try {
  CODE inside the try block
}
finally {
  FIN code inside finally block
}
NEXT code executed after the try-finally block (may be in a different method).

JLS ไม่ได้รับประกันว่าFINจะถูกดำเนินการหลังจากที่รหัส JLS รับประกันว่าถ้ารหัสและต่อไปจะดำเนินการแล้วFINจะถูกดำเนินการหลังจากรหัสและก่อนถัดไป

ทำไม JLS ไม่รับประกันว่าfinallyบล็อกจะถูกดำเนินการหลังจากtryบล็อกเสมอ? เพราะมันเป็นไปไม่ได้ เป็นไปได้ยาก แต่เป็นไปได้ที่ JVM จะถูกยกเลิก (kill, crash, off, off) หลังจากเสร็จสิ้นการtryบล็อก แต่ก่อนที่จะดำเนินการfinallyบล็อก ไม่มีสิ่งใดที่ JLS สามารถทำได้เพื่อหลีกเลี่ยงปัญหานี้

ดังนั้นซอฟต์แวร์ใด ๆ ที่มีพฤติกรรมที่เหมาะสมจะขึ้นอยู่กับfinallyบล็อกที่ถูกเรียกใช้งานเสมอหลังจากtryบล็อกของพวกเขาถูกบั๊ก

returnคำแนะนำในtryบล็อกไม่เกี่ยวข้องกับปัญหานี้ หากการดำเนินการถึงโค้ดหลังจากtry- catch- finallyรับประกันว่าfinallyบล็อกนั้นจะถูกดำเนินการก่อนหน้าโดยมีหรือไม่มีreturnคำแนะนำภายในtryบล็อก


8

ใช่! ฉันจะ. ไม่ว่าจะเกิดอะไรขึ้นในการลองหรือการบล็อกของคุณเว้นแต่ว่า System.exit () ที่เรียกว่าหรือ JVM ขัดข้อง หากมีคำสั่งคืนใด ๆ ในบล็อกในที่สุดก็จะถูกดำเนินการก่อนที่จะกลับคำสั่ง



8

การเพิ่มคำตอบของ @ vibhashเนื่องจากไม่มีคำตอบอื่นใดจะอธิบายสิ่งที่เกิดขึ้นในกรณีของวัตถุที่ไม่แน่นอนเช่นด้านล่าง

public static void main(String[] args) {
    System.out.println(test().toString());
}

public static StringBuffer test() {
    StringBuffer s = new StringBuffer();
    try {
        s.append("sb");
        return s;
    } finally {
        s.append("updated ");
    }
}

จะส่งออก

sbupdated 

ในฐานะของ Java 1.8.162 นี่ไม่ใช่เอาท์พุท
แซม

8

ฉันลองสิ่งนี้มันเป็นเธรดเดี่ยว

public static void main(String args[]) throws Exception {
    Object obj = new Object();
    try {
        synchronized (obj) {
            obj.wait();
            System.out.println("after wait()");
        }
    } catch (Exception ignored) {
    } finally {
        System.out.println("finally");
    }
}

main Threadจะอยู่ในwaitรัฐตลอดไปจึงfinallyจะไม่ถูกเรียกว่า ,

ดังนั้นคอนโซลเอาต์พุตจะไม่print String: หลังจากwait()หรือfinally

เห็นด้วยกับ @Stephen C ตัวอย่างข้างต้นเป็นหนึ่งในกรณีที่ 3 ที่กล่าวถึงที่นี่ :

การเพิ่มความเป็นไปได้แบบไม่ จำกัด เช่นในโค้ดต่อไปนี้:

// import java.util.concurrent.Semaphore;

public static void main(String[] args) {
    try {
        // Thread.sleep(Long.MAX_VALUE);
        // Thread.currentThread().join();
        // new Semaphore(0).acquire();
        // while (true){}
        System.out.println("after sleep join semaphore exit infinite while loop");
    } catch (Exception ignored) {
    } finally {
        System.out.println("finally");
    }
}

กรณีที่ 2: หาก JVM หยุดทำงานก่อน

import sun.misc.Unsafe;
import java.lang.reflect.Field;

public static void main(String args[]) {
    try {
        unsafeMethod();
        //Runtime.getRuntime().halt(123);
        System.out.println("After Jvm Crash!");
    } catch (Exception e) {
    } finally {
        System.out.println("finally");
    }
}

private static void unsafeMethod() throws NoSuchFieldException, IllegalAccessException {
    Field f = Unsafe.class.getDeclaredField("theUnsafe");
    f.setAccessible(true);
    Unsafe unsafe = (Unsafe) f.get(null);
    unsafe.putAddress(0, 0);
}

Ref: คุณทำผิดพลาด JVM ได้อย่างไร

กรณีที่ 6: หากfinallyบล็อกกำลังถูกเรียกใช้งานโดย daemon ThreadและการThreadsออกnon-daemon อื่น ๆ ทั้งหมดก่อนหน้าจะfinallyถูกเรียกใช้

public static void main(String args[]) {
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try {
                printThreads("Daemon Thread printing");
                // just to ensure this thread will live longer than main thread
                Thread.sleep(10000);
            } catch (Exception e) {
            } finally {
                System.out.println("finally");
            }
        }
    };
    Thread daemonThread = new Thread(runnable);
    daemonThread.setDaemon(Boolean.TRUE);
    daemonThread.setName("My Daemon Thread");
    daemonThread.start();
    printThreads("main Thread Printing");
}

private static synchronized void printThreads(String str) {
    System.out.println(str);
    int threadCount = 0;
    Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
    for (Thread t : threadSet) {
        if (t.getThreadGroup() == Thread.currentThread().getThreadGroup()) {
            System.out.println("Thread :" + t + ":" + "state:" + t.getState());
            ++threadCount;
        }
    }
    System.out.println("Thread count started by Main thread:" + threadCount);
    System.out.println("-------------------------------------------------");
}

เอาท์พุท: นี่ไม่ได้พิมพ์ "ที่สุด" ซึ่งหมายถึง "ในที่สุดบล็อก" ใน "daemon ด้าย" ไม่ได้ดำเนินการ

main Thread Printing  
Thread :Thread[My Daemon Thread,5,main]:state:BLOCKED  
Thread :Thread[main,5,main]:state:RUNNABLE  
Thread :Thread[Monitor Ctrl-Break,5,main]:state:RUNNABLE   
Thread count started by Main thread:3  
-------------------------------------------------  
Daemon Thread printing  
Thread :Thread[My Daemon Thread,5,main]:state:RUNNABLE  
Thread :Thread[Monitor Ctrl-Break,5,main]:state:RUNNABLE  
Thread count started by Main thread:2  
-------------------------------------------------  

Process finished with exit code 0

4
ดูคำตอบที่ยอมรับได้ นี่เป็นเพียงกรณีของ "infinite loop"
สตีเฟนซี

8

พิจารณาโปรแกรมต่อไปนี้:

public class SomeTest {

    private static StringBuilder sb = new StringBuilder();

    public static void main(String args[]) {

        System.out.println(someString());
        System.out.println("---AGAIN---");
        System.out.println(someString());
        System.out.println("---PRINT THE RESULT---");
        System.out.println(sb.toString());
    }

    private static String someString() {

        try {
            sb.append("-abc-");
            return sb.toString();

        } finally {
            sb.append("xyz");
        }
    }
}

ตั้งแต่ Java 1.8.162 บล็อกโค้ดด้านบนให้ผลลัพธ์ต่อไปนี้:

-abc-
---AGAIN---
-abc-xyz-abc-
---PRINT THE RESULT---
-abc-xyz-abc-xyz

ซึ่งหมายความว่าการใช้finallyเพื่อเพิ่มวัตถุเป็นแนวปฏิบัติที่ดีเช่นรหัสต่อไปนี้:

private static String someString() {

    StringBuilder sb = new StringBuilder();

    try {
        sb.append("abc");
        return sb.toString();

    } finally {
        sb = null; // Just an example, but you can close streams or DB connections this way.
    }
}

sb.setLength(0)ในที่สุดมันก็ไม่ควรเหรอ?
7294900

sb.setLength (0) จะทำให้ข้อมูลใน StringBuffer ว่างเปล่า ดังนั้น sb = null จะยกเลิกการเชื่อมโยงวัตถุจากการอ้างอิง
Sam

ไม่ควรพิมพ์ "xyz" สองครั้งในเอาต์พุต เนื่องจากฟังก์ชั่นถูกเรียกสองครั้งทำไม "ที่สุด" เป็นเพียงครั้งเดียว
fresko

2
นี่ไม่ใช่วิธีปฏิบัติที่ดี ในที่สุดบล็อกนั้นก็sb = null;เพิ่งเพิ่มรหัสที่ไม่จำเป็น ฉันเข้าใจว่าคุณหมายถึงว่าfinallyบล็อกเป็นสถานที่ที่ดีในการเพิ่มแหล่งข้อมูลฟรีเช่นการเชื่อมต่อฐานข้อมูลหรืออะไรทำนองนั้น แต่โปรดทราบว่าตัวอย่างของคุณอาจสร้างความสับสนให้กับผู้มาใหม่
Roc Boronat

1
@ Samim ขอบคุณฉันเพิ่มบรรทัด System.out.println("---AGAIN2---"); System.out.println(sb);และชัดเจนยิ่งขึ้นในขณะนี้ อย่างที่มันเป็นเอาท์พุทขัดกับวิทยานิพนธ์ของคุณ: p ฉันยังเพิ่มคำตอบของคุณ แต่การแก้ไขจะต้องได้รับการยอมรับจากผู้กลั่นกรองหรือใครบางคนเช่นนั้น
มิฉะนั้น

7

ที่จริงในภาษาใด ๆ ... ในที่สุดก็จะดำเนินการก่อนที่จะกลับคำสั่งไม่ว่าจะกลับมาอยู่ที่ไหนในร่างกายวิธีการ หากไม่เป็นเช่นนั้นในที่สุดการบล็อกก็ไม่มีความหมายมากนัก


7

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


7

finally จะดำเนินการและแน่นอน

finally จะไม่ดำเนินการในกรณีด้านล่าง:

กรณีที่ 1:

System.exit()เมื่อคุณได้รับการดำเนินการ

กรณีที่ 2:

เมื่อ JVM / Thread ของคุณขัดข้อง

กรณีที่ 3:

เมื่อการดำเนินการของคุณจะหยุดในระหว่างด้วยตนเอง


6
  1. ในที่สุด Block จะถูกเรียกใช้งานเสมอ เว้นแต่และจนกว่าจะ มีคำสั่งSystem.exit ()มี (คำสั่งแรกในที่สุดบล็อก)
  2. หากsystem.exit ()เป็นคำสั่งแรกดังนั้นในที่สุดการบล็อกจะไม่ถูกดำเนินการและการควบคุมออกมาจากการบล็อกในที่สุด เมื่อใดก็ตามที่คำสั่ง System.exit () ได้รับในที่สุดบล็อกจนคำสั่งนั้นในที่สุดก็บล็อกการดำเนินการและเมื่อ System.exit () ปรากฏขึ้นแล้วบังคับควบคุมอย่างเต็มที่ออกมาจากบล็อกในที่สุด

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