กรณีต่อต้านการตรวจสอบข้อยกเว้น


456

เป็นเวลาหลายปีแล้วที่ฉันไม่สามารถรับคำตอบที่เหมาะสมสำหรับคำถามต่อไปนี้: เหตุใดนักพัฒนาซอฟต์แวร์บางคนจึงต่อต้านข้อยกเว้นที่ตรวจสอบ ฉันมีการสนทนามากมายอ่านสิ่งต่าง ๆ บนบล็อกอ่านสิ่งที่ Bruce Eckel ต้องพูด (คนแรกที่ฉันเห็นพูดต่อต้านพวกเขา)

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

ทุกบทสนทนาที่ฉันจบลงด้วยคำถามเดียวกันที่ยังไม่ได้รับคำตอบขอให้ฉันตั้งค่า:

โดยทั่วไป (จากวิธีการออกแบบ Java)

  • Error สำหรับสิ่งที่ไม่ควรถูกจับ (VM มีอาการแพ้ถั่วลิสงและมีคนทิ้งขวดถั่วลิสงลงไป)
  • RuntimeException สำหรับสิ่งที่โปรแกรมเมอร์ทำผิด (โปรแกรมเมอร์เดินจากจุดสิ้นสุดของอาร์เรย์)
  • Exception(ยกเว้นRuntimeException) สำหรับสิ่งที่อยู่นอกเหนือการควบคุมของโปรแกรมเมอร์ (ดิสก์เต็มในขณะที่เขียนไปยังระบบไฟล์มีการ จำกัด การจัดการไฟล์สำหรับกระบวนการแล้วและคุณไม่สามารถเปิดไฟล์ได้อีก)
  • Throwable เป็นเพียงพาเรนต์ของชนิดข้อยกเว้นทั้งหมด

อาร์กิวเมนต์ทั่วไปที่ฉันได้ยินคือถ้ามีข้อยกเว้นเกิดขึ้นจากนั้นนักพัฒนาทั้งหมดจะต้องทำคือออกจากโปรแกรม

อาร์กิวเมนต์ที่พบบ่อยอีกข้อที่ฉันได้ยินคือการยกเว้นการตรวจสอบทำให้ยากต่อการกำหนดรหัสซ้ำ

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

สำหรับกลุ่ม "ทำให้ยากที่จะสร้างใหม่" ซึ่งบ่งชี้ว่าไม่ได้เลือกระดับที่เหมาะสมของสิ่งที่เป็นนามธรรม แทนที่จะประกาศวิธีการพ่นIOExceptionที่IOExceptionควรจะเปลี่ยนเป็นข้อยกเว้นที่เหมาะสมมากขึ้นสำหรับสิ่งที่เกิดขึ้นใน

ฉันไม่มีปัญหาในการตัด Main ด้วยcatch(Exception)(หรือในบางกรณีcatch(Throwable)เพื่อให้แน่ใจว่าโปรแกรมสามารถออกจากระบบได้อย่างงดงาม - แต่ฉันมักจะจับข้อยกเว้นเฉพาะที่จำเป็นต้องใช้การทำเช่นนั้นทำให้ฉันสามารถแสดงที่เหมาะสมอย่างน้อยที่สุด ข้อความผิดพลาด.

คำถามที่ผู้คนไม่เคยตอบคือ:

หากคุณโยนRuntimeException คลาสย่อยแทนที่จะเป็นException คลาสย่อยคุณจะรู้ได้อย่างไรว่าคุณควรจับอะไร

หากจับคำตอบExceptionแล้วคุณจะจัดการกับข้อผิดพลาดของโปรแกรมเมอร์เช่นเดียวกับข้อยกเว้นของระบบ ดูเหมือนว่าฉันผิด

หากคุณพบThrowableว่าคุณกำลังปฏิบัติต่อข้อยกเว้นของระบบและข้อผิดพลาดของ VM (และสิ่งที่คล้ายกัน) ในลักษณะเดียวกัน ดูเหมือนว่าฉันผิด

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

ฉันจะบอกว่าโปรแกรมที่แสดง stack trace นั้นผิด คนที่ไม่ชอบข้อยกเว้นที่ตรวจสอบแล้วไม่รู้สึกเช่นนั้นหรือไม่?

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

แก้ไข: ผมไม่ได้มองหาคำแนะนำเกี่ยวกับเมื่อมีการใช้รูปแบบทั้งสิ่งที่ฉันกำลังมองหาคือเหตุผลที่คนเพิ่มขึ้นจากRuntimeExceptionเพราะพวกเขาไม่ชอบยื่นออกมาจากExceptionและ / หรือเหตุผลที่พวกเขาจับข้อยกเว้นแล้ว rethrow RuntimeExceptionมากกว่าเพิ่มโยน วิธีการของพวกเขา ฉันต้องการที่จะเข้าใจแรงจูงใจในการยกเว้นข้อผิดพลาดที่ตรวจสอบ


46
ฉันไม่คิดว่ามันเป็นอัตนัยอย่างสมบูรณ์ - มันเป็นคุณสมบัติภาษาที่ได้รับการออกแบบให้มีการใช้งานที่เฉพาะเจาะจงมากกว่าที่จะให้ทุกคนตัดสินใจได้ว่ามันมีไว้สำหรับตัวเอง และมันไม่ได้เป็นที่ถกเถียงกันโดยเฉพาะมันกล่าวถึงการโต้แย้งเฉพาะล่วงหน้าซึ่งผู้คนสามารถเกิดขึ้นได้อย่างง่ายดาย
Gareth

6
มาเลย ถูกมองว่าเป็นคุณสมบัติทางภาษาหัวข้อนี้ได้รับและสามารถเข้าหาในทางที่เป็นวัตถุประสงค์
Kurt Schelfthout

6
@cletus "ตอบคำถามของคุณเอง" ถ้าฉันมีคำตอบฉันจะไม่ถามคำถาม!
TofuBeer

8
เป็นคำถามที่ดีมาก ใน C ++ ไม่มีการตรวจสอบข้อยกเว้นเลยและในความคิดของฉันมันทำให้คุณสมบัติการยกเว้นใช้ไม่ได้ คุณจะต้องตกอยู่ในสถานการณ์ที่ต้องจับการเรียกใช้ทุก ๆ ฟังก์ชั่นเพราะคุณไม่รู้ว่ามันจะส่งผลอะไรหรือไม่
Dimitri C.

4
ข้อโต้แย้งที่แข็งแกร่งที่สุดที่ฉันรู้สำหรับข้อยกเว้นที่ตรวจสอบคือพวกเขาไม่ได้มีอยู่ใน Java และเมื่อพวกเขาได้รับการแนะนำพวกเขาค้นพบข้อบกพร่องหลายร้อยรายการใน JDK นี่ค่อนข้างก่อน Java 1.0 โดยส่วนตัวแล้วฉันจะไม่อยู่โดยไม่มีพวกเขาและไม่เห็นด้วยอย่างรุนแรงกับ Bruce Eckel และคนอื่น ๆ ในเรื่องนี้
มาร์ควิสแห่ง Lorne

คำตอบ:


277

ฉันคิดว่าฉันอ่านบทสัมภาษณ์บรูซเอคเคลแบบเดียวกับที่คุณทำและมันก็บั่นทอนฉันอยู่เสมอ ในความเป็นจริงการโต้แย้งเกิดขึ้นโดยผู้ให้สัมภาษณ์ (ถ้านี่เป็นเรื่องจริงที่คุณพูดถึง) Anders Hejlsberg อัจฉริยะ MS ด้านหลัง. NET และ C #

http://www.artima.com/intv/handcuffs.html

Fan แม้ว่าฉันจะเป็น Hejlsberg และผลงานของเขาการโต้เถียงนี้ทำให้ฉันเป็นคนหลอกลวง โดยทั่วไปแล้วจะเดือดลงไปที่:

"การตรวจสอบข้อยกเว้นนั้นไม่ดีนักเพราะโปรแกรมเมอร์มักทำผิดกฎพวกเขาโดยการจับพวกเขาและไล่ออกสิ่งเหล่านี้ซึ่งนำไปสู่ปัญหาที่ถูกซ่อนอยู่และถูกเพิกเฉย

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

สรุปบทสรุปของการโต้แย้งคือการที่"โปรแกรมเมอร์จะไม่ใช้พวกเขาอย่างถูกต้องและไม่ได้ใช้อย่างถูกต้องจะเลวร้ายยิ่งกว่าไม่ได้มีพวกเขา"

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

แต่ในท้ายที่สุดฉันพบว่ามันเป็นการโต้เถียงที่ผิด ๆ ของ Hejlsberg และอาจเป็นโพสต์เฉพาะกิจที่สร้างขึ้นเพื่ออธิบายการขาดมากกว่าการตัดสินใจที่ดี

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

ตอนนี้โปรแกรมเมอร์ API ต้องระวังอย่าโยนข้อยกเว้นที่ถูกตรวจสอบไปทั่วสถานที่หรือพวกเขาจะรบกวนโปรแกรมเมอร์ของไคลเอ็นต์ โปรแกรมเมอร์ไคลเอนต์ที่ขี้เกียจจะหันมาจับ(Exception) {}เหมือน Hejlsberg เตือนและผลประโยชน์ทั้งหมดจะหายไปและนรกจะตามมา แต่ในบางกรณีไม่มีข้อยกเว้นสำหรับการตรวจสอบที่ดี

สำหรับฉันตัวอย่างคลาสสิกคือ API แบบเปิดไฟล์ ทุกภาษาโปรแกรมในประวัติศาสตร์ของภาษา (ในระบบไฟล์อย่างน้อย) มี API ที่ช่วยให้คุณเปิดไฟล์ และโปรแกรมเมอร์ลูกค้าทุกคนที่ใช้ API นี้รู้ว่าพวกเขาต้องจัดการกับกรณีที่ไฟล์ที่พวกเขาพยายามเปิดไม่มีอยู่ ให้ฉันใช้ถ้อยคำใหม่ที่: โปรแกรมเมอร์ลูกค้าทุกคนที่ใช้ API นี้ควรรู้ว่าพวกเขาต้องจัดการกับกรณีนี้ และมี rub: โปรแกรมเมอร์ API สามารถช่วยให้พวกเขารู้ว่าพวกเขาควรจัดการกับมันผ่านการแสดงความคิดเห็นเพียงอย่างเดียวหรือพวกเขาสามารถยืนยันลูกค้าจัดการกับมันได้

ใน C สำนวนเป็นไปอย่างนั้น

  if (f = fopen("goodluckfindingthisfile")) { ... } 
  else { // file not found ...

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

   f = fopen("goodluckfindingthisfile");
   f.read(); // BANG! 

และเรียนรู้วิธีที่ยากลำบาก

โปรดทราบว่าเรากำลังพูดถึงภาษาที่พิมพ์อย่างยิ่งเท่านั้นที่นี่: มีความคิดที่ชัดเจนว่า API นั้นเป็นภาษาที่มีการพิมพ์อย่างรุนแรง: มันเป็นฟังก์ชั่น Smorgasbord (วิธีการ) เพื่อให้คุณใช้กับโปรโตคอลที่กำหนดไว้อย่างชัดเจนสำหรับแต่ละคน

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

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

จุดที่ฉันพยายามทำคือ: ในภาษาที่พิมพ์แบบคงที่โปรแกรมเมอร์ API สนับสนุนให้ลูกค้าใช้ API อย่างถูกต้องโดยป้องกันรหัสลูกค้าของพวกเขาจากการรวบรวมถ้ามันทำผิดพลาดใด ๆ ที่เห็นได้ชัด

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

ดังนั้นข้อยกเว้นที่ตรวจสอบแล้วมีอะไรบ้าง

นี่คือหนึ่งใน API ของ Java ที่คุณสามารถใช้เปิดไฟล์ได้

try {
  f = new FileInputStream("goodluckfindingthisfile");
}
catch (FileNotFoundException e) {
  // deal with it. No really, deal with it!
  ... // this is me dealing with it
}

เห็นไหม นี่คือลายเซ็นต์สำหรับวิธี API นั้น:

public FileInputStream(String name)
                throws FileNotFoundException

โปรดทราบว่าFileNotFoundExceptionเป็นข้อยกเว้นที่ตรวจสอบ

โปรแกรมเมอร์ API กำลังบอกสิ่งนี้กับคุณ: "คุณสามารถใช้ตัวสร้างนี้เพื่อสร้าง FileInputStream ใหม่ แต่คุณ

a) ต้องส่งชื่อไฟล์เป็นสตริง
b) ต้องยอมรับความเป็นไปได้ที่ไฟล์อาจไม่พบในขณะใช้งาน "

และนั่นคือประเด็นทั้งหมดที่ฉันกังวล

ที่สำคัญคือสิ่งที่คำถามระบุว่า "สิ่งที่อยู่นอกเหนือการควบคุมของโปรแกรมเมอร์" ความคิดแรกของฉันคือเขา / เธอหมายถึงสิ่งที่อยู่นอกเหนือการควบคุมโปรแกรมเมอร์API แต่ในความเป็นจริงการตรวจสอบข้อยกเว้นเมื่อใช้อย่างถูกต้องควรเป็นสิ่งที่อยู่นอกการควบคุมของโปรแกรมเมอร์ของไคลเอ็นต์และการเขียนโปรแกรมของ API ฉันคิดว่านี่เป็นกุญแจสำคัญที่จะไม่ละเมิดข้อยกเว้นที่ตรวจสอบ

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

ดังนั้น API ทำให้ชัดเจน: จะมีบางกรณีที่ไฟล์นี้ไม่มีอยู่ในเวลาที่คุณโทรหาฉันและคุณก็จัดการกับมันได้ดีกว่า

นี่จะชัดเจนยิ่งขึ้นเมื่อมีกรณีพิพาท ลองนึกภาพฉันเขียนตาราง API ฉันมีรูปแบบตารางที่ไหนสักแห่งที่มี API รวมถึงวิธีนี้:

public RowData getRowData(int row) 

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

public RowData getRowData(int row) throws CheckedInvalidRowNumberException

(ฉันจะไม่เรียกมันว่า "ตรวจสอบ" แน่นอน)

นี่คือการใช้งานข้อยกเว้นที่ตรวจสอบไม่ถูกต้อง รหัสลูกค้าจะเต็มไปด้วยการโทรเพื่อดึงข้อมูลแถวทุกอันที่จะต้องใช้ลอง / จับและอะไร? พวกเขาจะรายงานกับผู้ใช้ว่าหาแถวผิดหรือไม่ อาจไม่ - เพราะสิ่งที่ UI ล้อมรอบมุมมองตารางของฉันคือมันไม่ควรให้ผู้ใช้เข้าสู่สถานะที่มีการร้องขอแถวที่ผิดกฎหมาย ดังนั้นจึงเป็นข้อบกพร่องในส่วนของโปรแกรมเมอร์ลูกค้า

โปรแกรมเมอร์ API IllegalArgumentExceptionยังคงสามารถคาดการณ์ได้ว่าลูกค้าจะได้รหัสข้อผิดพลาดดังกล่าวและควรจะจัดการกับมันด้วยข้อยกเว้นรันไทม์เช่น

มีข้อยกเว้นในการตรวจสอบgetRowDataนี้เป็นกรณีที่ชัดเจนที่จะนำไปสู่โปรแกรมเมอร์ขี้เกียจของ Hejlsberg เพียงแค่เพิ่มการจับว่างเปล่า เมื่อสิ่งนั้นเกิดขึ้นค่าแถวที่ผิดกฎหมายจะไม่ปรากฏชัดเจนแม้แต่กับผู้ทดสอบหรือการดีบักของนักพัฒนาไคลเอ็นต์ แต่จะนำไปสู่ข้อผิดพลาดแบบเคาะที่ยากที่จะระบุแหล่งที่มาของ จรวด Arianne จะระเบิดขึ้นหลังจากเปิดตัว

ตกลงนี่คือปัญหา: ฉันกำลังบอกว่าข้อยกเว้นที่ตรวจสอบFileNotFoundExceptionไม่ได้เป็นเพียงสิ่งที่ดี แต่เป็นเครื่องมือสำคัญในกล่องเครื่องมือโปรแกรมเมอร์ API สำหรับการกำหนด API ในวิธีที่มีประโยชน์ที่สุดสำหรับลูกค้าโปรแกรมเมอร์ แต่CheckedInvalidRowNumberExceptionสิ่งที่ไม่สะดวกคือการเขียนโปรแกรมที่ไม่ดีและควรหลีกเลี่ยง แต่จะบอกความแตกต่างได้อย่างไร

ฉันเดาว่ามันไม่ได้เป็นวิทยาศาสตร์ที่แน่นอนและฉันเดาว่าสิ่งนั้นเป็นพื้นฐานและอาจพิสูจน์ให้เห็นถึงข้อโต้แย้งของ Hejlsberg แต่ฉันไม่มีความสุขที่ได้โยนลูกออกมาพร้อมกับอ่างอาบน้ำที่นี่ดังนั้นให้ฉันดึงกฎบางอย่างที่นี่เพื่อแยกข้อยกเว้นที่ได้รับการตรวจสอบอย่างดีจากความไม่ดี:

  1. อยู่นอกการควบคุมของลูกค้าหรือปิด vs เปิด:

    ควรใช้ข้อยกเว้นที่ตรวจสอบแล้วเฉพาะกรณีที่เกิดข้อผิดพลาดที่ไม่สามารถควบคุมได้ทั้ง API และโปรแกรมเมอร์ของไคลเอ็นต์ สิ่งนี้เกี่ยวข้องกับการเปิดหรือปิดระบบ ในUI ที่มีข้อ จำกัดซึ่งโปรแกรมเมอร์ของไคลเอ็นต์มีการควบคุมพูดถึงปุ่มคำสั่งแป้นพิมพ์ ฯลฯ ทั้งหมดที่เพิ่มและลบแถวออกจากมุมมองตาราง (ระบบปิด) มันเป็นข้อผิดพลาดในการเขียนโปรแกรมไคลเอนต์หากพยายามดึงข้อมูลจาก แถวที่ไม่มีอยู่ ในระบบปฏิบัติการแบบใช้ไฟล์ที่มีจำนวนผู้ใช้ / แอปพลิเคชันใด ๆ สามารถเพิ่มและลบไฟล์ (ระบบเปิด) มันเป็นไปได้ว่าไฟล์ที่ลูกค้าร้องขอได้ถูกลบไปแล้วโดยไม่ต้องมีความรู้ดังนั้นพวกเขาควรคาดหวังว่าจะจัดการกับมัน .

  2. แพร่หลาย:

    การตรวจสอบข้อยกเว้นไม่ควรใช้กับการเรียก API ที่ไคลเอนต์ใช้บ่อย บ่อยครั้งที่ฉันหมายถึงสถานที่จำนวนมากในรหัสลูกค้า - ไม่บ่อยครั้ง ดังนั้นรหัสไคลเอนต์จึงไม่น่าจะพยายามเปิดไฟล์เดียวกันมากนัก แต่มุมมองตารางของฉันได้รับRowDataทุกที่จากวิธีการที่แตกต่างกัน โดยเฉพาะฉันจะเขียนโค้ดเป็นจำนวนมากเช่น

    if (model.getRowData().getCell(0).isEmpty())

    และมันจะเจ็บปวดที่ต้องห่อ / ลองทุกครั้ง

  3. แจ้งผู้ใช้:

    ควรใช้ข้อยกเว้นที่ผ่านการตรวจสอบในกรณีที่คุณสามารถจินตนาการถึงข้อความแสดงข้อผิดพลาดที่เป็นประโยชน์ซึ่งจะปรากฏต่อผู้ใช้ปลายทาง นี่คือ"และคุณจะทำอย่างไรเมื่อมันเกิดขึ้น?" คำถามที่ฉันยกขึ้นข้างบน นอกจากนี้ยังเกี่ยวข้องกับรายการ 1 เนื่องจากคุณสามารถคาดการณ์ได้ว่ามีบางสิ่งที่อยู่นอกระบบไคลเอนต์ API ของคุณอาจทำให้ไฟล์ไม่อยู่ที่นั่นคุณสามารถบอกผู้ใช้ได้อย่างสมเหตุสมผล:

    "Error: could not find the file 'goodluckfindingthisfile'"

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

    "Internal error occured: IllegalArgumentException in ...."

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

