มีข้อยกเว้นที่ถูกบล็อกการจับภายใน - จะถูกจับอีกครั้งหรือไม่


180

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

try {
  // Do something
} catch(IOException e) {
  throw new ApplicationException("Problem connecting to server");
} catch(Exception e) {
  // Will the ApplicationException be caught here?
}

ฉันคิดเสมอว่าคำตอบจะไม่ แต่ตอนนี้ฉันมีพฤติกรรมแปลก ๆ ที่อาจเกิดจากสิ่งนี้ คำตอบน่าจะเหมือนกันสำหรับภาษาส่วนใหญ่ แต่ฉันทำงานใน Java


2
บางทีคุณสามารถอธิบาย "พฤติกรรมแปลก ๆ " ได้ไหม?
Jeffrey L Whitledge

คุณแน่ใจหรือไม่ว่า ApplicationException ไม่ถูกส่งไปที่อื่นและแพร่กระจายไปยังบล็อกนี้
sblundy

ฉันสังเกตเห็นสิ่งนี้ใน Eclipse IDE ของฉัน มีความพยายามที่จะบังคับให้ฉันใส่ "ข้อยกเว้นใหม่" ในบล็อกลอง แต่ฉันไม่รู้ว่าทำไม ฉันเคยทำมาแล้วในอดีตโดยไม่ทำอย่างนั้น ฉันไม่เห็นสาเหตุที่ต้องมีการลองบล็อก ตัวอย่างจำนวนมากใน Google แสดงให้คนเห็นว่าไม่ต้องการบล็อกลอง เป็นเพราะฉันกำลังขว้างอยู่ข้างในคำสั่ง if?
djangofan

คำตอบ:


214

ไม่เนื่องจากใหม่throwไม่ได้อยู่ในtryบล็อกโดยตรง


ให้บอกว่ารหัสเป็นเช่นนี้ลอง {} catch (Exception e) {System.err.println ("In catch Exception:" + e.getClass ()); } catch (IOException e) {System.err.println ("กำลังจับ IOException:" + e.getClass ()); } และรหัสในบล็อกลองสร้าง IO Exception มันจะไปยังบล็อก Exception ทั่วไปทันทีหรือมันจะบินไปยังบล็อก catch ของ IOException หรือไม่
sofs1

4
@ user3705478 รหัสจะไม่รวบรวมเพื่อหลีกเลี่ยงสถานการณ์ที่ผิดพลาด โดยทั่วไปคุณไม่ได้รับอนุญาตให้มีการจับสำหรับคลาสย่อยหลังจากที่จับซูเปอร์คลาสในบล็อก try เดียวกัน
Chris Jester-Young

ขอบคุณ ดังนั้นฉันจะได้รับข้อผิดพลาดในการรวบรวมเวลาใช่มั้ย ฉันจะทดสอบเมื่อกลับถึงบ้าน
sofs1

นั่นขึ้นอยู่กับ @ user3705478 ถ้า a RuntimeExceptionถูกโยนจากcatchบล็อกจะไม่มีข้อผิดพลาดในการรวบรวม
แรนดี้เดฟ

@AndrewDunn ผมไม่คิดว่านั่นคือสิ่งที่ user3705478 ของคำถามเป็นเรื่องเกี่ยวกับ แต่สิ่งที่เกิดขึ้นหากมีการยกเว้นผู้ปกครองcatchประโยคเป็น บริษัท จดทะเบียนก่อนยกเว้นเด็กcatchประโยค ความเข้าใจของฉันคือ Java ไม่ยอมรับสิ่งนี้และมันก็เป็นช่วงเวลารวบรวม
Chris Jester-Young

71

ไม่ง่ายในการตรวจสอบ

public class Catch {
    public static void main(String[] args) {
        try {
            throw new java.io.IOException();
        } catch (java.io.IOException exc) {
            System.err.println("In catch IOException: "+exc.getClass());
            throw new RuntimeException();
        } catch (Exception exc) {
            System.err.println("In catch Exception: "+exc.getClass());
        } finally {
            System.err.println("In finally");
        }
    }
}

ควรพิมพ์:

กำลังเข้า IOException: class java.io.IOException
ในที่สุด
ข้อยกเว้นในเธรด "main" java.lang.RuntimeException
        ที่ Catch.main (Catch.java:8)

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

