静态方法抛出运行时异常导致反射生成实例失败

静态方法抛出运行时异常导致反射生成实例失败

场景还原

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的错误。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章