นั่นคือกฎของฉัน ค่อนข้างจะถูกวางแผนและจะมีข้อยกเว้นอย่างไม่ต้องสงสัย (โปรดช่วยฉันปรับแต่งถ้าคุณต้องการ) แต่เหตุผลหลักของฉันคือมีบางกรณีFileNotFoundExceptionที่ข้อยกเว้นที่ตรวจสอบนั้นมีความสำคัญและมีประโยชน์ส่วนหนึ่งของสัญญา API เป็นประเภทพารามิเตอร์ ดังนั้นเราไม่ควรแจกจ่ายมันเพียงเพราะมันถูกใช้ในทางที่ผิด

ขออภัยไม่ได้ตั้งใจที่จะทำสิ่งนี้ให้นานและใจร้อน ให้ฉันทำตามคำแนะนำสองข้อ:

ตอบ: โปรแกรมเมอร์ API: ใช้ข้อยกเว้นที่ตรวจสอบอย่าง จำกัด เพื่อรักษาประโยชน์ของมันไว้ เมื่อมีข้อสงสัยให้ใช้ข้อยกเว้นที่ไม่ได้ตรวจสอบ

B: โปรแกรมเมอร์ของไคลเอ็นต์: ติดนิสัยในการสร้างข้อยกเว้น (google it) ในการพัฒนาของคุณ JDK 1.4 และใหม่กว่านั้นให้ตัวสร้างในRuntimeExceptionสิ่งนี้ แต่คุณสามารถสร้างของคุณเองได้อย่างง่ายดาย นี่คือตัวสร้าง:

public RuntimeException(Throwable cause)

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

try {
  overzealousAPI(thisArgumentWontWork);
}
catch (OverzealousCheckedException exception) {
  throw new RuntimeException(exception);  
}

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

catch (Exception e) { /* TODO deal with this at some point (yeah right) */}

110
การเปิดไฟล์จริง ๆ แล้วเป็นตัวอย่างที่ดีสำหรับการยกเว้นการตรวจสอบอย่างเต็มที่ เพราะส่วนใหญ่แล้วรหัสที่เปิดไฟล์นั้นไม่สามารถทำอะไรที่เป็นประโยชน์เกี่ยวกับข้อยกเว้นได้ - ทำได้ดีกว่าหลายเลเยอร์ของ call stack ข้อยกเว้นที่ตรวจสอบจะบังคับให้คุณถ่วงลายเซ็นวิธีการของคุณเพียงเพื่อให้คุณสามารถทำสิ่งที่คุณจะทำต่อไป - จัดการข้อยกเว้นในสถานที่ที่เหมาะสมที่สุดที่จะทำ
Michael Borgwardt

116
@Michael: เห็นด้วยโดยทั่วไปคุณอาจจัดการข้อยกเว้น IO เหล่านี้ได้หลายระดับขึ้นไป - แต่ฉันไม่ได้ยินคุณปฏิเสธว่าเป็นลูกค้า API คุณต้องคาดหวังสิ่งเหล่านี้ ด้วยเหตุผลนี้ฉันคิดว่าการตรวจสอบข้อยกเว้นมีความเหมาะสม ใช่คุณจะต้องประกาศ "throws" ในแต่ละวิธีการตั้งค่า call stack แต่ฉันไม่เห็นด้วยว่านี่เป็นความยุ่งเหยิง มันเป็นข้อมูลที่มีประโยชน์ในการลงนามวิธีการของคุณ คุณกำลังพูดว่า: วิธีการระดับต่ำของฉันอาจพบไฟล์ที่หายไป แต่พวกเขาจะปล่อยให้คุณจัดการกับมัน ผมเห็นอะไรจะเสียและดีเท่านั้นรหัสที่สะอาดในการรับ
Rhubarb

23
@Rhubarb: +1 คำตอบที่น่าสนใจมากแสดงข้อโต้แย้งของทั้งสองฝ่าย ยิ่งใหญ่ เกี่ยวกับความคิดเห็นล่าสุดของคุณ: โปรดทราบว่าการประกาศการขว้างปาในแต่ละวิธีอาจไม่สามารถทำได้โดยเฉพาะอย่างยิ่งหากคุณกำลังใช้อินเทอร์เฟซตลอดเส้นทาง
R. Martinho Fernandes

8
นี่เป็นข้อโต้แย้งที่แข็งแกร่งมาก (และฉันเห็นด้วยกับเรื่องนี้โดยทั่วไป) แต่มีกรณีที่มันไม่ทำงาน: การเรียกกลับทั่วไป หากฉันมีห้องสมุดเรียกรหัสผู้ใช้กำหนดบางอย่างไม่มีทาง (ที่ฉันรู้ว่าในภาษาจาวา) สำหรับห้องสมุดที่จะเผยแพร่ข้อยกเว้นตรวจสอบจากรหัสผู้ใช้ที่เรียกไปยังรหัสผู้ใช้ที่เรียกว่า ปัญหานี้ยังขยายไปยังส่วนอื่น ๆ ของระบบชนิดของภาษาที่พิมพ์แบบคงที่จำนวนมากที่ไม่สนับสนุนประเภททั่วไปที่เหมาะสม คำตอบนี้จะได้รับการปรับปรุงโดยการกล่าวถึงเรื่องนี้ (และอาจเป็นคำตอบ)
Mankarse

7
ผิด @Rhubarb การตรวจสอบข้อยกเว้นมีไว้สำหรับ 'ภาระผูกพัน' ซึ่งสามารถ & ควรได้รับการจัดการ แต่มักจะไม่สอดคล้องกับแนวทางปฏิบัติที่ดีที่สุด การเปิดไฟล์เป็นตัวอย่างที่เลือกโดยเชอร์รี่ซึ่งสามารถจัดการได้ IOException ส่วนใหญ่ (เช่นการเขียนไบต์), SQLException, RemoteException เป็นไปไม่ได้ที่จะจัดการอย่างมีความหมาย ความล้มเหลวหรือลองใหม่ควรอยู่ในระดับ "ธุรกิจ" หรือ "คำขอ" และตรวจสอบข้อยกเว้น - ที่ใช้ในไลบรารี Java - เป็นข้อผิดพลาดที่ทำให้ยาก literatejava.com/exceptions/…
Thomas W

184

สิ่งที่เกี่ยวกับข้อยกเว้นที่ผ่านการตรวจสอบคือพวกเขาไม่ใช่ข้อยกเว้นจริง ๆ โดยความเข้าใจตามปกติของแนวคิด แต่เป็นค่าตอบแทน API แทน

แนวคิดทั้งหมดของข้อยกเว้นคือข้อผิดพลาดที่เกิดขึ้นที่ใดก็ตามที่สายโซ่การสนทนาสามารถทำให้เกิดฟองขึ้นมาและจัดการโดยรหัสบางส่วนที่ไกลออกไปโดยไม่ต้องใช้รหัสที่ต้องกังวล ในทางกลับกันการตรวจสอบนั้นต้องการรหัสทุกระดับระหว่างผู้ขว้างและผู้จับเพื่อประกาศว่าพวกเขารู้เกี่ยวกับข้อยกเว้นทุกรูปแบบที่สามารถผ่านได้ วิธีนี้แตกต่างกันเล็กน้อยในทางปฏิบัติหากการตรวจสอบข้อยกเว้นเป็นเพียงค่าตอบแทนพิเศษที่ผู้โทรต้องตรวจสอบ เช่น [pseudocode].

public [int or IOException] writeToStream(OutputStream stream) {
    [void or IOException] a= stream.write(mybytes);
    if (a instanceof IOException)
        return a;
    return mybytes.length;
}

เนื่องจาก Java ไม่สามารถทำค่าส่งคืนทางเลือกหรือสิ่งอันดับอินไลน์อย่างง่ายเป็นค่าส่งคืนข้อยกเว้นที่ตรวจสอบจึงเป็นการตอบสนองที่สมเหตุสมผล

ปัญหาคือมีรหัสจำนวนมากรวมถึง swathes ที่ยอดเยี่ยมของไลบรารีมาตรฐานการใช้ข้อยกเว้นที่ตรวจสอบในทางที่ผิดสำหรับเงื่อนไขพิเศษจริง ๆ ที่คุณอาจต้องการจับได้หลายระดับ ทำไม IOException ไม่ใช่ RuntimeException ในทุก ๆ ภาษาฉันสามารถปล่อยให้มีข้อยกเว้นของ IO เกิดขึ้นและถ้าฉันไม่ทำสิ่งใดเพื่อจัดการกับมันแอปพลิเคชันของฉันจะหยุดทำงาน นี่คือสิ่งที่ดีที่สุดที่สามารถเกิดขึ้นได้

บางทีสองวิธีขึ้นจากตัวอย่างที่คุณต้องการติดตาม IOExceptions ทั้งหมดจากกระบวนการเขียนไปยังกระแสข้อมูลทั้งหมดยกเลิกกระบวนการและข้ามไปยังรหัสการรายงานข้อผิดพลาด ใน Java คุณไม่สามารถทำได้โดยไม่ต้องเพิ่ม 'throw IOException' ในทุก ๆ ระดับการโทรแม้แต่ในระดับที่ตัวเองไม่ได้ใช้ IO วิธีการดังกล่าวไม่จำเป็นต้องรู้เกี่ยวกับการจัดการข้อยกเว้น; ต้องเพิ่มข้อยกเว้นในลายเซ็น:

  1. เพิ่มการมีเพศสัมพันธ์โดยไม่จำเป็น
  2. ทำให้ส่วนต่อประสานที่ใช้งานง่ายมีความเปราะบางเปลี่ยนไป
  3. ทำให้โค้ดอ่านน้อยลง
  4. เป็นเรื่องที่น่ารำคาญมากที่ปฏิกิริยาของโปรแกรมเมอร์ทั่วไปคือการเอาชนะระบบโดยทำสิ่งที่น่ากลัวเช่น 'throws Exception', 'catch (Exception e) {}' หรือล้อมทุกอย่างไว้ใน RuntimeException (ซึ่งทำให้การดีบักยากขึ้น)

แล้วมีข้อยกเว้นห้องสมุดที่น่าหัวเราะมากมายเช่น:

try {
    httpconn.setRequestMethod("POST");
} catch (ProtocolException e) {
    throw new CanNeverHappenException("oh dear!");
}

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

ผลกระทบที่ไม่ดีอีกอย่างหนึ่งคือการควบคุมการผกผันโดยที่ส่วนประกอบ A ส่งกลับไปยังองค์ประกอบทั่วไป B. ส่วนประกอบ A ต้องการให้สามารถยกเว้นการโยนจากการเรียกกลับไปยังสถานที่ที่เรียกว่าคอมโพเนนต์ B แต่ไม่สามารถทำได้ เพราะนั่นจะเปลี่ยนอินเตอร์เฟสการติดต่อกลับซึ่งแก้ไขโดย B. A เท่านั้นที่ทำได้โดยการตัดข้อยกเว้นจริงใน RuntimeException ซึ่งยังมีการจัดการข้อยกเว้นสำเร็จรูปในการเขียน

ตรวจสอบข้อยกเว้นตามที่นำมาใช้ใน Java และไลบรารีมาตรฐานของมันหมายถึงสำเร็จรูปและสำเร็จรูป ในภาษา verbose แล้วนี่ไม่ใช่การชนะ


14
ในตัวอย่างโค้ดของคุณควรเชื่อมโยงข้อยกเว้นเพื่อหาสาเหตุดั้งเดิมเมื่ออ่านบันทึก: การโยน CanNeverHappenException (e);
Esko Luontola

5
ฉันไม่เห็นด้วย. ข้อยกเว้นตรวจสอบหรือไม่เป็นเงื่อนไขที่ยอดเยี่ยม ตัวอย่าง: วิธีการดึงข้อมูลวัตถุผ่าน HTTP ค่าตอบแทนเป็นวัตถุหรือไม่มีอะไรอื่นทุกสิ่งที่เลวร้ายได้ รักษาพวกเขาเป็นค่าตอบแทนตามที่ทำใน C เพียงนำไปสู่ความสับสนและการออกแบบที่ไม่ดี
มิสเตอร์สมิ ธ

15
@Mister: สิ่งที่ฉันพูดคือการตรวจสอบข้อยกเว้นที่นำมาใช้ใน Java ในทางปฏิบัติมากขึ้นเช่นค่าตอบแทนใน C กว่าพวกเขาทำ 'ข้อยกเว้น' แบบดั้งเดิมที่เราอาจรู้จักจาก C ++ และภาษา Java ล่วงหน้าอื่น ๆ และ IMO นี้ก็นำไปสู่ความสับสนและการออกแบบที่ไม่ดี
bobince

9
ยอมรับว่าไลบรารีมาตรฐานในทางที่ผิดในการใช้ข้อยกเว้นที่ตรวจสอบแล้วได้เพิ่มความสับสนและพฤติกรรมการจับที่ไม่ดีอย่างแน่นอน และบ่อยครั้งเป็นเพียงเอกสารที่ไม่ดีเช่นวิธีการฉีกขาดเช่น disconnect () ที่พ่น IOException เมื่อ "เกิดข้อผิดพลาด I / O อื่น ๆ " ฉันก็ตัดการเชื่อมต่อ! ฉันกำลังรั่วไหลที่จับหรือทรัพยากรอื่น ๆ ? ฉันต้องลองอีกครั้งหรือไม่ โดยไม่รู้ว่าทำไมมันเกิดขึ้นฉันไม่สามารถรับการกระทำที่ฉันควรทำและดังนั้นฉันต้องเดาว่าฉันควรกลืนมันลองอีกครั้งหรือประกันตัว
charstar

14
+1 สำหรับ "API ค่าส่งคืนทางเลือก" วิธีการดูข้อยกเว้นที่น่าสนใจ
Zsolt Török

75

แทนที่จะทำใหม่ด้วยเหตุผลทั้งหมด (หลายข้อ) ต่อข้อยกเว้นที่เลือกฉันจะเลือกเพียงข้อเดียว ฉันเสียจำนวนครั้งที่ฉันเขียนบล็อคของรหัสนี้:

try {
  // do stuff
} catch (AnnoyingcheckedException e) {
  throw new RuntimeException(e);
}

99% ของเวลาที่ฉันไม่สามารถทำอะไรกับมัน ในที่สุดบล็อกทำการล้างข้อมูลที่จำเป็น (หรืออย่างน้อยก็ควร)

ฉันได้สูญเสียจำนวนครั้งที่ฉันได้เห็นสิ่งนี้:

try {
  // do stuff
} catch (AnnoyingCheckedException e) {
  // do nothing
}

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

แล้วเราก็มีโค้ดที่ทำให้โกรธซึ่งใช้ข้อยกเว้นเป็นรูปแบบของการควบคุมการไหลเช่นjava.text.Formatทำ Bzzzt ไม่ถูกต้อง. ผู้ใช้ที่ใส่ "abc" ลงในเขตข้อมูลหมายเลขบนแบบฟอร์มไม่ใช่ข้อยกเว้น

ตกลงฉันเดาว่าเป็นสามเหตุผล


4
แต่ถ้ามีการดักจับข้อยกเว้นอย่างเหมาะสมคุณสามารถแจ้งผู้ใช้ทำงานอื่น ๆ (บันทึก?) และออกจากแอปพลิเคชันในแบบที่ควบคุมได้ ฉันยอมรับว่าบางส่วนของ API อาจได้รับการออกแบบให้ดีขึ้น และด้วยเหตุผลที่โปรแกรมเมอร์ขี้เกียจฉันคิดว่าในฐานะโปรแกรมเมอร์คุณเป็นผู้รับผิดชอบโค้ดของคุณ 100%
มิสเตอร์สมิ ธ

3
โปรดทราบว่าลอง catch-rethrow ช่วยให้คุณสามารถระบุข้อความ - ฉันมักจะใช้มันเพื่อเพิ่มข้อมูลเกี่ยวกับเนื้อหาของตัวแปรสถานะ ตัวอย่างที่พบบ่อยสำหรับ IOExceptions เพื่อเพิ่ม absolutePathName () ของไฟล์ที่เป็นปัญหา
Thorbjørn Ravn Andersen

15
ฉันคิดว่า IDEs เช่น Eclipse มีโทษหลายอย่างที่คุณเคยเห็นบล็อก catch ว่างเปล่า จริง ๆ แล้วพวกเขาควรจะสร้างใหม่ตามค่าเริ่มต้น
artbristol

