靜態方法拋出運行時異常導致反射生成實例失敗

靜態方法拋出運行時異常導致反射生成實例失敗

場景還原

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塊捕獲。

結論

​ 通過請教他人得知:

  1. 這類Error必須幹掉,不能帶進程序,
  2. static屬性初始化也不允許拋出異常,
  3. catch塊也不能去抓取error或者Throwable的錯誤。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章