动态加载未实现接口方法的类时无异常抛出的原因探究
问题描述
我偶然发现一个完全出乎意料的有趣现象:当通过URLClassLoader动态加载一个类,实例化后将其强制转换为某个接口(该加载类实现的是旧版本接口,而加载方使用的是新版本接口),调用接口中新增的未实现方法时,居然不会抛出任何异常。
举个例子:编写一个实现某接口的类XYZ,编写及编译该类时,接口仅包含fun()方法。编译完成后,将该类打包成jar包(不含接口文件)。随后修改该接口,新增otherFun()方法声明。此时,使用包含新增方法的新版本接口的类,调用动态加载并实例化的该类的otherFun()方法,编译是合法的,但按常理运行时应该抛出异常——结果却不会抛出任何异常,代码直接停止执行并跳转到finally块。
这是为什么?此时实际发生了什么?
代码示例
打包jar时的旧版本接口
interface MyInterface{ public void fun(); }
打包jar后的新版本接口
interface MyInterface{ public void fun(); public void otherFun(); }
动态加载代码
try{ File jarFile = new File(jarPath); // 该路径下的jar包含实现旧版本MyInterface的类 URLClassLoader child = new URLClassLoader( new URL[] {jarFile.toURI().toURL()}, this.getClass().getClassLoader() ); Class<?> rawClass = Class.forName(name, true, child); Class<? extends MyInterface> explicitClass = (Class<? extends MyInterface>)rawClass; Constructor<? extends MyInterface> constructor = explicitClass.getConstructor(); MyInterface instance = constructor.newInstance(); instance.fun(); // 正常执行 instance.otherFun(); // 无任何操作,直接跳转到finally块 System.out.println("I am the unreachable code block, due to the unimplemented method call above."); }catch(Exception e){ System.out.println("I am also an unreachable code block, due to the straight jump to the finally block."); } finally { System.out.println("I'm done without raising an exception."); }
原因分析
你看到的现象本质是JVM抛出了AbstractMethodError,但你的代码没有捕获它,具体逻辑如下:
- 当调用接口新增的
otherFun()方法时,JVM发现实例对应的类并没有实现这个方法,会抛出AbstractMethodError——这是一个Error类型,而非Exception类型。 - 你的
catch块只捕获Exception及其子类,对Error无效,所以异常不会进入catch块。 - 根据Java的异常处理规则,当未捕获的
Error发生时,JVM会先执行当前try块对应的finally代码,之后再终止线程(或者整个程序,取决于是否是主线程)。 - 这就造成了“代码直接跳转到finally块”的错觉,实际上是
Error触发了finally的执行,而非没有异常发生。
补充说明:
- Java的
Error属于严重系统级错误,通常不建议捕获,JVM默认会在执行完finally后终止程序。 - 这种场景是典型的接口版本不兼容问题,在模块化开发中可以通过版本管理避免。
内容的提问来源于stack exchange,提问作者roediGERhard
相关产品推荐
相关产品推荐