12
"99% ของเวลาที่ฉันไม่สามารถทำอะไรกับมันได้" - ผิดคุณสามารถแสดงข้อความผู้ใช้ว่า "ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์" หรือ "อุปกรณ์ IO ล้มเหลว" แทนที่จะปล่อยให้แอปขัดข้อง เนื่องจากเครือข่ายเล็กน้อยสะดุด ตัวอย่างของคุณทั้งสองเป็นงานศิลปะจากโปรแกรมเมอร์ที่ไม่ดี คุณควรจะโจมตีโปรแกรมเมอร์ที่ไม่ดีและไม่ได้ตรวจสอบข้อยกเว้นตัวเอง มันเหมือนกับฉันโจมตีอินซูลินที่ไม่ได้ช่วยเบาหวานเมื่อฉันใช้มันเป็นน้ำสลัด
AxiomaticNexus

2
@YasmaniLlanes คุณไม่สามารถทำสิ่งเหล่านี้ได้ตลอดเวลา บางครั้งคุณมีอินเทอร์เฟซที่จะปฏิบัติตาม และนี่เป็นเรื่องจริงโดยเฉพาะอย่างยิ่งเมื่อคุณออกแบบ API ที่บำรุงรักษาได้ดีเพราะคุณไม่สามารถเริ่มผลข้างเคียงได้ทุกที่ ทั้งความซับซ้อนและความซับซ้อนที่จะนำเสนอจะกัดคุณอย่างรุนแรงในวงกว้าง ใช่แล้ว 99% ของเวลาไม่มีอะไรที่ต้องทำ
MasterMastic

50

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

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

ใช้Listอินเทอร์เฟซJava ที่ดี เรามีร่วมกันในการใช้งานหน่วยความจำชอบและArrayList LinkedListนอกจากนี้เรายังมีคลาสโครงกระดูกAbstractListซึ่งทำให้ง่ายต่อการออกแบบรายการประเภทใหม่ สำหรับรายการที่อ่านอย่างเดียวที่เราจำเป็นต้องใช้เพียงสองวิธี: และsize()get(int index)

WidgetListคลาสตัวอย่างนี้อ่านออบเจ็กต์ขนาดคงที่Widget(ไม่แสดง) จากไฟล์:

class WidgetList extends AbstractList<Widget> {
    private static final int SIZE_OF_WIDGET = 100;
    private final RandomAccessFile file;

    public WidgetList(RandomAccessFile file) {
        this.file = file;
    }

    @Override
    public int size() {
        return (int)(file.length() / SIZE_OF_WIDGET);
    }

    @Override
    public Widget get(int index) {
        file.seek((long)index * SIZE_OF_WIDGET);
        byte[] data = new byte[SIZE_OF_WIDGET];
        file.read(data);
        return new Widget(data);
    }
}

โดยการเปิดเผยวิดเจ็ตโดยใช้Listอินเทอร์เฟซที่คุ้นเคยคุณสามารถดึงรายการ ( list.get(123)) หรือทำซ้ำรายการ ( for (Widget w : list) ...) โดยไม่จำเป็นต้องรู้เกี่ยวกับWidgetListตัวเอง หนึ่งสามารถผ่านรายการนี้เพื่อวิธีการมาตรฐานใด ๆ Collections.synchronizedListที่ใช้รายการทั่วไปหรือห่อไว้ใน รหัสที่ใช้ไม่จำเป็นต้องรู้หรือไม่สนใจว่า "วิดเจ็ต" ถูกสร้างขึ้นตรงจุดมาจากอาร์เรย์หรืออ่านจากไฟล์หรือฐานข้อมูลหรือจากทั่วเครือข่ายหรือจากการถ่ายทอดพื้นที่ย่อยในอนาคต มันจะยังคงทำงานได้อย่างถูกต้องเนื่องจากมีการใช้Listอินเทอร์เฟซอย่างถูกต้อง

ยกเว้นว่ามันจะไม่ ชั้นข้างต้นไม่ได้รวบรวมเพราะวิธีการเข้าถึงไฟล์อาจโยนIOExceptionข้อยกเว้นการตรวจสอบซึ่งคุณต้อง "จับหรือระบุ" คุณไม่สามารถระบุว่าโยนได้คอมไพเลอร์จะไม่ยอมให้คุณเพราะนั่นจะเป็นการละเมิดสัญญาของListส่วนต่อประสาน และไม่มีวิธีที่มีประโยชน์ที่WidgetListตัวเองสามารถจัดการกับข้อยกเว้น (อย่างที่ฉันจะอธิบายในภายหลัง)

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

@Override
public int size() {
    try {
        return (int)(file.length() / SIZE_OF_WIDGET);
    } catch (IOException e) {
        throw new WidgetListException(e);
    }
}

public static class WidgetListException extends RuntimeException {
    public WidgetListException(Throwable cause) {
        super(cause);
    }
}

((แก้ไข: Java 8 ได้เพิ่มUncheckedIOExceptionคลาสสำหรับกรณีนี้อย่างแน่นอน: สำหรับการจับและ rethrowing IOExceptions ข้ามขอบเขตเมธอด polymorphic ชนิดของจุดพิสูจน์ของฉัน!))

ดังนั้นข้อยกเว้นที่ตรวจสอบจึงไม่สามารถใช้งานได้ในกรณีเช่นนี้ คุณไม่สามารถโยนพวกเขา เช่นเดียวกันสำหรับการMapสำรองข้อมูลที่ฉลาดโดยฐานข้อมูลหรือการใช้งานของการjava.util.Randomเชื่อมต่อกับแหล่งข้อมูลเอนโทรปีควอนตัมผ่านพอร์ต COM ทันทีที่คุณพยายามทำสิ่งใดก็ตามที่แปลกใหม่ด้วยการใช้งานส่วนต่อประสาน polymorphic แนวคิดของข้อยกเว้นที่ตรวจสอบจะล้มเหลว แต่ข้อยกเว้นที่ถูกตรวจสอบนั้นน่ารังเกียจจนพวกเขายังคงไม่ทำให้คุณอยู่ในความสงบเพราะคุณยังต้องจับและรื้อถอนวิธีการในระดับที่ต่ำกว่าการทำให้โค้ดยุ่งเหยิงและการติดตามสแต็ก

ฉันพบว่าRunnableอินเทอร์เฟซที่แพร่หลายมักจะถูกสำรองข้อมูลไว้ที่มุมนี้หากเรียกสิ่งที่พ่นข้อยกเว้นที่ตรวจสอบ มันไม่สามารถโยนข้อยกเว้นตามที่เป็นอยู่ดังนั้นสิ่งที่มันสามารถทำได้คือความยุ่งเหยิงรหัสโดยการจับและ rethrowing RuntimeExceptionเป็นที่

จริงๆแล้วคุณ สามารถโยนข้อยกเว้นตรวจสอบไม่ได้ประกาศถ้าคุณหันไปแฮ็ก JVM ในขณะดำเนินการไม่สนใจกฎข้อยกเว้นที่ตรวจสอบดังนั้นเราจำเป็นต้องหลอกผู้รวบรวมเท่านั้น วิธีที่ง่ายที่สุดในการทำเช่นนี้คือการใช้ยาในทางที่ผิด นี่คือวิธีการของฉัน (ชื่อคลาสที่แสดงเพราะ (ก่อน Java 8) มันจำเป็นในการเรียกไวยากรณ์สำหรับวิธีการทั่วไป):

class Util {
    /**
     * Throws any {@link Throwable} without needing to declare it in the
     * method's {@code throws} clause.
     * 
     * <p>When calling, it is suggested to prepend this method by the
     * {@code throw} keyword. This tells the compiler about the control flow,
     * about reachable and unreachable code. (For example, you don't need to
     * specify a method return value when throwing an exception.) To support
     * this, this method has a return type of {@link RuntimeException},
     * although it never returns anything.
     * 
     * @param t the {@code Throwable} to throw
     * @return nothing; this method never returns normally
     * @throws Throwable that was provided to the method
     * @throws NullPointerException if {@code t} is {@code null}
     */
    public static RuntimeException sneakyThrow(Throwable t) {
        return Util.<RuntimeException>sneakyThrow1(t);
    }

    @SuppressWarnings("unchecked")
    private static <T extends Throwable> RuntimeException sneakyThrow1(
            Throwable t) throws T {
        throw (T)t;
    }
}

เย่! การใช้สิ่งนี้เราสามารถโยนข้อยกเว้นที่ถูกตรวจสอบความลึกใด ๆ ของสแต็กโดยไม่ต้องประกาศโดยไม่ต้องห่อในRuntimeExceptionและโดยไม่ทำให้กองสแต็ควุ่นวาย! ใช้ตัวอย่าง "WidgetList" อีกครั้ง:

@Override
public int size() {
    try {
        return (int)(file.length() / SIZE_OF_WIDGET);
    } catch (IOException e) {
        throw sneakyThrow(e);
    }
}

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

try {
    ...
} catch (Throwable t) { // catch everything
    if (t instanceof IOException) {
        // handle it
        ...
    } else {
        // didn't want to catch this one; let it go
        throw t;
    }
}

นั่นคืออึดอัดเล็กน้อย RuntimeExceptionแต่ในด้านบวกก็ยังคงง่ายกว่ารหัสสำหรับการแยกข้อยกเว้นการตรวจสอบที่ถูกห่อในเล็กน้อย

อย่างมีความสุขthrow t;คำสั่งถูกกฎหมายที่นี่แม้ว่าประเภทของtมีการตรวจสอบขอบคุณกฎที่เพิ่มใน Java 7 เกี่ยวกับการ rethrowing ข้อยกเว้นจับ


เมื่อตรวจสอบข้อยกเว้นพบ polymorphism ในกรณีตรงกันข้ามก็เป็นปัญหา: เมื่อวิธีการระบุว่าอาจจะเป็นข้อยกเว้นที่ตรวจสอบ แต่การใช้งานแทนที่ไม่ได้ ยกตัวอย่างเช่นระดับนามธรรมOutputStream's วิธีการทั้งหมดที่ระบุwrite เป็นคลาสย่อยที่เขียนไปยังอาร์เรย์ในหน่วยความจำแทนแหล่ง I / O จริง วิธีการแทนที่มันไม่สามารถทำให้เกิดs ดังนั้นพวกเขาไม่มีthrows IOExceptionByteArrayOutputStreamwriteIOExceptionthrowsประโยคและคุณสามารถเรียกพวกเขาโดยไม่ต้องกังวลเกี่ยวกับข้อกำหนด catch-or-ระบุ

ยกเว้นไม่เสมอไป สมมติว่าWidgetมีวิธีบันทึกเป็นสตรีม:

public void writeTo(OutputStream out) throws IOException;

การประกาศวิธีนี้ให้ยอมรับแบบธรรมดาOutputStreamเป็นสิ่งที่ถูกต้องที่ต้องทำดังนั้นจึงสามารถใช้ polymorphically กับเอาต์พุตทุกชนิด: ไฟล์ฐานข้อมูลเครือข่ายและอื่น ๆ และอาร์เรย์ในหน่วยความจำ อย่างไรก็ตามด้วยอาร์เรย์ในหน่วยความจำมีข้อกำหนดที่ต้องใช้ในการจัดการกับข้อยกเว้นที่ไม่สามารถเกิดขึ้นได้จริง:

ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
    someWidget.writeTo(out);
} catch (IOException e) {
    // can't happen (although we shouldn't ignore it if it does)
    throw new RuntimeException(e);
}

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

แต่เดี๋ยวก่อนข้อยกเว้นที่ตรวจสอบแล้วน่ารำคาญจริงซึ่งจะไม่ทำให้คุณย้อนกลับ! ลองนึกภาพคุณกำลังจับใด ๆIOExceptionโยนโดยwriteการโทรบนOutputStreamแต่คุณต้องการเปลี่ยนประเภทการประกาศตัวแปรไปยังByteArrayOutputStreamคอมไพเลอร์จะด่าคุณพยายามที่จะจับข้อยกเว้นการตรวจสอบว่ามันบอกว่าไม่สามารถโยน

กฎนั้นทำให้เกิดปัญหาที่ไร้สาระบางอย่าง ตัวอย่างเช่นหนึ่งในสามwriteวิธีการOutputStreamคือไม่ได้ByteArrayOutputStreamแทนที่โดย โดยเฉพาะwrite(byte[] data)คือวิธีการอำนวยความสะดวกที่เขียนอาร์เรย์แบบเต็มโดยการโทรwrite(byte[] data, int offset, int length)ด้วย offset ของ 0 และความยาวของอาร์เรย์ ByteArrayOutputStreamแทนที่เมธอดสามอาร์กิวเมนต์ แต่สืบทอดวิธีการความสะดวกแบบหนึ่งอาร์กิวเมนต์ตามที่เป็นอยู่ วิธีที่สืบทอดมานั้นทำในสิ่งที่ถูกต้อง แต่ก็มีthrowsข้อที่ไม่พึงประสงค์ นั่นอาจเป็นการกำกับดูแลในการออกแบบของByteArrayOutputStreamแต่พวกเขาไม่สามารถแก้ไขได้เพราะมันจะทำลายความเข้ากันได้ของแหล่งที่มากับโค้ดใด ๆ ที่จับข้อยกเว้น - ข้อยกเว้นที่ไม่เคยมีมาไม่เคยจะไม่ถูกโยนทิ้ง!

กฎนั้นน่ารำคาญในระหว่างการแก้ไขและแก้ไขบั๊ก เช่นบางครั้งฉันจะคอมเมนต์การเรียกใช้เมธอดเป็นการชั่วคราวและหากมีข้อผิดพลาดที่ตรวจสอบได้ตอนนี้คอมไพเลอร์จะบ่นเกี่ยวกับการมีอยู่ของโลคัลtryและcatchบล็อก ดังนั้นฉันต้องแสดงความคิดเห็นออกด้วยและตอนนี้เมื่อแก้ไขรหัสภายใน IDE จะเยื้องไปในระดับที่ไม่ถูกต้องเพราะ{และ}มีการแสดงความคิดเห็น Gah! มันเป็นเรื่องร้องเรียนเล็กน้อย แต่ดูเหมือนว่าสิ่งเดียวที่ตรวจสอบข้อยกเว้นที่เคยทำคือก่อให้เกิดปัญหา


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

แต่เราได้รับสำนวนดังต่อไปนี้ซึ่งอาละวาดเป็นวิธีปิดคอมไพเลอร์:

try {
    ...
} catch (SomeStupidExceptionOmgWhoCares e) {
    e.printStackTrace();
}

ใน GUI หรือโปรแกรมอัตโนมัติข้อความที่พิมพ์จะไม่ปรากฏให้เห็น ที่แย่กว่านั้นคือจะใช้รหัสที่เหลือหลังจากข้อยกเว้น ข้อยกเว้นไม่ใช่ข้อผิดพลาดจริงหรือไม่ จากนั้นอย่าพิมพ์ออกมา มิฉะนั้นจะมีสิ่งอื่นกำลังระเบิดขึ้นในช่วงเวลาหนึ่งซึ่งวัตถุข้อยกเว้นเดิมจะหายไป สำนวนนี้ไม่ดีกว่าพื้นฐานของOn Error Resume Nextหรือของ error_reporting(0);PHP

การเรียกคลาสตัวบันทึกบางชนิดนั้นไม่ได้ดีกว่า:

try {
    ...
} catch (SomethingWeird e) {
    logger.log(e);
}

นั่นเป็นเพียงขี้เกียจe.printStackTrace();และยังคงไถบนด้วยรหัสในสถานะไม่แน่นอน ยิ่งไปกว่านั้นตัวเลือกของระบบการบันทึกเฉพาะหรือตัวจัดการอื่น ๆ เป็นแบบเฉพาะแอปพลิเคชันดังนั้นจึงเป็นการใช้รหัสซ้ำ

แต่เดี๋ยวก่อน! มีวิธีที่ง่ายและเป็นสากลในการค้นหาตัวจัดการเฉพาะแอปพลิเคชัน มันสูงกว่า call stack (หรือถูกตั้งค่าเป็นhandler exception thread ที่ไม่ถูกตรวจจับ ) ดังนั้นในสถานที่มากที่สุดสิ่งที่คุณต้องทำคือการโยนข้อยกเว้นสูงขึ้นสแต็ค เช่นthrow e;. ตรวจสอบข้อยกเว้นเพิ่งได้รับในทาง

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


สำหรับวิธีขนาดของคุณด้วย WidgetList ฉันจะแคชขนาดในตัวแปรและตั้งไว้ในตัวสร้าง ตัวสร้างมีอิสระที่จะโยนข้อยกเว้น สิ่งนี้จะไม่ทำงานหากไฟล์มีการเปลี่ยนแปลงในขณะที่ใช้ WidgetList ซึ่งอาจจะไม่ดีถ้าเป็นเช่นนั้น
TofuBeer

2
SomeStupidExceptionOmgWhoCares คนที่ดูแลตัวเองดีพอที่จะโยนมัน ดังนั้นไม่ควรมีการโยน (ออกแบบไม่ดี) หรือคุณควรจัดการกับมัน เช่นเดียวกับการใช้งานไม่ถูกต้องของคลาส pre-1.0 (สตรีมเอาต์พุตอาร์เรย์ไบต์) ซึ่งการออกแบบไม่ดี
TofuBeer

2
สำนวนที่เหมาะสมจะได้รับการสั่งการที่จะจับข้อยกเว้นใด ๆ ที่ระบุโยนโดยสายย่อยที่ซ้อนกันและ rethrow RuntimeExceptionพวกเขาอยู่ในห่อหนึ่ง โปรดทราบว่ารูทีนสามารถประกาศพร้อมกันได้throws IOExceptionและยังระบุว่าการIOExceptionส่งออกจากการโทรที่ซ้อนกันควรถูกพิจารณาว่าไม่คาดคิด
supercat

15
ฉันเป็นนักพัฒนา C # มืออาชีพที่มีประสบการณ์เกี่ยวกับ Java ซึ่งสะดุดกับโพสต์นี้ ฉันงุนงงว่าทำไมทุกคนจะสนับสนุนพฤติกรรมที่แปลกประหลาดนี้ ใน. NET ถ้าฉันต้องการตรวจจับชนิดของข้อยกเว้นเฉพาะฉันสามารถตรวจจับได้ ถ้าฉันต้องการปล่อยให้มันถูกโยนทิ้งไปกอง ๆ มันไม่มีอะไรจะทำ ฉันหวังว่า Java นั้นไม่แปลกเลย :)
aikeru

