運行輸出包含兩次完整堆棧跟蹤,且最後顯示 Inside wq(),e.printStackTrace(): 及 Caught in main,e.printStackTrace():,乍看像「報錯」,實則程序正常終結。
main 調用 wq()wq() 調用 rt()rt() 拋出 new Exception("thrown from rt()")wq() 捕獲,打印堆棧(第一次),throw e; 重拋 同一異常對象main 捕獲,打印堆棧(第二次),catch 塊結束main 方法正常返回,JVM 退出,無未捕獲異常throw e vs throw new Exception(e)| 寫法 | 堆棧跟蹤 | 適用場景 |
|---|---|---|
throw e; | 保留原始堆棧(調試首選) | 單純透傳,不增加業務信息 |
throw new Exception("msg", e); | 新異常堆棧從此處開始,e 成為 cause(鏈式異常) | 包裝業務異常,隱藏底層細節 |
代碼使用 throw e;,故兩次 printStackTrace() 輸出完全一致,均指向 rt():6 原始拋出點。
wq() 拋出 Throwable 而 main 只 catch (Exception) 卻不報錯?Exception 是 Throwable 子類,捕獲 Exception 可捕獲所有 Exception 及其子類實例。rt() 拋出 Exception,wq() 重拋同對象,類型仍為 Exception,main 的 catch (Exception) 完全匹配,無未處理異常。rt() 拋出 Error 或非 Exception 的 Throwable,則會穿透 main 導致 JVM 打印未捕獲異常並退出。e.printStackTrace() 觀察流程;log.error("msg", e),避免污染標準輸出;throw new BizException("業務含義", e),保留原始原因便於排查;throws Exception 而非 Throwable,除非極少數框架級代碼需捕獲 Error。代碼邏輯完全正確,雙重堆棧輸出是 throw e 保留原始堆棧特性導致,非運行錯誤。理解「重拋同一對象保留堆棧」與「包裝新異常形成鏈」的區別,是 Java 異常處理進階的關鍵。