โปรดทราบด้วยว่าถ้าคุณสลับไปมารอบสอง catch catch มันจะไม่คอมไพล์ จับที่สองจะไม่สามารถเข้าถึงได้อย่างสมบูรณ์

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


3
วิธีที่ชัดเจนที่สุดที่จะหลีกเลี่ยงการเป็นของหลักสูตรเพื่อโทรfinally System.exit:-P
Chris Jester-Young

3
@Chris Jester-Young for(;;);นั้นสั้นกว่าซึ่งบรรจุอยู่ในภาษานั้นไม่ค่อยมีผลข้างเคียงมากนักและสำหรับฉันก็เห็นได้ชัดกว่า
Tom Hawtin - tackline

1
System.exitเป็นมิตรกับซีพียูมากขึ้น! : -O แต่ใช่ตกลงชัดเจนว่านี่เป็นเกณฑ์ที่เป็นอัตวิสัย นอกจากนี้ฉันไม่ทราบว่าคุณเป็นนักกอล์ฟรหัส ;-)
Chris Jester-Young

28

ข้อกำหนดภาษา Java กล่าวไว้ในส่วน 14.19.1:

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

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

การอ้างอิง: http://java.sun.com/docs/books/jls/second_edition/html/statements.doc.html#24134

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

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


แค่อยากจะเพิ่มมันตั้งแต่ Java 7 คุณอาจหลีกเลี่ยงการใช้try-with-resources แล้วถ้าtryและfinallyทั้งโยนfinallyถูกระงับ tryแต่ยังจะมีการเพิ่มการยกเว้นจาก หากcatchโยนด้วยคุณไม่มีโชคเว้นแต่คุณจัดการตัวเองผ่าน 'addSuppressed' และเพิ่มtryข้อยกเว้น - จากนั้นคุณมีทั้งสามข้อ
LAFK พูดว่า Reinstate Monica

6

ถ้าคุณต้องการที่จะโยนข้อยกเว้นจาก catch block คุณต้องแจ้งวิธี / class / etc ของคุณ ต้องโยนข้อยกเว้นดังกล่าว ชอบมาก

public void doStuff() throws MyException {
    try {
        //Stuff
    } catch(StuffException e) {
        throw new MyException();
    }
}

และตอนนี้คอมไพเลอร์ของคุณจะไม่ตะโกนใส่คุณ :)



2

ดังที่ได้กล่าวข้างต้น ...
ฉันจะเพิ่มว่าถ้าคุณมีปัญหาในการดูว่าเกิดอะไรขึ้นถ้าคุณไม่สามารถทำให้เกิดปัญหาในการดีบักคุณสามารถเพิ่มการติดตามก่อนที่จะโยนข้อยกเว้นใหม่ (ด้วยระบบเก่าที่ดี) .out.println แย่ลงด้วยระบบบันทึกที่ดีเช่น log4j มิฉะนั้น)


2

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

try {
    doSomething();
} catch (IOException) {
   try {
       doSomething();
   } catch (IOException e) {
       throw new ApplicationException("Failed twice at doSomething" +
       e.toString());
   }          
} catch (Exception e) {
}

ฉันเขียนรหัสที่คล้ายกัน แต่ฉันไม่มั่นใจ ฉันต้องการเรียก Thread.sleep () ใน catch block ของฉัน แต่ Thread.sleep จะขัดจังหวะการขัดจังหวะ ถูกต้องหรือไม่ (เป็นการปฏิบัติที่ดีที่สุด) ที่จะทำเช่นเดียวกับที่คุณแสดงในตัวอย่างของคุณ?
riroo

1

ไม่เนื่องจากการจับทั้งหมดอ้างถึงบล็อกลองเดียวกันดังนั้นการส่งจากภายในบล็อกจับจะถูกจับโดยบล็อกลองปิดล้อม (อาจเป็นวิธีที่เรียกว่าบล็อกนี้)


-4

โพสต์เก่า แต่ตัวแปร "e" จะต้องไม่ซ้ำกัน:

try {
  // Do something
} catch(IOException ioE) {
  throw new ApplicationException("Problem connecting to server");
} catch(Exception e) {
  // Will the ApplicationException be caught here?
}

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