เกี่ยวกับ "บางครั้งฉันจะออกความเห็นวิธีการโทรชั่วคราว" - ฉันเรียนรู้ที่จะใช้if (false)สำหรับสิ่งนี้ มันหลีกเลี่ยงปัญหาการโยนประโยคและคำเตือนช่วยให้ฉันนำทางกลับได้เร็วขึ้น +++ ที่กล่าวว่าฉันเห็นด้วยกับทุกสิ่งที่คุณเขียน การตรวจสอบข้อยกเว้นมีค่าบางอย่าง แต่ค่านี้เล็กน้อยเมื่อเทียบกับค่าใช้จ่ายของพวกเขา เกือบทุกครั้งที่พวกเขาเข้าใกล้
maaartinus

45

มันไม่เกี่ยวกับการแสดง stacktrace หรือ crashing อย่างเงียบ ๆ มันเกี่ยวกับความสามารถในการสื่อสารข้อผิดพลาดระหว่างเลเยอร์

ปัญหาเกี่ยวกับข้อยกเว้นที่ตรวจสอบคือพวกเขาสนับสนุนให้ผู้คนกลืนรายละเอียดที่สำคัญ (กล่าวคือคลาสยกเว้น) หากคุณเลือกที่จะไม่กลืนรายละเอียดนั้นคุณต้องเพิ่มการประกาศในแอพทั้งหมดของคุณ ซึ่งหมายความว่า 1) ประเภทยกเว้นใหม่จะมีผลต่อลายเซ็นของฟังก์ชันจำนวนมากและ 2) คุณสามารถพลาดอินสแตนซ์ที่เฉพาะเจาะจงของข้อยกเว้นที่คุณต้องการจับได้ (กล่าวว่าคุณเปิดไฟล์รองสำหรับฟังก์ชั่นที่เขียนข้อมูลไปยัง ไฟล์ไฟล์ที่สองเป็นทางเลือกดังนั้นคุณสามารถละเว้นข้อผิดพลาดได้ แต่เนื่องจากลายเซ็นthrows IOExceptionจึงมองข้ามสิ่งนี้ได้ง่าย)

ตอนนี้ฉันกำลังจัดการกับสถานการณ์นี้ในแอปพลิเคชัน เราบรรจุใหม่เกือบยกเว้นเป็น AppSpecificException สิ่งนี้ทำให้ลายเซ็นสะอาดและเราไม่ต้องกังวลเกี่ยวกับการระเบิดthrowsในลายเซ็น

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

นี่คือเหตุผลที่ฉันเป็นแฟนตัวยงของการจัดการข้อยกเว้นในงูหลาม คุณสามารถจับเฉพาะสิ่งที่คุณต้องการและ / หรือสามารถจัดการได้ ทุกสิ่งทุกอย่างจะทำให้คุณรู้สึกราวกับว่าคุณกลับมาเอง (ซึ่งคุณได้ทำไปแล้ว)

ฉันพบแล้วครั้งแล้วครั้งเล่าและตลอดโครงการที่ฉันกล่าวถึงการจัดการข้อยกเว้นนั้นแบ่งออกเป็น 3 ประเภท:

  1. จับและจัดการข้อยกเว้นเฉพาะ นี่คือการใช้ตรรกะลองใหม่ตัวอย่างเช่น
  2. จับและสร้างข้อยกเว้นอื่น ๆ สิ่งที่เกิดขึ้นที่นี่มักจะเข้าสู่ระบบและมักจะมีข้อความซ้ำซากเช่น "ไม่สามารถเปิด $ filename" ข้อผิดพลาดเหล่านี้เป็นข้อผิดพลาดที่คุณไม่สามารถทำได้ มีเพียงระดับที่สูงกว่าเท่านั้นที่รู้พอที่จะรับมือได้
  3. จับทุกอย่างและแสดงข้อความข้อผิดพลาด โดยปกติแล้วจะอยู่ที่รูทของโปรแกรมเลือกจ่ายงานและมันทำให้แน่ใจว่าสามารถสื่อสารข้อผิดพลาดกับผู้โทรผ่านกลไกที่ไม่ใช่ข้อยกเว้นได้

5
คุณสามารถสร้างคลาสย่อยเฉพาะของ AppSpecificException เพื่ออนุญาตการแยกขณะที่ยังคงลายเซ็นวิธีการธรรมดา
Thorbjørn Ravn Andersen

1
นอกจากนี้ยังมีความสำคัญมากสำหรับรายการที่ 2 คือช่วยให้คุณสามารถเพิ่มข้อมูลในข้อยกเว้นที่จับได้ (เช่นการซ้อนใน RuntimeException) เป็นการดีกว่ามากหากมีชื่อของไฟล์ที่ไม่พบในการติดตามสแต็กแทนที่จะซ่อนลึกลงในไฟล์บันทึก
Thorbjørn Ravn Andersen

1
โดยพื้นฐานแล้วข้อโต้แย้งของคุณคือ "การจัดการข้อยกเว้นเหนื่อยมากดังนั้นฉันจึงไม่อยากจัดการกับมัน" เมื่อข้อยกเว้นทำให้ความหมายและการสร้างบริบทนั้นไร้ประโยชน์จริง ในฐานะผู้ออกแบบ API คุณควรทำสัญญาอย่างชัดเจนว่าจะเกิดอะไรขึ้นเมื่อสิ่งต่าง ๆ ผิดพลาดหากโปรแกรมของฉันขัดข้องเพราะฉันไม่ได้รับแจ้งว่าสิ่งนี้หรือข้อยกเว้นนั้นทำให้ "ฟองสบู่" คุณเป็นนักออกแบบล้มเหลวและ ผลลัพธ์ของความล้มเหลวของคุณระบบของฉันไม่เสถียรเท่าที่ควร
Newtopian

4
นั่นไม่ใช่สิ่งที่ฉันพูดเลย ประโยคสุดท้ายของคุณเห็นด้วยกับฉันจริง ๆ หากทุกอย่างถูกห่อใน AppSpecificException จะไม่ทำให้เกิดฟอง (และความหมาย / บริบทหายไป) และใช่ไคลเอ็นต์ API ไม่ได้รับการแจ้ง - นี่เป็นสิ่งที่เกิดขึ้นกับข้อยกเว้นที่ตรวจสอบแล้ว (เหมือนใน java) เพราะผู้คนไม่ต้องการจัดการกับฟังก์ชั่นที่มีการthrowsประกาศมากมาย
Richard Levasseur

6
@ Newtopian - ข้อยกเว้นส่วนใหญ่สามารถจัดการได้ที่ระดับ "ธุรกิจ" หรือ "คำขอ" มันเหมาะสมที่จะล้มเหลวหรือลองอีกครั้งในขนาดใหญ่ไม่ใช่สำหรับทุกความล้มเหลวที่อาจเกิดขึ้น ด้วยเหตุนี้วิธีปฏิบัติที่ดีที่สุดในการจัดการข้อยกเว้นจึงสรุปได้ว่า "เริ่มเร็วเข้าช้า" การตรวจสอบข้อยกเว้นทำให้ยากต่อการจัดการความน่าเชื่อถือในระดับที่ถูกต้องและกระตุ้นให้บล็อกบล็อกที่มีโค้ดผิดพลาดจำนวนมาก literatejava.com/exceptions/…
Thomas W

24

SNR

ประการแรกข้อยกเว้นที่ตรวจสอบจะลด "อัตราส่วนสัญญาณต่อสัญญาณรบกวน" สำหรับรหัส Anders Hejlsberg ยังพูดถึงการเขียนโปรแกรมเชิงเปรียบเทียบและเชิงพยากรณ์ซึ่งเป็นแนวคิดที่คล้ายกัน อย่างไรก็ตามพิจารณาตัวอย่างโค้ดต่อไปนี้:

อัปเดต UI จากไม่ใช่ UI-thread ใน Java:

try {  
    // Run the update code on the Swing thread  
    SwingUtilities.invokeAndWait(() -> {  
        try {
            // Update UI value from the file system data  
            FileUtility f = new FileUtility();  
            uiComponent.setValue(f.readSomething());
        } catch (IOException e) {  
            throw new UncheckedIOException(e);
        }
    });
} catch (InterruptedException ex) {  
    throw new IllegalStateException("Interrupted updating UI", ex);  
} catch (InvocationTargetException ex) {
    throw new IllegalStateException("Invocation target exception updating UI", ex);
}

อัปเดต UI จากไม่ใช่ UI-thread ใน C #:

private void UpdateValue()  
{  
   // Ensure the update happens on the UI thread  
   if (InvokeRequired)  
   {  
       Invoke(new MethodInvoker(UpdateValue));  
   }  
   else  
   {  
       // Update UI value from the file system data  
       FileUtility f = new FileUtility();  
       uiComponent.Value = f.ReadSomething();  
   }  
}  

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

แหกคุก

ในการติดตั้งแม้กระทั่งการใช้งานขั้นพื้นฐานที่สุดเช่นอินเทอร์เฟซรายการของ Java ตรวจสอบข้อยกเว้นว่าเป็นเครื่องมือสำหรับการออกแบบโดยสัญญาล้มลง พิจารณารายการที่สำรองข้อมูลโดยฐานข้อมูลหรือระบบไฟล์หรือการใช้งานอื่น ๆ ที่ส่งข้อยกเว้นที่ตรวจสอบ การนำไปใช้ที่เป็นไปได้เพียงอย่างเดียวคือการตรวจจับข้อยกเว้นที่ตรวจสอบและสร้างใหม่เป็นข้อยกเว้นที่ไม่ได้ตรวจสอบ:

@Override
public void clear()  
{  
   try  
   {  
       backingImplementation.clear();  
   }  
   catch (CheckedBackingImplException ex)  
   {  
       throw new IllegalStateException("Error clearing underlying list.", ex);  
   }  
}  

และตอนนี้คุณต้องถามว่าอะไรคือจุดสำคัญของรหัสทั้งหมด? ข้อยกเว้นที่ผ่านการตรวจสอบเพียงเพิ่มสัญญาณรบกวนมีการตรวจพบข้อยกเว้น แต่ไม่ได้จัดการและออกแบบโดยสัญญา (ในแง่ของข้อยกเว้นที่ตรวจสอบแล้ว) พัง

ข้อสรุป

  • การจับข้อยกเว้นนั้นแตกต่างจากการจัดการพวกมัน
  • การตรวจสอบข้อยกเว้นเพิ่มสัญญาณรบกวนให้กับรหัส
  • การจัดการข้อยกเว้นทำงานได้ดีใน C # หากไม่มี

ฉัน blogged เกี่ยวกับเรื่องนี้ก่อนหน้านี้


3
ในตัวอย่างทั้ง Java และ C # เป็นเพียงการปล่อยให้ข้อยกเว้นเผยแพร่โดยไม่ต้องจัดการ (Java ผ่าน IllegalStateException) ความแตกต่างคือคุณอาจต้องการจัดการ FileNotFoundException แต่ไม่น่าที่การจัดการ InvocationTargetException หรือ InterruptedException จะมีประโยชน์
ลุค Quinane

3
และในวิธี C # ฉันจะรู้ได้อย่างไรว่าข้อยกเว้น I / O สามารถเกิดขึ้นได้? นอกจากนี้ฉันจะไม่โยนข้อยกเว้นจากการทำงาน ... ฉันพิจารณาว่าการจัดการข้อยกเว้นที่ไม่เหมาะสม ขออภัยสำหรับส่วนนั้นของรหัสของฉันฉันก็เห็นด้านข้างของคุณ
TofuBeer

7
เรากำลังไปถึง :-) ดังนั้นด้วย API รุ่นใหม่แต่ละครั้งคุณต้องรวมการโทรทั้งหมดและค้นหาข้อยกเว้นใหม่ ๆ ที่อาจเกิดขึ้น? สามารถเกิดขึ้นได้ง่ายกับ API ภายในของ บริษัท เนื่องจากพวกเขาไม่ต้องกังวลเกี่ยวกับความเข้ากันได้ย้อนหลัง
TofuBeer

3
คุณหมายถึงลดอัตราส่วนสัญญาณต่อเสียงรบกวนหรือไม่
neo2862

3
@TofuBeer ไม่ได้ถูกบังคับให้อัปเดตรหัสของคุณหลังจากอินเทอร์เฟซของ API ที่อยู่ภายใต้การเปลี่ยนแปลงเป็นสิ่งที่ดีใช่ไหม หากคุณมีข้อยกเว้นที่ไม่ได้ตรวจสอบอยู่ที่นั่นคุณจะต้องลงเอยด้วยโปรแกรมที่เสียหาย / ไม่สมบูรณ์โดยไม่ทราบ
Francois Bourgeois

23

Artima ตีพิมพ์บทสัมภาษณ์กับหนึ่งในสถาปนิกของ. NET, Anders Hejlsberg ซึ่งครอบคลุมข้อโต้แย้งอย่างรุนแรงจากข้อยกเว้นที่ตรวจสอบ นักชิมสั้น:

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


2
อันที่จริงหัวหน้าสถาปนิก
กับดัก

18
ฉันได้อ่านมาแล้วสำหรับเขาแล้วการโต้เถียงของเขาทำให้ "มีโปรแกรมเมอร์ที่ไม่ดี"
TofuBeer

6
TofuBeer ไม่ได้ทั้งหมด ประเด็นทั้งหมดคือหลายครั้งที่คุณไม่รู้ว่าจะทำอย่างไรกับข้อยกเว้นว่าวิธีการที่เรียกใช้จะถูกโยนและกรณีที่คุณสนใจจริงๆไม่ได้กล่าวถึง คุณเปิดไฟล์คุณจะได้รับข้อยกเว้นของ IO ตัวอย่างเช่น ... นั่นไม่ใช่ปัญหาของฉันดังนั้นฉันจึงโยนมันทิ้ง แต่วิธีการโทรระดับบนสุดจะต้องการหยุดการประมวลผลและแจ้งให้ผู้ใช้ทราบว่ามีปัญหาที่ไม่รู้จัก ข้อยกเว้นที่เลือกไม่ได้ช่วยอะไรเลย มันเป็นหนึ่งในล้านสิ่งประหลาดที่สามารถเกิดขึ้นได้
Dan Rosenstark

4
@ ใช่ถ้าคุณไม่ชอบข้อยกเว้นที่ตรวจสอบแล้วให้ทำ "โยน RuntimeException ใหม่ (" เราไม่ได้คาดหวังสิ่งนี้เมื่อทำ Foo.bar () ", e)" และทำได้ด้วย
Thorbjørn Ravn Andersen

4
@ ThorbjørnRavnAndersen: จุดอ่อนการออกแบบพื้นฐานใน Java ซึ่ง. net คัดลอกโชคร้ายคือมันใช้ประเภทของข้อยกเว้นเป็นทั้งวิธีการหลักในการตัดสินใจว่าควรจะดำเนินการและวิธีการหลักในการระบุประเภททั่วไปของสิ่ง มันผิดพลาดจริง ๆ แล้วทั้งสองประเด็นนี้มีฉากตั้งฉากเป็นส่วนใหญ่ สิ่งที่สำคัญไม่ใช่สิ่งที่ผิดพลาด แต่มีวัตถุสถานะใดบ้างนอกจากนี้ทั้ง. net และ Java จะคิดตามค่าเริ่มต้นที่ดำเนินการและแก้ไขข้อยกเว้นโดยทั่วไปแล้วเป็นสิ่งเดียวกัน
supercat

20

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

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

และเหตุผลก็เพราะถ้าเป็นเช่นนั้นฉันต้องแก้ไขและฉันต้องแก้ไขทันที ฉันต้องการทราบทันทีว่ามีปัญหา

คุณจัดการกับข้อยกเว้นจริงกี่ครั้ง ฉันไม่ได้พูดถึงการจับข้อยกเว้น - ฉันกำลังพูดถึงเรื่องการจัดการพวกมันอยู่เหรอ? ง่ายเกินไปที่จะเขียนสิ่งต่อไปนี้:

try {
  thirdPartyMethod();
} catch(TPException e) {
  // this should never happen
}

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

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


Java สนับสนุนให้คุณทำสิ่งนี้เพื่อที่คุณจะได้ไม่ต้องเพิ่มข้อยกเว้นทุกประเภทลงในลายเซ็นวิธีการทุกวิธี
yfeldblum

17
ตลก .. นับตั้งแต่ฉันสวมกอดข้อยกเว้นที่ตรวจสอบอย่างถูกต้องและใช้อย่างเหมาะสมโปรแกรมของฉันหยุดการระเบิดกองใหญ่ในใบหน้าของคุณไม่พอใจลูกค้า หากในขณะที่การพัฒนาคุณมีการติดตามสแต็กที่ไม่ดีที่น่าเกลียดอย่างยิ่งใหญ่ลูกค้าก็จำเป็นต้องได้รับเช่นกัน ได้เห็นใบหน้าของเขาเมื่อเขาเห็น ArrayIndexOutOfBoundsException ด้วยการติดตามสแต็คสูงเป็นไมล์บนระบบที่ขัดข้องแทนการแจ้งเตือนถาดเล็ก ๆ โดยบอกว่าการกำหนดค่าสีสำหรับปุ่ม XYZ ไม่สามารถแยกวิเคราะห์ได้ดังนั้นจึงใช้ค่าเริ่มต้นแทนซอฟต์แวร์ พร้อม
Newtopian

1
บางทีสิ่งที่ Java ต้องการคือการประกาศ "cantHandle" ซึ่งจะระบุว่าวิธีการหรือลอง / catch block ของรหัสไม่ได้เตรียมที่จะจัดการกับข้อยกเว้นเฉพาะที่เกิดขึ้นภายในและข้อยกเว้นใด ๆ ที่เกิดขึ้นผ่านวิธีอื่นนอกเหนือจากที่ชัดเจน โยนภายในวิธีการนั้น (ตรงข้ามกับวิธีการที่เรียก) ควรถูกห่อและส่งใหม่โดยอัตโนมัติใน RuntimeException IMHO ข้อยกเว้นที่ตรวจสอบแล้วไม่ค่อยควรเผยแพร่ call stack โดยไม่ห่อหุ้ม
supercat

3
@ Newtopian - ฉันเขียนเซิร์ฟเวอร์และซอฟต์แวร์ความน่าเชื่อถือสูงและดำเนินการมา 25 ปีแล้ว โปรแกรมของฉันไม่เคยปลิวไปและฉันทำงานกับระบบที่มีความพร้อมใช้งานสูงลองและเชื่อมต่อใหม่ระบบการเงินและการทหารตามการบูรณาการ ฉันมีจุดประสงค์ที่แน่นอนเพื่อให้ได้ข้อยกเว้นรันไทม์ ข้อยกเว้นที่ตรวจสอบแล้วทำให้ยากต่อการปฏิบัติที่ดีที่สุดในการ "โยนเร็วจับช้า" ความน่าเชื่อถือที่ถูกต้องและการจัดการข้อผิดพลาดอยู่ในระดับ "ธุรกิจ", "การเชื่อมต่อ" หรือ "คำขอ" (หรือบางครั้งเมื่อแยกวิเคราะห์ข้อมูล) การตรวจสอบข้อยกเว้นจะทำให้ถูกต้อง
Thomas W

