靜態方法拋出運行時異常導致反射生成實例失敗
場景還原
1. 啓動時,static屬性獲取時便打印了異常,不過沒有注意到。
2. 之後在異常無法捕獲之處既無正常往下走到return,也沒有被Catch捕獲異常,甚至在該行無法Step in進入debug,最終被外層的finally塊處理。
生成實例失敗的類
public class InnerPositionThread extends MonitorThread {
Logger logger = LoggerFactory.getLogger(getClass());
private static Map<String, List<BlockField>> blockAttrCfg = BlockUtils.getBlocks("innerposition.xml");
}
拋出運行時異常的方法
public class BlockUtils {
public static Map<String, List<BlockField>> getBlocks(String templateName) {
try {
// do something
}
} catch (Exception var14) {
var14.printStackTrace();
throw new RuntimeException(var14);
}
}
無法捕獲異常之處
try {
Object monitor;
// 下面這行將會異常
monitor = (IfMonitor)Class.forName(mClass).newInstance();
return null;
} catch (Exception var4) {
log.error("啓動監視器失敗 " + config.getName() + " (" + monitorKey + ")", var4);
return getStartFalseResponse(config, monitorKey, isCreate ? "監視器已建立,激活失敗" : "啓動監視器失敗");
}
重試
另起工程簡化示例進行查看原因。
接口
public interface InterfaceA {
void functionA();
}
類
靜態屬性初始化拋出異常
public class ClassImplA implements InterfaceA {
private static Logger logger = LoggerFactory.getLogger(ClassImplA.class);
private static Long staticProperties = initStaticProperties();
private static Long initStaticProperties() {
throw new RuntimeException("ClassImplA throw runtime exception");
}
@Override
public void functionA() {
logger.info("function A be called!");
}
}
測試main方法
public static void main(String[] args) {
logger.info("start main");
Object classAImpl;
String className = ClassImplA.class.getName();
logger.info("start get class A");
try {
classAImpl = (InterfaceA)Class.forName(className).newInstance();
((InterfaceA)classAImpl).functionA();
} catch (Exception e) {
logger.error("catch exception反射失敗" + e.getMessage());
} catch (Error e) {
logger.error("catch error反射失敗" + e.getMessage());
} finally {
logger.info("in finally end get class A");
}
logger.info("end main");
}
測試輸出
六月 02, 2020 2:34:52 下午 forname.MainTest main
信息: start main
六月 02, 2020 2:34:52 下午 forname.MainTest main
信息: start get class A
六月 02, 2020 2:34:52 下午 forname.MainTest main
嚴重: catch error反射失敗null
六月 02, 2020 2:34:52 下午 forname.MainTest main
信息: in finally end get class A
六月 02, 2020 2:34:52 下午 forname.MainTest main
信息: end main
觀察捕獲到Error,而非Exception,打印堆棧可見,其最終拋出的錯誤是ExceptionInInitializerError
。
java.lang.ExceptionInInitializerError
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at forname.MainTest.main(MainTest.java:16)
Caused by: java.lang.RuntimeException: ClassImplA throw runtime exception
at forname.ClassImplA.initStaticProperties(ClassImplA.java:12)
at forname.ClassImplA.<clinit>(ClassImplA.java:9)
... 3 more
通過查看該類的繼承發現ExceptionInInitializerError
繼承LinkageError
,再繼承Error
,最終繼承Throwable
,所以該ExceptionInInitializerError
不會被Exception
的catch塊捕獲。
結論
通過請教他人得知:
- 這類Error必須幹掉,不能帶進程序,
- static屬性初始化也不允許拋出異常,
- catch塊也不能去抓取
error
或者Throwable
的錯誤。