1
ข้อยกเว้นที่คุณพูดถึงอยู่ที่นี่RuntimeExceptionsซึ่งแน่นอนว่าคุณไม่ต้องจับและฉันยอมรับว่าคุณควรปล่อยให้โปรแกรมระเบิดออกมา ข้อยกเว้นที่คุณควรจับและจัดการอยู่เสมอคือข้อยกเว้นที่IOExceptionเลือก หากคุณได้รับIOExceptionไม่มีสิ่งใดที่จะแก้ไขในรหัสของคุณ โปรแกรมของคุณไม่ควรระเบิดเพราะมีอาการสะอึกบนเครือข่าย
AxiomaticNexus

20

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

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

ความผิด: เงื่อนไขที่ไม่ได้วางแผนซึ่งป้องกันไม่ให้วิธีการบรรลุวัตถุประสงค์ตามวัตถุประสงค์ที่ไม่สามารถอธิบายได้หากไม่มีการอ้างอิงถึงการใช้งานภายในของวิธีการ

(ดังนั้นไม่อนุญาตให้ใช้ตารางดังนั้นคุณอาจต้องการอ่านสิ่งต่อไปนี้จาก หน้าต้นฉบับ ... )

ฉุกเฉิน

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

ความผิดพลาด

  • ถือว่าเป็น: แปลกใจที่น่ารังเกียจ
  • คาดว่าจะเกิดขึ้น: ไม่
  • ใครสนใจเกี่ยวกับเรื่องนี้: ผู้ที่ต้องการแก้ไขปัญหา
  • ตัวอย่าง: ข้อบกพร่องในการเขียนโปรแกรม, ความผิดปกติของฮาร์ดแวร์, ความผิดพลาดในการกำหนดค่า, ไฟล์ที่หายไป, เซิร์ฟเวอร์ไม่พร้อมใช้งาน
  • การแมปที่ดีที่สุด: ข้อยกเว้นที่ไม่ได้ตรวจสอบ

ฉันรู้ว่าเมื่อจะใช้พวกเขาผมต้องการที่จะรู้ว่าทำไมคนที่ไม่ได้ทำตามคำแนะนำว่า ... ไม่ได้ทำตามคำแนะนำที่ :-)
TofuBeer

บั๊กโปรแกรมมิงคืออะไรและจะแยกพวกมันออกจากบั๊กการใช้งานได้อย่างไร? มันเป็นข้อผิดพลาดในการเขียนโปรแกรมหรือไม่ถ้าผู้ใช้ส่งอาร์กิวเมนต์ที่ไม่ถูกต้องไปที่โปรแกรม? จากมุมมอง Java อาจไม่ใช่บั๊กการเขียนโปรแกรม แต่จากมุมมองเชลล์สคริปต์จะเป็นบั๊กการเขียนโปรแกรม ดังนั้นอาร์กิวเมนต์ที่ไม่ถูกต้องargs[]คืออะไร พวกเขาเป็นภาระผูกพันหรือความผิดพลาด?
ceving

3
@TofuBeer - เพราะนักออกแบบ Java ห้องสมุดเลือกที่จะใส่ทุกชนิดของความล้มเหลวในระดับต่ำไม่สามารถกู้เป็นการตรวจสอบข้อยกเว้นเมื่อพวกเขาได้อย่างชัดเจนควรจะได้รับไม่ถูกตรวจสอบ FileNotFound เป็น IOException เดียวที่ควรตรวจสอบเช่น ในเรื่องเกี่ยวกับ JDBC ด้วย - เพียงการเชื่อมต่อกับฐานข้อมูลพอสมควรได้รับการพิจารณาฉุกเฉิน SQLExceptions อื่น ๆ ทั้งหมดควรมีความล้มเหลวและไม่ถูกตรวจสอบ การจัดการข้อผิดพลาดควรอยู่ในระดับ "ธุรกิจ" หรือ "คำขอ" อย่างถูกต้องโปรดดูแนวทางปฏิบัติที่ดีที่สุด "เร็ว แต่ช้าไปช้า" การตรวจสอบข้อยกเว้นเป็นอุปสรรคต่อการที่
โทมัส W

1
การโต้แย้งของคุณมีข้อบกพร่องอย่างใหญ่หลวง "ความไม่แน่นอน" จะต้องไม่ถูกจัดการผ่านข้อยกเว้น แต่ผ่านทางรหัสธุรกิจและวิธีการคืนค่า มีข้อยกเว้นสำหรับตามที่คำว่าสถานการณ์ที่ผิดปกติดังนั้นข้อผิดพลาด
Matteo Mosca

@MatteoMosca รหัสที่ส่งคืนข้อผิดพลาดมีแนวโน้มที่จะถูกละเว้นและนั่นก็เพียงพอที่จะตัดสิทธิ์พวกเขา ที่จริงแล้วสิ่งที่สามารถเกิดขึ้นได้บ่อยครั้งจะถูกจัดการที่ใดที่หนึ่งในสแต็กเท่านั้นและนั่นเป็นกรณีที่ใช้เพื่อการยกเว้น ฉันนึกภาพออกว่าจะFile#openInputStreamกลับEither<InputStream, Problem>- ถ้านั่นคือสิ่งที่คุณหมายถึงเราอาจเห็นด้วย
maaartinus

19

ในระยะสั้น:

ข้อยกเว้นเป็นคำถามการออกแบบ API -- ไม่มากไม่น้อย.

อาร์กิวเมนต์สำหรับข้อยกเว้นที่ตรวจสอบ:

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

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

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

ในความเป็นจริงแม้ว่าเคยบ่อยเกินไปแพคเกจcom.acmeเท่านั้นพ่นAcmeExceptionมากกว่า subclasses ที่เฉพาะเจาะจง ผู้โทรต้องจัดการประกาศหรือส่งสัญญาณAcmeExceptionsใหม่ แต่ยังไม่สามารถแน่ใจได้ว่าAcmeFileNotFoundErrorเกิดขึ้นหรือไม่AcmePermissionDeniedErrorไม่

ดังนั้นหากคุณกำลังสนใจเฉพาะในAcmeFileNotFoundErrorการแก้ปัญหาคือจะยื่นคำขอพร้อมโปรแกรมเมอร์ ACME AcmeExceptionและบอกพวกเขาในการดำเนินการประกาศและเอกสารที่เป็นรอง

เหตุใดจึงต้องกังวล

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

เป็นผลให้ภาษาที่ไม่มีข้อยกเว้นที่ตรวจสอบแล้วมักไม่เลวร้ายลงมากนัก โปรแกรมเมอร์อาจถูกล่อลวงให้สร้างอินสแตนซ์ที่ไม่เฉพาะเจาะจงของErrorคลาสทั่วไปมากกว่าAcmeExceptionแต่ถ้าพวกเขาสนใจเกี่ยวกับคุณภาพ API ของพวกเขาพวกเขาจะได้เรียนรู้ที่จะแนะนำAcmeFileNotFoundErrorต่อไป

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

หากคุณทำตามแนวเหตุผลนี้มันควรจะชัดเจนว่า "ความยุ่งยาก" ของการประกาศจับและโยนข้อยกเว้นที่มีอยู่ทั่วไปในภาษาเช่น Java มักจะเพิ่มคุณค่าเล็กน้อย

นอกจากนี้ยังเป็นที่น่าสังเกตว่า Java VM ไม่ได้ตรวจสอบข้อยกเว้น - เฉพาะคอมไพเลอร์ Java จะตรวจสอบพวกเขาและไฟล์คลาสที่มีการประกาศข้อยกเว้นที่เปลี่ยนแปลงนั้นเข้ากันได้ในเวลาทำงาน ความปลอดภัย Java VM ไม่ได้รับการปรับปรุงโดยการตรวจสอบข้อยกเว้นสไตล์การเขียนโค้ดเท่านั้น


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

1
การออกแบบห้องสมุด Java ทำผิดพลาดร้ายแรง 'ข้อยกเว้นที่ตรวจสอบแล้ว' มีไว้เพื่อคาดการณ์และกู้คืนได้ - เช่นไม่พบไฟล์, ไม่สามารถเชื่อมต่อได้ พวกเขาไม่เคยตั้งใจหรือเหมาะสำหรับความล้มเหลวของระบบในระดับต่ำ จะเป็นการดีที่จะบังคับให้เปิดไฟล์ที่จะตรวจสอบ แต่ไม่มีการลองใหม่หรือการกู้คืนที่เหมาะสมสำหรับความล้มเหลวในการเขียนไบต์เดียว / ดำเนินการแบบสอบถาม SQL เป็นต้นลองใหม่หรือกู้คืนจะถูกจัดการอย่างถูกต้องที่คำขอ "ธุรกิจ" หรือ " "ระดับซึ่งการตรวจสอบข้อยกเว้นทำให้ยากลำบาก literatejava.com/exceptions/ …
Thomas W

17

ฉันทำงานกับนักพัฒนาหลายคนในช่วงสามปีที่ผ่านมาในแอปพลิเคชันที่ค่อนข้างซับซ้อน เรามีฐานรหัสที่ใช้ข้อยกเว้นที่ตรวจสอบแล้วบ่อยครั้งด้วยการจัดการข้อผิดพลาดที่เหมาะสมและอื่น ๆ ที่ไม่ได้

จนถึงตอนนี้ฉันพบว่าการทำงานกับรหัสฐานกับ Checked Exceptions ง่ายขึ้น เมื่อฉันใช้ API ของบุคคลอื่นเป็นเรื่องดีที่ฉันสามารถเห็นได้ว่าเงื่อนไขข้อผิดพลาดประเภทใดที่ฉันสามารถคาดหวังได้เมื่อฉันเรียกใช้รหัสและจัดการอย่างถูกต้องไม่ว่าจะเป็นการบันทึกการแสดงหรือเพิกเฉย (ใช่มีกรณีที่ถูกต้อง ข้อยกเว้นเช่นการใช้ ClassLoader) ที่ให้รหัสฉันเขียนโอกาสในการกู้คืน ข้อยกเว้นรันไทม์ทั้งหมดที่ฉันเผยแพร่จนกว่าพวกเขาจะแคชและจัดการกับรหัสการจัดการข้อผิดพลาดทั่วไปบางอย่าง เมื่อฉันพบข้อยกเว้นที่ถูกตรวจสอบซึ่งฉันไม่ต้องการจัดการในระดับที่เฉพาะเจาะจงหรือฉันพิจารณาข้อผิดพลาดเชิงตรรกะของการเขียนโปรแกรมจากนั้นฉันก็วางมันลงใน RuntimeException และปล่อยให้มันเกิดฟองขึ้น ไม่เคยกลืนข้อยกเว้นโดยไม่มีเหตุผลที่ดี (และเหตุผลที่ดีในการทำเช่นนี้ค่อนข้างหายาก)

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

ทั้งหมดนี้เป็นเรื่องของการตั้งค่าและทักษะการพัฒนา การเขียนโปรแกรมและการจัดการข้อผิดพลาดทั้งสองวิธีมีประสิทธิภาพเท่ากัน (หรือไม่มีประสิทธิภาพ) ดังนั้นฉันจะไม่พูดว่ามีทางเดียว

สรุปแล้วฉันพบว่าการทำงานกับ Checked Exceptions ง่ายขึ้นโดยเฉพาะในโครงการขนาดใหญ่ที่มีผู้พัฒนาจำนวนมาก


6
ฉันทำเพื่อ สำหรับฉันพวกเขาเป็นส่วนสำคัญของสัญญา โดยไม่ต้องไปดูรายละเอียดในเอกสาร API ฉันสามารถรู้ได้อย่างรวดเร็วถึงสถานการณ์ข้อผิดพลาด
เวย์นฮาร์ทแมน

2
ตกลง. ฉันประสบความจำเป็นในการตรวจสอบข้อยกเว้นใน. Net หนึ่งครั้งเมื่อฉันพยายามโทรผ่านเครือข่าย เมื่อรู้ว่าการสะอึกเครือข่ายอาจเกิดขึ้นได้ทุกเมื่อฉันต้องอ่านเอกสารทั้งหมดของ API เพื่อดูว่ามีข้อยกเว้นอะไรบ้างที่ฉันจำเป็นต้องตรวจจับเป็นพิเศษสำหรับสถานการณ์นั้น หาก C # ตรวจสอบข้อยกเว้นฉันจะรู้ทันที นักพัฒนา C # อื่น ๆ อาจปล่อยให้แอปพังจากข้อผิดพลาดของเครือข่ายอย่างง่าย
AxiomaticNexus

13

ประเภทข้อยกเว้น

เมื่อพูดถึงข้อยกเว้นฉันมักจะอ้างถึงบทความในบล็อกข้อยกเว้น Vexing ของ Eric Lippert เขาวางข้อยกเว้นไว้ในหมวดหมู่เหล่านี้:

  • ร้ายแรง - ข้อยกเว้นเหล่านี้ไม่ใช่ความผิดของคุณ : คุณไม่สามารถป้องกันได้และคุณไม่สามารถจัดการได้อย่างสมเหตุสมผล ยกตัวอย่างเช่นหรือOutOfMemoryErrorThreadAbortException
  • Boneheaded - ข้อยกเว้นเหล่านี้เป็นความผิดของคุณ : คุณควรป้องกันไม่ให้พวกเขาและพวกเขาเป็นตัวแทนข้อบกพร่องในรหัสของคุณ ตัวอย่างเช่นArrayIndexOutOfBoundsException, หรือใด ๆNullPointerExceptionIllegalArgumentException
  • Vexing - ข้อยกเว้นเหล่านี้ไม่ได้ยอดเยี่ยมไม่ใช่ความผิดของคุณคุณไม่สามารถป้องกันได้ แต่คุณจะต้องจัดการกับพวกเขา พวกเขามักจะเป็นผลมาจากการตัดสินใจการออกแบบที่โชคร้ายเช่นการขว้างNumberFormatExceptionจากInteger.parseIntแทนที่จะให้Integer.tryParseIntวิธีการที่ส่งกลับค่าบูลีนเท็จในการแยกวิเคราะห์ความล้มเหลว
  • ภายนอก - ข้อยกเว้นเหล่านี้มักจะได้รับการยกเว้นไม่ผิดของคุณคุณจะไม่สามารถ (พอสมควร) ป้องกันไม่ให้พวกเขา แต่คุณต้องจัดการกับพวกเขา ตัวอย่างเช่นFileNotFoundException.

ผู้ใช้ API:

  • ต้องไม่จัดการกับข้อยกเว้นที่ร้ายแรงหรือปวดหัว
  • ควรจัดการกับข้อยกเว้นที่น่ารำคาญแต่ไม่ควรเกิดขึ้นใน API ในอุดมคติ
  • ต้องจัดการกับข้อยกเว้นภายนอก

ตรวจสอบข้อยกเว้น

ความจริงที่ว่าผู้ใช้ API ต้องจัดการกับข้อยกเว้นบางอย่างเป็นส่วนหนึ่งของสัญญาของวิธีการระหว่างผู้โทรและผู้รับ ระบุสัญญาในสิ่งอื่น ๆ : จำนวนและประเภทของข้อโต้แย้งคาดว่าผู้ถูกเรียกประเภทของค่าตอบแทนผู้โทรสามารถคาดหวังและข้อยกเว้นโทรที่คาดว่าจะจับ

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

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

ปัญหา API

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

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

ข้อสรุป

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

ดังนั้นอย่าเกลียดชวา Java และข้อยกเว้นที่เลือก แต่ไม่ชอบ API ที่ใช้การตรวจสอบข้อยกเว้นมากเกินไป


และใช้ในทางที่ผิดโดยไม่มีลำดับชั้น
TofuBeer

1
FileNotFound และการสร้างการเชื่อมต่อ JDBC / เครือข่ายเป็นภาระผูกพันและถูกต้องที่จะตรวจสอบข้อยกเว้นเนื่องจากสิ่งเหล่านี้สามารถคาดการณ์ได้และสามารถกู้คืนได้ (เป็นไปได้) IOExceptions อื่น ๆ ส่วนใหญ่ SQLExceptions, RemoteException และอื่น ๆ เป็นความล้มเหลวที่คาดเดาไม่ได้และไม่สามารถกู้คืนได้และควรเป็นข้อยกเว้นแบบรันไทม์ เนื่องจากการออกแบบห้องสมุด Java ที่เข้าใจผิดเราทุกคนต่างตกอยู่ในความผิดพลาดนี้และตอนนี้ส่วนใหญ่ใช้ Spring & Hibernate (ผู้ที่ได้รับการออกแบบที่ถูกต้อง)
Thomas W

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

6

ตกลง ... ข้อยกเว้นที่ตรวจสอบแล้วไม่เหมาะและมีข้อแม้บางประการ แต่มีจุดประสงค์ เมื่อสร้าง API มีกรณีเฉพาะของความล้มเหลวที่เป็นสัญญาของ API นี้ เมื่ออยู่ในบริบทของภาษาที่มีการพิมพ์อย่างคงที่เช่น Java หากไม่มีการใช้ข้อยกเว้นที่ตรวจสอบแล้วเราจะต้องอาศัยเอกสารและระเบียบปฏิบัติเพื่อถ่ายทอดความเป็นไปได้ของข้อผิดพลาด การทำเช่นนี้จะเป็นการลบผลประโยชน์ทั้งหมดที่คอมไพเลอร์สามารถนำมาซึ่งการจัดการข้อผิดพลาดและคุณถูกทิ้งให้อยู่ในความตั้งใจที่ดีของโปรแกรมเมอร์

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

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

รับกรณีเมื่อมีการโยนข้อยกเว้นบางแห่งที่ด้านล่างของเลเยอร์ API และเพิ่ง bubbled ขึ้นเพราะไม่มีใครรู้ว่ามันเป็นไปได้สำหรับข้อผิดพลาดที่จะเกิดขึ้นนี้แม้ว่ามันเป็นข้อผิดพลาดชนิดที่น่าเชื่อถือมากเมื่อรหัสโทร โยน (เช่น FileNotFoundException ซึ่งต่างกับ VogonsTrashingEarthExcept ... ซึ่งในกรณีนี้มันจะไม่สำคัญว่าเราจะจัดการกับมันหรือไม่เพราะไม่มีอะไรเหลือให้จัดการกับมัน)

หลายคนแย้งว่าการไม่สามารถโหลดไฟล์ได้มักจะเป็นจุดสิ้นสุดของโลกสำหรับกระบวนการและมันจะต้องตายด้วยความตายที่น่ากลัวและเจ็บปวด ดังนั้นใช่ .. แน่นอน ... ตกลง .. คุณสร้าง API สำหรับบางอย่างและโหลดไฟล์ในบางจุด ... ฉันเป็นผู้ใช้ API ที่กล่าวว่าสามารถตอบสนองได้ ... "ใครที่คุณจะตัดสินใจเมื่อฉัน โปรแกรมน่าจะผิดพลาด! " แน่ใจว่าได้รับตัวเลือกที่ข้อยกเว้นถูกกลืนและไม่มีร่องรอยหรือ EletroFlabbingChunkFluxManifoldChuggingException ด้วยการติดตามสแต็กลึกกว่าร่องลึก Marianna ฉันจะใช้วิธีหลังโดยไม่ลังเล แต่ก็หมายความว่าเป็นวิธีที่พึงประสงค์ ? เราไม่สามารถอยู่ที่ไหนสักแห่งตรงกลางที่ยกเว้นจะถูกแต่งใหม่และห่อทุกครั้งที่มันเข้าไปในระดับใหม่ของสิ่งที่เป็นนามธรรมเพื่อให้มันหมายถึงอะไร?

ท้ายที่สุดข้อโต้แย้งส่วนใหญ่ที่ฉันเห็นคือ "ฉันไม่ต้องการจัดการกับข้อยกเว้นหลายคนไม่ต้องการจัดการกับข้อยกเว้นการตรวจสอบข้อยกเว้นบังคับให้ฉันจัดการกับพวกเขาดังนั้นฉันเกลียดการตรวจสอบข้อยกเว้น" เพื่อกำจัดกลไกดังกล่าวทั้งหมดและ ผลักไสให้ไปยังช่องว่างของนรกนรกเป็นเพียงโง่และขาด jugement และวิสัยทัศน์

หากเรากำจัดข้อยกเว้นที่ตรวจสอบแล้วเราสามารถกำจัดชนิดส่งคืนสำหรับฟังก์ชั่นและส่งกลับตัวแปร "anytype" เสมอ ... ที่จะทำให้ชีวิตง่ายขึ้นมากในตอนนี้มันจะไม่?


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

6

อันที่จริงการตรวจสอบข้อยกเว้นในมือข้างหนึ่งเพิ่มความแข็งแกร่งและความถูกต้องของโปรแกรมของคุณ (คุณถูกบังคับให้ทำการประกาศที่ถูกต้องของอินเทอร์เฟซของคุณ - ข้อยกเว้นวิธีการพ่นเป็นชนิดผลตอบแทนพิเศษ) ในทางกลับกันคุณประสบปัญหาว่าเนื่องจากข้อยกเว้น "ฟองสบู่" บ่อยครั้งมากที่คุณต้องเปลี่ยนวิธีการมากมาย (ผู้โทรทั้งหมดและผู้โทรของผู้โทรเป็นต้น) เมื่อคุณเปลี่ยนข้อยกเว้น วิธีการพ่น

การตรวจสอบข้อยกเว้นใน Java ไม่สามารถแก้ปัญหาหลัง; C # และ VB.NET โยนลูกน้อยออกไปพร้อมกับอาบน้ำ

แนวทางที่ดีที่ใช้ถนนสายกลางได้อธิบายไว้ในบทความ OOPSLA 2005 นี้ (หรือรายงานทางเทคนิคที่เกี่ยวข้อง )

กล่าวโดยย่อคือคุณสามารถพูดได้: method g(x) throws like f(x)ซึ่งหมายความว่า g ขว้างข้อยกเว้นทั้งหมดที่พ่นออกมา Voila ตรวจสอบข้อยกเว้นโดยไม่มีปัญหาการเปลี่ยนแปลงแบบเรียงซ้อน

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


5

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

โดยชั้นExceptionหนึ่งจินตนาการหรือไม่ ไม่ดีเพราะExceptions เป็นข้อยกเว้นและข้อยกเว้นเช่นเดียวกันคือExceptions ยกเว้นข้อยกเว้นเหล่านั้นที่ไม่ใช่ Exception s เพราะข้อยกเว้นอื่น ๆ คือErrors จริงซึ่งเป็นข้อยกเว้นชนิดอื่นเป็นข้อยกเว้นพิเศษที่ไม่ควรเกิดขึ้นยกเว้น เมื่อใดและคุณไม่ควรจับยกเว้นบางครั้งคุณต้อง ยกเว้นนั่นไม่ใช่ทั้งหมดเพราะคุณสามารถกำหนดข้อยกเว้นอื่น ๆ ที่ไม่ใช่ทั้งExceptions และยกเว้นErrorแต่เพียงThrowableข้อยกเว้น

ข้อยกเว้นใดที่ "ตรวจสอบ" เหล่านี้คือข้อใด Throwables ถูกตรวจสอบข้อยกเว้นยกเว้นถ้าเป็นErrors ซึ่งเป็นข้อยกเว้นที่ไม่ได้ทำเครื่องหมายและมีExceptions ซึ่งเป็นThrowables และเป็นประเภทหลักของข้อยกเว้นที่ตรวจสอบด้วยยกเว้นมีข้อยกเว้นหนึ่งข้อเช่นกันซึ่งก็คือถ้าพวกเขา ก็RuntimeExceptionเป็นเพราะนั่นคือข้อยกเว้นอื่น ๆ ที่ไม่ถูกตรวจสอบ

มีRuntimeExceptionไว้เพื่ออะไร? เช่นเดียวกับชื่อที่แสดงถึงพวกเขาเป็นข้อยกเว้นเช่นเดียวกับทุกคนExceptionและพวกเขาเกิดขึ้นที่เวลาทำงานเช่นเดียวกับข้อยกเว้นทั้งหมดจริงยกเว้นว่าRuntimeExceptions นั้นยอดเยี่ยมเมื่อเทียบกับเวลาทำงานอื่น ๆExceptionเพราะพวกเขาไม่ควรจะเกิดขึ้นยกเว้น เมื่อคุณทำผิดพลาดโง่บางแม้ว่าRuntimeExceptions ไม่เคยErrors, เพื่อให้พวกเขาสำหรับสิ่งที่มีความผิดพลาดเป็นพิเศษ แต่ที่ไม่จริงErrors ยกเว้นRuntimeErrorExceptionซึ่งจริงๆเป็นRuntimeExceptionสำหรับErrors แต่ข้อยกเว้นทั้งหมดไม่ควรแสดงสถานการณ์ที่ผิดพลาดอยู่ดี ใช่พวกเขาทั้งหมด ยกเว้นสำหรับThreadDeathข้อยกเว้นที่ไม่มีข้อยกเว้นเป็นพิเศษเนื่องจากเอกสารอธิบายว่ามันเป็น "การเกิดขึ้นตามปกติ" และนั่นคือ 'Error

อย่างไรก็ตามเนื่องจากเราแบ่งข้อยกเว้นทั้งหมดลงตรงกลางเป็นErrors (ซึ่งมีไว้สำหรับข้อยกเว้นในการดำเนินการพิเศษดังนั้นไม่ต้องทำเครื่องหมาย) และExceptions (ซึ่งเป็นข้อผิดพลาดในการดำเนินการที่น้อยกว่าพิเศษดังนั้นให้ตรวจสอบยกเว้นเมื่อไม่ได้) ข้อยกเว้นแต่ละชนิดแตกต่างกัน ดังนั้นเราจึงจำเป็นIllegalAccessErrorและIllegalAccessExceptionและInstantiationErrorและInstantiationExceptionและNoSuchFieldErrorและNoSuchFieldExceptionและNoSuchMethodErrorและNoSuchMethodExceptionและและZipErrorZipException

ยกเว้นว่าแม้เมื่อมีการตรวจสอบข้อยกเว้นมีวิธีการโกงคอมไพเลอร์และโยนมันโดยไม่มีการตรวจสอบ ถ้าคุณทำคุณว่าคุณอาจจะได้รับการUndeclaredThrowableExceptionยกเว้นในกรณีอื่น ๆ ที่มันอาจจะโยนขึ้นเป็นUnexpectedExceptionหรือUnknownException(ซึ่งไม่เกี่ยวข้องกับUnknownErrorซึ่งเป็นเพียงสำหรับ "ข้อยกเว้นร้ายแรง") หรือExecutionExceptionหรือหรือInvocationTargetExceptionExceptionInInitializerError

โอ้และเราต้องไม่ลืมความโกลาหลใหม่ของ Java 8 UncheckedIOExceptionซึ่งเป็นRuntimeExceptionข้อยกเว้นที่ออกแบบมาเพื่อให้คุณสามารถโยนแนวคิดการตรวจสอบข้อยกเว้นออกไปนอกหน้าต่างโดยการตัดคำสั่งตรวจสอบIOExceptionข้อยกเว้นที่เกิดจากข้อผิดพลาด I / O (ซึ่งไม่ทำให้เกิดIOErrorข้อยกเว้น ด้วย) ที่จัดการได้ยากเป็นพิเศษและคุณต้องการให้ตรวจสอบไม่ได้

ขอบคุณ Java!


2
เท่าที่ฉันสามารถพูดได้คำตอบนี้บอกเพียงแค่ "ข้อยกเว้นของ Java เป็นระเบียบ" เต็มไปด้วยวิธีที่น่าขันและน่าขัน สิ่งที่ดูเหมือนจะไม่ทำคืออธิบายว่าทำไมโปรแกรมเมอร์จึงพยายามหลีกเลี่ยงที่จะเข้าใจว่าสิ่งเหล่านี้ควรจะทำงานอย่างไร นอกจากนี้ในกรณีของชีวิตจริง (อย่างน้อยฉันก็มีโอกาสจัดการกับ) ถ้าโปรแกรมเมอร์ไม่ได้พยายามทำให้ชีวิตของพวกเขายากขึ้นโดยมีข้อยกเว้นนั้นไม่มีอะไรซับซ้อนเท่าที่คุณได้อธิบายไว้
CptBartender

5

ปัญหา

ปัญหาที่เลวร้ายที่สุดที่ฉันเห็นด้วยกลไกการจัดการข้อยกเว้นคือมันแนะนำการทำสำเนาโค้ดในระดับมาก ! มาซื่อสัตย์กัน: ในโครงการส่วนใหญ่ใน 95% ของเวลาทั้งหมดที่นักพัฒนาจำเป็นต้องทำจริงๆคือการสื่อสารกับผู้ใช้ (และในบางกรณีให้กับทีมพัฒนาเช่นโดยการส่ง e -mail ด้วยการติดตามสแต็ก) ดังนั้นโดยปกติจะใช้บรรทัด / บล็อกเดียวกันในทุกที่ที่จัดการข้อยกเว้น

สมมติว่าเราทำการบันทึกอย่างง่าย ๆ ในแต่ละ catch block สำหรับข้อยกเว้นที่เลือกบางประเภท:

try{
   methodDeclaringCheckedException();
}catch(CheckedException e){
   logger.error(e);
}

หากเป็นข้อยกเว้นที่พบบ่อยอาจมีบล็อกการลองจับหลายร้อยตัวใน codebase ที่ใหญ่กว่า ตอนนี้สมมติว่าเราจำเป็นต้องแนะนำการจัดการข้อยกเว้นแบบป๊อปอัพแทนการบันทึกคอนโซลหรือเริ่มส่งอีเมลเพิ่มเติมไปยังทีมพัฒนา

รอสักครู่ ... เราจะแก้ไขที่ตั้งหลายร้อยแห่งในรหัสทั้งหมดหรือไม่! คุณได้รับคะแนนของฉัน :-)

การแก้ไขปัญหา

สิ่งที่เราทำเพื่อแก้ไขปัญหานั้นคือการนำเสนอแนวคิดของตัวจัดการข้อยกเว้น (ซึ่งฉันจะเรียกว่า EH's ต่อไป) เพื่อรวมศูนย์การจัดการข้อยกเว้น สำหรับทุก ๆ คลาสที่จำเป็นต้องดำเนินการข้อยกเว้นอินสแตนซ์ของตัวจัดการข้อยกเว้นจะถูกฉีดโดยเฟรมเวิร์กการฉีดของเรา ดังนั้นรูปแบบทั่วไปของการจัดการข้อยกเว้นตอนนี้จะเป็นดังนี้:

try{
    methodDeclaringCheckedException();
}catch(CheckedException e){
    exceptionHandler.handleError(e);
}

ตอนนี้เพื่อปรับแต่งการจัดการข้อยกเว้นของเราเราเพียงแค่ต้องเปลี่ยนรหัสในที่เดียว (รหัส EH)

แน่นอนว่าสำหรับกรณีที่ซับซ้อนมากขึ้นเราสามารถนำคลาสย่อยของ EHs มาใช้และใช้ประโยชน์จากคุณสมบัติที่ DI Framework ของเรามอบให้เรา ด้วยการเปลี่ยนการกำหนดค่า DI Framework ของเราเราสามารถสลับการใช้งาน EH ทั่วโลกได้อย่างง่ายดายหรือให้การใช้งานเฉพาะของ EH เป็นคลาสที่มีความต้องการการจัดการข้อยกเว้นพิเศษ (ตัวอย่างเช่น

ด้วยวิธีนี้เราสามารถแยกความแตกต่างของพฤติกรรมการจัดการข้อยกเว้นในการพัฒนาและรุ่นของแอพพลิเคชั่น (เช่นการพัฒนา - การบันทึกข้อผิดพลาดและการหยุดแอปพลิเคชัน, prod - การบันทึกข้อผิดพลาดด้วยรายละเอียดเพิ่มเติม

สิ่งสุดท้ายที่

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


6
ข้อยกเว้นถูกประดิษฐ์ขึ้นเพื่อทำสิ่งที่มีประโยชน์กับพวกเขา การเขียนลงในแฟ้มบันทึกหรือการแสดงผลหน้าต่างสวยไม่มีประโยชน์เนื่องจากปัญหาดั้งเดิมไม่ได้รับการแก้ไขโดยสิ่งนี้ การทำสิ่งที่มีประโยชน์ต้องใช้กลยุทธ์การแก้ปัญหาที่แตกต่างกัน ตัวอย่าง: ถ้าฉันไม่สามารถรับข้อมูลจากเซิร์ฟเวอร์ AI ลองบนเซิร์ฟเวอร์ B หรือหากอัลกอริทึม A สร้างฮีปมากเกินฉันลองอัลกอริทึม B ซึ่งช้ากว่ามาก แต่อาจประสบความสำเร็จ
ceving

2
@ รับใช่มันเป็นสิ่งที่ดีและเป็นจริงในทางทฤษฎี แต่ตอนนี้เรากลับมาฝึกคำศัพท์กันเถอะ กรุณาตอบด้วยความสัตย์จริงว่าคุณทำโครงการคำศัพท์จริงบ่อยแค่ไหน? ส่วนใดของcatchบล็อกในโครงการจริงนี้ทำอะไรบางอย่างที่ "มีประโยชน์" โดยมีข้อยกเว้น? 10% น่าจะดี ปัญหาที่เกิดขึ้นตามปกติที่สร้างข้อยกเว้นนั้นคือพยายามอ่านการกำหนดค่าจากไฟล์ที่ไม่มีอยู่ OutOfMemoryErrors, NullPointerExceptions ข้อผิดพลาดเกี่ยวกับความสมบูรณ์ของฐานข้อมูลข้อ จำกัด ฯลฯ ฯลฯ คุณพยายามกู้จากพวกมันทั้งหมดหรือไม่? ฉันไม่เชื่อคุณ :) บ่อยครั้งที่ไม่มีวิธีการกู้คืน
Piotr Sobczyk

3
@PiotrSobczyk: หากโปรแกรมดำเนินการบางอย่างเนื่องจากคำขอ suer และการดำเนินการล้มเหลวในแบบที่ไม่ได้เกิดความเสียหายในสถานะของระบบการแจ้งเตือนผู้ใช้ว่าการดำเนินการไม่เสร็จสมบูรณ์มีประโยชน์อย่างสมบูรณ์ วิธีการจัดการสถานการณ์ ความล้มเหลวที่ใหญ่ที่สุดของข้อยกเว้นใน C # และ. net คือว่าไม่มีวิธีที่สอดคล้องกันในการตรวจสอบว่าสิ่งใดในสถานะของระบบอาจได้รับความเสียหาย
supercat

4
ถูกต้อง @PiotrSobczyk ส่วนใหญ่การกระทำที่ถูกต้องเพียงอย่างเดียวที่จะตอบสนองต่อข้อยกเว้นคือการย้อนกลับธุรกรรมและส่งกลับการตอบกลับข้อผิดพลาด แนวคิดของ "การแก้ไขข้อยกเว้น" บ่งบอกถึงความรู้และอำนาจที่เราไม่มี (และไม่ควร) และละเมิดการห่อหุ้ม หากใบสมัครของเราไม่ใช่ DB เราไม่ควรพยายามแก้ไข DB ความล้มเหลวอย่างหมดจดและหลีกเลี่ยงการเขียนข้อมูลที่ผิดพลาด ceving เป็นพอมีประโยชน์
โทมัส W

@PiotrSobczyk เมื่อวานนี้ฉันจัดการกับข้อยกเว้น "ไม่สามารถอ่านวัตถุ" (ซึ่งจะเกิดขึ้นเพราะฐานข้อมูลพื้นฐานได้รับการปรับปรุงก่อนซอฟต์แวร์ - ซึ่งไม่ควรเกิดขึ้น แต่เป็นไปได้เนื่องจากข้อผิดพลาดของมนุษย์) โดยไม่ผ่าน เวอร์ชันประวัติของฐานข้อมูลรับประกันให้ชี้ไปที่เวอร์ชันเก่าของวัตถุ
Eponymous

4

Anders พูดถึงข้อผิดพลาดของข้อยกเว้นที่ตรวจสอบและทำไมเขาถึงปล่อยพวกเขาออกจาก C # ในตอนที่ 97ของวิทยุวิศวกรรมซอฟต์แวร์


4

การเขียนบน c2.com ของฉันยังคงไม่เปลี่ยนแปลงจากรูปแบบเดิมมากที่สุด: CheckedExceptionsAreIncompatibleWithVisitorPattern

สรุป:

รูปแบบผู้เยี่ยมชมและญาติของมันคือคลาสของอินเทอร์เฟซที่ผู้เรียกทางอ้อมและการใช้งานอินเทอร์เฟซทั้งรู้เกี่ยวกับข้อยกเว้น แต่อินเทอร์เฟซและผู้เรียกโดยตรงสร้างไลบรารี

ข้อสันนิษฐานเบื้องต้นของ CheckedExceptions คือข้อยกเว้นที่ประกาศไว้ทั้งหมดสามารถโยนทิ้งได้จากทุกจุดที่เรียกใช้เมธอดที่มีการประกาศนั้น VisitorPattern เปิดเผยว่าข้อผิดพลาดนี้เกิดขึ้น

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

สำหรับปัญหาพื้นฐาน:

แนวคิดทั่วไปของฉันคือตัวจัดการระดับสูงต้องตีความข้อยกเว้นและแสดงข้อความแสดงข้อผิดพลาดที่เหมาะสม ฉันมักจะเห็นทั้งข้อยกเว้น IO, ข้อยกเว้นการสื่อสาร (ด้วยเหตุผลบางอย่างแยกแยะ API) หรือข้อผิดพลาดร้ายแรงของงาน (ข้อผิดพลาดของโปรแกรมหรือปัญหาร้ายแรงในการสำรองเซิร์ฟเวอร์) ดังนั้นสิ่งนี้ไม่ควรยากเกินไปถ้าเราอนุญาต ปัญหาเซิร์ฟเวอร์


1
คุณควรมีสิ่งที่ต้องการ DAGNodeException ในอินเทอร์เฟซแล้วจับ IOException และแปลงเป็น DAGNodeException: การเรียกเป็นโมฆะสาธารณะ (DAGNode ARG) โยน DAGNodeException;
TofuBeer

2
@TeerBeer นั่นคือสิ่งที่ฉันต้องการ ฉันพบว่าการยกเว้นและการแกะห่ออย่างต่อเนื่องนั้นแย่กว่าการเอาข้อยกเว้นที่ตรวจสอบออก
Joshua

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

1
@TofuBeer - เมื่อล้มเหลวบอกผู้ใช้ว่าล้มเหลวถูกต้อง! คุณมีทางเลือกอื่นนอกเหนือไปจาก "กระดาษทับ" ความล้มเหลวของ 'โมฆะ' หรือข้อมูลไม่สมบูรณ์ / ไม่ถูกต้อง? การแกล้งทำเป็นว่าประสบความสำเร็จนั้นเป็นเรื่องโกหก ด้วยประสบการณ์ 25 ปีในระบบความน่าเชื่อถือสูงตรรกะการลองใหม่ควรใช้อย่างระมัดระวังและเหมาะสม ฉันคาดหวังว่าผู้เยี่ยมชมจะล้มเหลวอีกครั้งไม่ว่าคุณจะลองกี่ครั้งก็ตาม เว้นแต่ว่าคุณกำลังบินด้วยเครื่องบินการสลับไปใช้อัลกอริทึมรุ่นที่สองจะไม่สามารถใช้งานได้และไม่น่าเชื่อ (และอาจล้มเหลว)
Thomas W

4

หากต้องการพยายามตอบคำถามที่ยังไม่ได้รับคำตอบ:

ถ้าคุณใช้คลาสย่อยของ RuntimeException แทนที่จะเป็นคลาสย่อย Exception คุณจะรู้ได้อย่างไรว่าคุณควรจะจับอะไร?

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

ตัวอย่างเช่น:

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

อย่างไรก็ตามหากฉันกำลังเขียนเอ็นจิ้นการประมวลผลฉันจะใช้ XPathException (ตรวจสอบ) ในลักษณะเดียวกับ NPE: ฉันจะปล่อยให้มันวิ่งขึ้นไปด้านบนของเธรดคนงานข้ามส่วนที่เหลือของแบตช์นั้น ปัญหา (หรือส่งไปยังแผนกสนับสนุนสำหรับการวินิจฉัย) และออกความคิดเห็นสำหรับผู้ใช้เพื่อติดต่อฝ่ายสนับสนุน


2
เผง ง่ายและตรงไปตรงมาวิธีการจัดการข้อยกเว้นเป็น ในฐานะที่เป็นเดฟกล่าวว่าข้อยกเว้นการจัดการที่ถูกต้องจะทำตามปกติในระดับสูง "โยนเร็วจับช้า" เป็นหลักการ การตรวจสอบข้อยกเว้นทำให้ยาก
Thomas W

4

บทความนี้เป็นข้อความที่ดีที่สุดเกี่ยวกับการจัดการข้อยกเว้นใน Java ที่ฉันเคยอ่าน

มันจะไม่ถูกตรวจสอบในข้อยกเว้นที่ตรวจสอบ แต่ตัวเลือกนี้มีการอธิบายอย่างมากและอยู่บนพื้นฐานของข้อโต้แย้งที่แข็งแกร่ง

ฉันไม่ต้องการอ้างถึงเนื้อหาของบทความมากเกินไปที่นี่ (เป็นการดีที่สุดที่จะอ่านโดยรวม) แต่ครอบคลุมถึงข้อโต้แย้งส่วนใหญ่ของข้อยกเว้นที่ไม่ได้ตรวจสอบที่สนับสนุนจากหัวข้อนี้ โดยเฉพาะอาร์กิวเมนต์นี้ (ซึ่งดูเหมือนจะเป็นที่นิยมมาก) ครอบคลุม:

รับกรณีเมื่อมีการโยนข้อยกเว้นบางแห่งที่ด้านล่างของเลเยอร์ API และเพิ่ง bubbled ขึ้นเพราะไม่มีใครรู้ว่ามันเป็นไปได้สำหรับข้อผิดพลาดที่จะเกิดขึ้นนี้แม้ว่ามันเป็นข้อผิดพลาดชนิดที่น่าเชื่อถือมากเมื่อรหัสโทร โยน (เช่น FileNotFoundException ซึ่งต่างกับ VogonsTrashingEarthExcept ... ซึ่งในกรณีนี้มันจะไม่สำคัญว่าเราจะจัดการกับมันหรือไม่เพราะไม่มีอะไรเหลือให้จัดการกับมัน)

ผู้เขียน "การตอบสนอง":

มันไม่ถูกต้องอย่างแน่นอนที่จะสมมติว่าข้อยกเว้นรันไทม์ทั้งหมดไม่ควรถูกจับและได้รับอนุญาตให้เผยแพร่ไปยัง "สุดยอด" ของแอปพลิเคชัน (... ) สำหรับทุกเงื่อนไขพิเศษที่จำเป็นต้องได้รับการจัดการอย่างชัดเจน - ตามความต้องการของระบบ / ธุรกิจ - โปรแกรมเมอร์จะต้องตัดสินใจว่าจะจับที่ใดและจะทำอย่างไรเมื่อจับเงื่อนไขแล้ว สิ่งนี้จะต้องทำอย่างเคร่งครัดตามความต้องการที่แท้จริงของแอปพลิเคชันไม่ใช่ตามการแจ้งเตือนของคอมไพเลอร์ ข้อผิดพลาดอื่น ๆ ทั้งหมดจะต้องได้รับอนุญาตให้เผยแพร่อย่างอิสระไปยังตัวจัดการสูงสุดที่พวกเขาจะถูกบันทึกและการดำเนินการที่สง่างาม (อาจจะยุติ)

และความคิดหลักหรือบทความคือ:

เมื่อกล่าวถึงข้อผิดพลาดในการจัดการซอฟต์แวร์ข้อสันนิษฐานที่ปลอดภัยและถูกต้องเท่านั้นที่อาจเกิดขึ้นคือความล้มเหลวอาจเกิดขึ้นได้ในรูทีนย่อยหรือโมดูลที่มีอยู่จริง!

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

น่าเศร้าที่มีคนไม่มากนักที่ดูเหมือนจะค้นพบบทความที่ยอดเยี่ยมนี้ :-( ฉันขอแนะนำอย่างสุดซึ้งทุกคนที่ลังเลว่าวิธีการใดดีกว่าที่จะใช้เวลาสักครู่และอ่าน


4

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

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

โดยทั่วไปความล้มเหลวเป็นไปได้ในโค้ดและ EJB, คอนเทนเนอร์ของเว็บ & Swing / AWT ได้จัดเตรียมสิ่งนี้ไว้แล้วโดยการจัดหาตัวจัดการข้อยกเว้น "คำขอล้มเหลว" นอกสุด กลยุทธ์ที่ถูกต้องพื้นฐานที่สุดคือการย้อนกลับธุรกรรมและส่งคืนข้อผิดพลาด

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

อาร์กิวเมนต์ที่ใหญ่ที่สุดของข้อยกเว้น“ ที่ตรวจสอบ” คือข้อยกเว้นส่วนใหญ่ไม่สามารถแก้ไขได้ ความจริงง่ายๆคือเราไม่ได้เป็นเจ้าของรหัส / ระบบย่อยที่พัง เราไม่เห็นการใช้งานเราไม่รับผิดชอบและไม่สามารถแก้ไขได้

หากใบสมัครของเราไม่ใช่ DB .. เราไม่ควรลองและแก้ไข DB ที่จะละเมิด หลักการของการห่อหุ้ม

ปัญหาที่เกิดขึ้นโดยเฉพาะอย่างยิ่งได้รับพื้นที่ของ JDBC (SQLException) และ RMI สำหรับ EJB (RemoteException) แทนที่จะระบุถึงภาระผูกพันที่แน่นอนตามแนวคิด“ การตรวจสอบยกเว้น” ดั้งเดิมปัญหาบังคับความน่าเชื่อถือเชิงระบบที่แพร่หลายเหล่านี้ไม่สามารถแก้ไขได้จริงที่จะประกาศอย่างกว้างขวาง

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

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

สุดท้าย "การตรวจสอบข้อยกเว้น" ค่อนข้างเข้ากันไม่ได้กับการตั้งโปรแกรมการทำงานของ FP

การยืนหยัดของพวกเขาใน "การจัดการทันที" เป็นสิ่งที่ขัดแย้งกับทั้ง "จับสาย" การปฏิบัติที่ดีที่สุดยกเว้นการจัดการและโครงสร้าง FP ใด ๆ ที่บทคัดย่อวนรอบ / หรือการไหลของการควบคุม

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

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

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

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

ดู:
- http://literatejava.com/exceptions/checked-exceptions-javas-biggest-mistake/


1
ประเด็นของฉันคือการล้มเหลวอย่างถูกต้องตามกลยุทธ์ทั่วไป ข้อยกเว้นที่ไม่ได้ตรวจสอบช่วยในขณะที่พวกเขาไม่ได้บังคับให้บล็อก catch ถูก interposed จับข้อผิดพลาดและการเข้าสู่ระบบแล้วสามารถด้านซ้ายไปยังรถขนชั้นนอกสุดไม่กี่มากกว่าพัน miscoded ครั้งตลอด codebase (ซึ่งเป็นจริงสิ่งที่ซ่อนข้อบกพร่อง) สำหรับความล้มเหลวโดยพลการข้อยกเว้นที่ไม่ได้ตรวจสอบนั้นถูกต้องที่สุด ภาระผูกพัน - ผลลัพธ์ที่คาดการณ์ได้เช่นเงินทุนไม่เพียงพอ - เป็นข้อยกเว้นเพียงข้อเดียวที่ถูกต้องตามกฎหมายเท่านั้นที่ควรตรวจสอบ
โทมัส W

1
คำตอบของฉันอยู่เหนือสิ่งนี้แล้ว อันดับแรกและสำคัญที่สุด1) ตัวจัดการความล้มเหลวด้านนอกควรจับทุกอย่าง นอกเหนือจากนั้นสำหรับไซต์ที่ระบุเฉพาะเท่านั้น2) ภาระผูกพันที่คาดหวังเฉพาะสามารถจับและจัดการได้ นั่นหมายความว่าไม่พบไฟล์เงินทุนไม่เพียงพอ ฯลฯ ณ จุดที่สามารถกู้คืนได้ - ไม่สูงกว่า หลักการห่อหุ้มหมายความว่าชั้นนอกไม่สามารถ / ไม่ควรรับผิดชอบในการทำความเข้าใจ / กู้คืนจากความล้มเหลวในส่วนลึก ประการที่สาม3) ทุกอย่างอื่นควรถูกโยนออกไปข้างนอก - ไม่เลือกถ้าเป็นไปได้
Thomas W

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

1
ไม่ถูกต้อง งานของตัวจัดการนอกสุดคือการล้มเหลวอย่างสมบูรณ์ & บันทึกข้อผิดพลาดในขอบเขต 'คำขอ' คำขอที่เสียหายไม่ทำงานอย่างถูกต้อง, มีรายงานข้อผิดพลาด, เธรดสามารถให้บริการคำขอถัดไปได้ ตัวจัดการนอกสุดนั้นเป็นคุณสมบัติมาตรฐานในตู้คอนเทนเนอร์ Tomcat, AWT, Spring, EJB และเธรด 'main' ของ Java
โทมัส W

1
ทำไมการรายงาน "บั๊กของแท้" จึงเป็นเรื่องอันตรายที่ขอบเขตการร้องขอหรือตัวจัดการนอกสุด ??? ฉันทำงานเป็นประจำในการรวมระบบและความน่าเชื่อถือซึ่งความน่าเชื่อถือทางวิศวกรรมที่ถูกต้องมีความสำคัญจริง ๆ และใช้ "ข้อยกเว้นที่ไม่ จำกัด " เพื่อดำเนินการดังกล่าว ฉันไม่แน่ใจว่าสิ่งที่คุณกำลังถกเถียงกันจริง - ดูเหมือนว่าคุณอาจต้องการใช้เวลา 3 เดือนในวิธีการยกเว้นที่ไม่ถูกตรวจสอบรับความรู้สึกและจากนั้นบางทีเราสามารถพูดคุยเพิ่มเติมได้ ขอบคุณ
โทมัส W

4

ในฐานะที่เป็นคนได้ระบุไว้แล้วไม่มีข้อยกเว้นตรวจสอบอยู่ใน Java bytecode มันเป็นเพียงกลไกคอมไพเลอร์ไม่เหมือนกับการตรวจสอบไวยากรณ์อื่น ๆ if(true) { a; } b;ผมเห็นการตรวจสอบข้อยกเว้นจำนวนมากเช่นผมเห็นคอมไพเลอร์บ่นเกี่ยวกับการซ้ำซ้อนเงื่อนไข: มีประโยชน์ แต่ฉันอาจทำแบบนี้โดยเจตนาดังนั้นขอให้ฉันเพิกเฉยต่อคำเตือนของคุณ

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

แก้ไขโปรแกรมที่ไม่ดีออกไป! อย่าพยายามแก้ไขภาษาเพื่อไม่อนุญาต! สำหรับคนส่วนใหญ่การ "ทำอะไรบางอย่างเกี่ยวกับข้อยกเว้น" เป็นเพียงการบอกผู้ใช้เกี่ยวกับเรื่องนี้ ฉันสามารถบอกผู้ใช้เกี่ยวกับข้อยกเว้นที่ไม่ได้ตรวจสอบได้เช่นกันดังนั้นให้ยกเว้นคลาสการตรวจสอบของคุณจาก API ของฉัน


ใช่ฉันแค่ต้องการเน้นความแตกต่างระหว่างโค้ดที่เข้าไม่ถึง (ที่สร้างข้อผิดพลาด) และเงื่อนไขกับผลลัพธ์ที่สามารถคาดเดาได้ ฉันจะลบความคิดเห็นนี้ในภายหลัง
Holger

3

ปัญหาเกี่ยวกับข้อยกเว้นที่ถูกตรวจสอบคือข้อยกเว้นมักจะเชื่อมต่อกับวิธีการของอินเทอร์เฟซแม้ว่าจะมีการใช้งานอินเทอร์เฟซนั้นเพียงครั้งเดียวก็ตาม

ปัญหาอื่นที่มีข้อยกเว้นที่ตรวจสอบคือพวกเขามักจะถูกนำไปใช้ในทางที่ผิด ตัวอย่างที่สมบูรณ์แบบนี้อยู่ในjava.sql.Connection's close()วิธี มันอาจส่งSQLExceptionถึงแม้ว่าคุณได้ระบุไว้อย่างชัดเจนว่าคุณได้ทำการเชื่อมต่อแล้ว ข้อมูลใดที่สามารถปิด () บอกได้ว่าคุณสนใจอะไร

โดยปกติเมื่อฉันปิด () การเชื่อมต่อ*จะมีลักษณะดังนี้:

try {
    conn.close();
} catch (SQLException ex) {
    // Do nothing
}

นอกจากนี้อย่าให้ฉันเริ่มต้นกับวิธีการวิเคราะห์คำแบบต่าง ๆ และ NumberFormatException ... ของ TryParse ของ. NET ซึ่งไม่ส่งข้อยกเว้นเป็นเรื่องง่ายมากที่จะใช้มันเจ็บปวดมากที่ต้องกลับไปที่ Java (เราใช้ทั้ง Java และ C # ที่ฉันทำงาน)

*ในฐานะที่เป็นความคิดเห็นเพิ่มเติม Connection.close ของ PooledConnection () ไม่ได้ปิดการเชื่อมต่อ แต่คุณยังต้องจับ SQLException เนื่องจากเป็นข้อยกเว้นที่ตรวจสอบ


2
ถูกต้องไดรเวอร์ใด ๆ สามารถ ... คำถามค่อนข้าง "ทำไมโปรแกรมเมอร์ควรดูแล?" เพราะเขาเข้าถึงฐานข้อมูลเสร็จแล้ว เอกสารยังเตือนคุณว่าคุณควรกระทำ () หรือย้อนกลับ () ธุรกรรมปัจจุบันก่อนที่จะโทรปิด ()
Powerlord

หลายคนคิดว่าการปิดไฟล์ไม่สามารถทิ้งข้อยกเว้น ... stackoverflow.com/questions/588546/…คุณแน่ใจ 100% ว่าไม่มีกรณีใดที่จะเป็นปัญหาหรือไม่
TofuBeer

ฉันมั่นใจ 100% ว่าไม่มีกรณีที่จะสำคัญและผู้โทรจะไม่ลอง / จับ
Martin

1
ตัวอย่างที่ยอดเยี่ยมกับการเชื่อมต่อปิดมาร์ติน! ฉันสามารถใช้ถ้อยคำใหม่ให้กับคุณเท่านั้น: หากเราเพิ่งระบุอย่างชัดเจนว่าเราได้ทำการเชื่อมต่อแล้วเหตุใดจึงควรกังวลว่าเกิดอะไรขึ้นเมื่อเราปิดมัน มีอีกหลายกรณีเช่นนี้ที่โปรแกรมเมอร์ไม่สนใจจริง ๆ ว่ามีข้อยกเว้นเกิดขึ้นและเขาพูดถูก
Piotr Sobczyk

1
@PiotrSobczyk: ไดรเวอร์ SQL บางตัวอาจจะบีบรัดหากมีการปิดการเชื่อมต่อหลังจากเริ่มทำธุรกรรม แต่ไม่ยืนยันหรือไม่หมุนกลับ IMHO การ squawking นั้นดีกว่าการเพิกเฉยต่อปัญหาอย่างเงียบ ๆ อย่างน้อยในกรณีที่การ squawking จะไม่ทำให้เกิดข้อยกเว้นอื่น ๆ ให้หายไป
supercat

3

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

ประโยชน์ที่บางเฉียบนั้นมีมากกว่าเมื่อเทียบกับค่าใช้จ่ายที่หนักหน่วง (โดยเฉพาะอย่างยิ่งในฐานรหัสที่ใหญ่กว่าและมีความยืดหยุ่นน้อยกว่า

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


2

ฉันคิดว่านี่เป็นคำถามที่ยอดเยี่ยมและไม่ได้โต้แย้งเลย ฉันคิดว่าห้องสมุดของบุคคลที่สามควร (โดยทั่วไป) โยนข้อยกเว้นที่ไม่ได้ตรวจสอบ ซึ่งหมายความว่าคุณสามารถแยกการอ้างอิงของคุณในห้องสมุด (เช่นคุณจะได้ไม่ต้องทั้งเรื่องการโยนข้อยกเว้นของพวกเขาหรือโยนException- การปฏิบัติมักจะไม่ดี) เลเยอร์ DAOของ Spring เป็นตัวอย่างที่ยอดเยี่ยมของสิ่งนี้

บนมืออื่น ๆ ยกเว้นจากแกน Java API ควรในปี พ.ศ. ทั่วไปตรวจสอบว่าพวกเขาจะเคยได้รับการจัดการ ใช้FileNotFoundExceptionหรือ InterruptedException(ที่ชื่นชอบของฉัน) เงื่อนไขเหล่านี้ควรได้รับการจัดการเป็นพิเศษเสมอ (เช่นปฏิกิริยาของคุณต่อ a InterruptedExceptionไม่เหมือนกับปฏิกิริยาของคุณต่อ a IllegalArgumentException) ความจริงที่ว่าข้อยกเว้นของคุณได้รับการตรวจสอบแล้วทำให้นักพัฒนาคิดว่าเงื่อนไขนั้นสามารถจัดการได้หรือไม่ (ที่กล่าวว่าฉันเห็นไม่ค่อยInterruptedExceptionจัดการอย่างถูกต้อง!)

อีกสิ่งหนึ่ง - RuntimeExceptionไม่เสมอไป "ที่นักพัฒนาซอฟต์แวร์ผิดพลาด" มีข้อผิดพลาดการโต้แย้งที่ผิดกฎหมายเกิดขึ้นเมื่อคุณลองและสร้างการenumใช้งานvalueOfและไม่มีenumชื่อนั้น สิ่งนี้ไม่ได้เป็นความผิดพลาดของผู้พัฒนา!


1
ใช่มันเป็นความผิดพลาดของผู้พัฒนา เห็นได้ชัดว่าพวกเขาไม่ได้ใช้ชื่อที่ถูกต้องดังนั้นพวกเขาจึงต้องกลับไปแก้ไขรหัสของพวกเขา
AxiomaticNexus

@AxiomaticNexus ไม่มีผู้พัฒนาที่มีสติใช้enumชื่อสมาชิกเพียงเพราะพวกเขาใช้enumวัตถุแทน ดังนั้นชื่อที่ผิดสามารถมาจากภายนอกไม่ว่าจะเป็นไฟล์นำเข้าหรืออะไรก็ตาม วิธีหนึ่งที่เป็นไปได้ในการจัดการกับชื่อดังกล่าวคือการโทรหาMyEnum#valueOfและตรวจสอบ IAE อีกวิธีหนึ่งคือใช้แบบเติมข้อมูลล่วงหน้าMap<String, MyEnum>แต่สิ่งเหล่านี้เป็นรายละเอียดการใช้งาน
maaartinus

@maaartinus มีหลายกรณีที่ชื่อสมาชิก enum ถูกใช้โดยไม่มีสตริงที่มาจากภายนอก ตัวอย่างเช่นเมื่อคุณต้องการวนรอบสมาชิกทั้งหมดแบบไดนามิกเพื่อทำบางสิ่งบางอย่างกับแต่ละคน นอกจากนี้ไม่ว่าสตริงนั้นมาจากภายนอกหรือไม่เกี่ยวข้อง ผู้พัฒนามีข้อมูลทั้งหมดที่พวกเขาต้องการเพื่อทราบว่าการส่งสตริง x ไปยัง "MyEnum # valueOf" จะส่งผลให้เกิดข้อผิดพลาดก่อนส่งหรือไม่ การส่งสตริง x ไปที่ "MyEnum # valueOf" อย่างไรก็ตามเมื่อมันจะทำให้เกิดข้อผิดพลาดจะเป็นความผิดพลาดอย่างชัดเจนในส่วนของนักพัฒนา
AxiomaticNexus

2

นี่คือหนึ่งอาร์กิวเมนต์กับข้อยกเว้นที่เลือก (จาก joelonsoftware.com):

เหตุผลก็คือฉันคิดว่าข้อยกเว้นนั้นไม่ดีไปกว่า "goto's" ซึ่งถือว่าเป็นอันตรายมาตั้งแต่ทศวรรษ 1960 โดยที่พวกเขาสร้างการกระโดดแบบฉับพลันจากจุดหนึ่งไปยังอีกจุดหนึ่ง ในความเป็นจริงพวกเขาแย่กว่าของ goto:

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

3
+1 คุณอาจต้องการสรุปการโต้แย้งในคำตอบของคุณ? พวกเขาเป็นเหมือน gotos ที่มองไม่เห็นและออกก่อนกำหนดสำหรับกิจวัตรของคุณกระจัดกระจายตลอดทั้งโปรแกรม
MarkJ

13
นั่นเป็นข้อโต้แย้งเพิ่มเติมเกี่ยวกับข้อยกเว้นโดยทั่วไป
Ionuț G. Stan

10
คุณอ่านบทความจริง ๆ !! ประการแรกเขาพูดเกี่ยวกับข้อยกเว้นโดยทั่วไปส่วนที่สอง "พวกเขาจะมองไม่เห็นในซอร์สโค้ด" ใช้เฉพาะกับข้อยกเว้น UNCHECKED นี้เป็นจุดรวมของการยกเว้นการตรวจสอบ ... เพื่อให้คุณรู้ว่าสิ่งที่รหัสพ่นอะไรที่ไหน
Newtopian

2
@Eva พวกเขาไม่เหมือนกัน ด้วยคำสั่ง goto คุณสามารถดูgotoคำหลักได้ ด้วยการวนซ้ำคุณจะเห็นวงเล็บปิดbreakหรือcontinueคำสำคัญหรือ พวกเขาทั้งหมดข้ามไปยังจุดหนึ่งในวิธีการปัจจุบัน แต่คุณไม่สามารถมองเห็นได้เสมอthrowเพราะบ่อยครั้งที่มันไม่ได้อยู่ในวิธีการปัจจุบัน แต่ในอีกวิธีหนึ่งที่เรียก (ทางอ้อม)
finnw

5
ฟังก์ชั่น @finnw เป็นรูปแบบของ goto คุณมักจะไม่ทราบว่าฟังก์ชั่นที่คุณใช้กำลังโทรอะไรอยู่ หากคุณตั้งโปรแกรมโดยไม่มีฟังก์ชั่นคุณจะไม่มีปัญหากับข้อยกเว้นที่มองไม่เห็น ซึ่งหมายความว่าปัญหาไม่ได้เชื่อมโยงกับข้อยกเว้นโดยเฉพาะและไม่ใช่ข้อโต้แย้งที่ถูกต้องกับข้อยกเว้นโดยทั่วไป คุณสามารถบอกว่ารหัสข้อผิดพลาดเร็วขึ้นคุณสามารถบอกว่า monads นั้นสะอาดกว่า แต่อาร์กิวเมนต์ goto นั้นโง่
Eva

2

สิ่งที่พิสูจน์ได้ว่าไม่จำเป็นต้องมีการตรวจสอบข้อยกเว้นคือ:

  1. เฟรมเวิร์กมากมายที่ใช้งานได้กับ Java เช่นเดียวกับ Spring ที่ตัดข้อยกเว้น JDBC ไปยังข้อยกเว้นที่ไม่ได้ทำเครื่องหมายการขว้างข้อความไปยังบันทึก
  2. ภาษาจำนวนมากที่มาหลังจาก java แม้จะอยู่ด้านบนสุดของแพลตฟอร์ม java - พวกเขาไม่ได้ใช้
  3. ตรวจสอบข้อยกเว้นมันเป็นคำทำนายที่ดีเกี่ยวกับวิธีที่ลูกค้าจะใช้รหัสที่ส่งข้อยกเว้น แต่นักพัฒนาที่เขียนรหัสนี้จะไม่เคยรู้เกี่ยวกับระบบและธุรกิจที่ลูกค้าของรหัสทำงานในตัวอย่างวิธี Interfcace ที่บังคับให้โยนข้อยกเว้นที่ตรวจสอบ มีการนำไปใช้มากกว่า 100 ระบบ 50 หรือ 90 ของการใช้งานไม่ทิ้งข้อยกเว้นนี้ แต่ลูกค้ายังคงต้องตรวจจับข้อยกเว้นนี้หากผู้ใช้อ้างอิงถึงส่วนต่อประสานนั้น การใช้งาน 50 หรือ 90 นั้นมีแนวโน้มที่จะจัดการกับข้อยกเว้นเหล่านั้นภายในตัวเองทำให้เกิดข้อยกเว้นในบันทึก (และนี่เป็นพฤติกรรมที่ดีสำหรับพวกเขา) เราควรทำอะไรกับสิ่งนั้น ฉันควรจะมีตรรกะพื้นหลังที่จะทำงานทั้งหมด - ส่งข้อความไปยังบันทึก และถ้าฉันในฐานะลูกค้าของรหัสจะรู้สึกว่าฉันต้องจัดการกับข้อยกเว้น - ฉันจะทำมัน
  4. อีกตัวอย่างหนึ่งเมื่อฉันทำงานกับ I / O ใน java มันบังคับให้ฉันตรวจสอบข้อยกเว้นทั้งหมดหากไฟล์ไม่มีอยู่หรือไม่ ฉันควรทำอย่างไรกับสิ่งนั้น หากไม่มีอยู่ระบบจะไม่ไปยังขั้นตอนถัดไป ลูกค้าของวิธีนี้จะไม่ได้รับเนื้อหาที่คาดหวังจากไฟล์นั้น - เขาสามารถจัดการ Runtime Exception มิฉะนั้นฉันควรตรวจสอบ Checked Exception ก่อนใส่ข้อความเพื่อเข้าสู่ระบบจากนั้นโยนข้อยกเว้นแบบฟอร์มออกวิธี ไม่ ... ไม่ - ฉันควรที่จะทำมันโดยอัตโนมัติด้วย RuntimeEception นั่นทำให้มัน / อัตโนมัติขึ้น ไม่มีความรู้สึกใด ๆ ที่จะจัดการด้วยตนเอง - ฉันยินดีที่ฉันเห็นข้อความแสดงข้อผิดพลาดในบันทึก (AOP สามารถช่วยได้ .. สิ่งที่แก้ไขจาวา) ในที่สุดหากฉันเชื่อว่าระบบควรแสดงข้อความป๊อปอัพต่อผู้ใช้ - ฉันจะแสดงข้อความนั้นไม่ใช่ปัญหา

ฉันมีความสุขถ้า java จะให้ทางเลือกแก่ฉันในการใช้งานเมื่อทำงานกับ core libs เช่น I / O กดไลค์ให้สองสำเนาของคลาสเดียวกัน - หนึ่งห่อด้วย RuntimeEception แล้วเราสามารถเปรียบเทียบสิ่งที่คนจะใช้ แต่สำหรับตอนนี้หลาย ๆ คนน่าจะเลือกใช้เฟรมเวิร์กบนจาวาหรือภาษาอื่น เช่นเดียวกับสกาล่า JRuby อะไรก็ตาม หลายคนเชื่อว่าอาทิตย์นั้นถูกต้อง


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

2

สิ่งสำคัญที่ไม่มีใครพูดถึงคือมันรบกวนการเชื่อมต่อและการแสดงออกแลมบ์ดาได้อย่างไร

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

ทุกอย่างดูโอเคจนกระทั่งคุณต้องการใช้งานอินเทอร์เฟซที่ไม่ใช่ของคุณ เห็นได้ชัดว่ามันไม่ได้ประกาศความตั้งใจที่จะโยนMyApExceptionดังนั้นคอมไพเลอร์ไม่อนุญาตให้คุณโยนข้อยกเว้นจากที่นั่น

อย่างไรก็ตามหากข้อยกเว้นของคุณขยายออกไปRuntimeExceptionจะไม่มีปัญหากับส่วนต่อประสาน คุณสามารถพูดถึงข้อยกเว้นใน JavaDoc ได้โดยสมัครใจหากคุณต้องการ แต่นอกเหนือไปจากนั้นมันก็แค่ไร้ฟองผ่านสิ่งใด ๆ ที่จะติดอยู่ในชั้นจัดการข้อยกเว้นของคุณ


1

เราได้เห็นการอ้างอิงถึงหัวหน้าสถาปนิกของ C #

ต่อไปนี้เป็นมุมมองทางเลือกจากผู้ใช้ Java เกี่ยวกับเวลาที่จะใช้ข้อยกเว้นที่ตรวจสอบ เขายอมรับว่ามีคนปฏิเสธหลายคนกล่าวถึง: ข้อยกเว้นที่มีประสิทธิภาพ


2
ปัญหาเกี่ยวกับข้อยกเว้นที่ตรวจสอบใน Java เกิดจากปัญหาที่ลึกกว่าซึ่งเป็นวิธีที่ข้อมูลมากเกินไปถูกห่อหุ้มใน TYPE ของข้อยกเว้นมากกว่าในคุณสมบัติของอินสแตนซ์ มันจะมีประโยชน์ที่จะตรวจสอบข้อยกเว้นถ้าเป็น "ตรวจสอบ" เป็นคุณลักษณะของการโยน / จับไซต์และถ้าใครสามารถระบุได้อย่างชัดเจนว่าข้อยกเว้นการตรวจสอบที่หนีบล็อกของรหัสควรจะยังคงเป็นข้อยกเว้นตรวจสอบหรือจะเห็น โดยบล็อกที่ล้อมรอบเป็นข้อยกเว้นที่ไม่ได้ตรวจสอบ บล็อก catch ในทำนองเดียวกันควรสามารถระบุได้ว่าพวกเขาต้องการเพียงข้อยกเว้นที่เลือก
supercat

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

0

ฉันได้อ่านมากเกี่ยวกับการจัดการข้อยกเว้นแม้ว่า (เกือบตลอดเวลา) ฉันไม่สามารถพูดได้จริง ๆ ว่าฉันมีความสุขหรือเศร้าเกี่ยวกับการมีอยู่ของข้อยกเว้นที่ตรวจสอบแล้วนี่เป็นของฉัน: การตรวจสอบข้อยกเว้นในรหัสระดับต่ำ (IO, เครือข่าย) , OS, ฯลฯ ) และข้อยกเว้นที่ไม่ได้ตรวจสอบใน API / แอปพลิเคชันระดับสูง

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

โครงการที่ฉันกำลังทำอยู่ใช้ห้องสมุดเป็นจำนวนมากและรวมเข้าด้วยกันภายใต้ API เดียวกัน API ซึ่งขึ้นอยู่กับข้อยกเว้นที่ไม่ได้ตรวจสอบกรอบงานนี้ให้ API ระดับสูงซึ่งในตอนแรกนั้นเต็มไปด้วยข้อยกเว้นที่ตรวจสอบแล้ว ข้อยกเว้น (เริ่มต้นยกเว้น ConfigurationException ฯลฯ ) และต้องบอกว่าเป็นไม่ได้เป็นอย่างที่เป็นมิตร เวลาส่วนใหญ่ที่คุณต้องจับหรือโยนข้อยกเว้นที่คุณไม่ทราบวิธีการจัดการหรือคุณไม่สนใจ (ไม่ต้องสับสนกับคุณควรละเว้นข้อยกเว้น) โดยเฉพาะอย่างยิ่งในฝั่งไคลเอ็นต์ที่เดียว คลิกอาจโยนข้อยกเว้นที่เป็นไปได้ 10 ข้อ (ทำเครื่องหมายไว้)

รุ่นปัจจุบัน (รุ่นที่ 3) ใช้ข้อยกเว้นที่ไม่ได้ตรวจสอบเท่านั้นและมีตัวจัดการข้อยกเว้นระดับโลกซึ่งรับผิดชอบการจัดการสิ่งใดที่ไม่ถูกตรวจจับ API จัดเตรียมวิธีการลงทะเบียนตัวจัดการข้อยกเว้นซึ่งจะตัดสินว่าข้อยกเว้นนั้นถือเป็นข้อผิดพลาด (ส่วนใหญ่แล้วเป็นกรณีนี้) ซึ่งหมายถึงบันทึก & แจ้งให้คนอื่นหรืออาจหมายถึงสิ่งอื่น - เช่นข้อยกเว้นนี้ AbortException ซึ่งหมายถึงแบ่งเธรดการเรียกใช้ปัจจุบันและไม่บันทึกข้อผิดพลาด 'ทำให้ไม่ต้องการทำเช่นนั้น แน่นอนว่าในการทำงานเธรดที่กำหนดเองทั้งหมดจะต้องจัดการเมธอด run () ด้วยการลอง {... } catch (all)

โมฆะสาธารณะวิ่ง () {

try {
     ... do something ...
} catch (Throwable throwable) {
     ApplicationContext.getExceptionService().handleException("Handle this exception", throwable);
}

}

สิ่งนี้ไม่จำเป็นถ้าคุณใช้ WorkerService เพื่อกำหนดเวลางาน (Runnable, Callable, Worker) ซึ่งจัดการทุกอย่างให้คุณ

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

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