什么是UndeclaredThrowableException异常

学习java的老铁们应该都知道,在java中子类方法不能抛出被父类更广泛的异常,意思就是说,如果在父类或者接口的方法签名中声明了,该方法可以抛出A异常,那么子类或者实现类在重写这个方法的时候,只能抛出异常A或者A的子类才可以,而且这个规则的验证,已经放到了java编译器中了,如果在代码编写的过程中违反了这个规则,会出现编译错误。

不知道各位老铁有没有想过,java为什么会有这样的要求?这是因为在面向对象编程范式中,要求我们面向抽象而非具体编程。所在在编写方法调用的客户端程序时,调用的方法是定义在父类或者接口中,那么此时对异常的处理也是面向父类方法中抛出的异常来的,如果子类在实现方法时,抛出了比父类更广泛异常的话,那么在运行期,客户端程序的异常处理逻辑就失效了,这显然是不符合我们预期的。

了解了java中的这个异常处理规则后,我么再来看看UndeclaredThrowableException 这个异常,看起来是不是很陌生,不过从名字中可以大概看出他的含义:未声明的Throwable异常,就是抛出了未声明的异常。这个异常描述的就是子类方法抛出了比父类方法声明异常更广泛的异常。但是上面我们说到,在代码编写的时候编译器会帮助我们进行检查,来避免这类情况的出现,所以一般情况下,我们基本上见不到这个异常类,这也是我们对这个异常很陌生的原因。那么到底什么情况下抛出这个异常呢?

为什么会抛出这个异常

上面我们说到了抛出这个异常的原因,也说了因为编译器的帮助,这种异常也很少会产生,那么这种异常产生场景是什么呢?
我们知道,编译器的检查是在编译期,如果在运行期出现了违反上面所说的规则的话,如动态代理生成的代理对象,对于这种情况编译器就无能为力了,那么在使用动态代理场景下,是如何触发这个异常的呢?下面我们复现一下。

异常复现

以jdk动态代理为例,jdk中动态代理的实现方式是生成一个和被代理对象实现相同接口的一个实现类,然后在这个实现类中对被代理对象进行增强。具体细节看下面代码:

interface Flyable {void fly() throws IOException;}static class Bired implements Flyable {@Overridepublic void fly() throws IOException {}}static class Handler implements InvocationHandler {private Object target;public Handler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(String.format("before invoke %s", method.getName()));throw new Exception("throw checked exception");}}public static void main(String[] args) {// 将生成的代理类的class文件,写入磁盘System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");Class birdClazz = Bired.class;Bired bired = new Bired();Flyable flyable = (Flyable) Proxy.newProxyInstance(birdClazz.getClassLoader(), birdClazz.getInterfaces(),new Handler(bired));try {flyable.fly();} catch (IOException e) {e.printStackTrace();}}

上面的代码是使用jdk动态代理的经典范式,接口Flyable的fly方法声明了可能抛出IOException,所以在main方法调用的时候,就对IOException进行了捕获处理,但是生成的代理对象的fly方法会抛出Exception异常,也就是比IOException更广泛的异常。那么此时执行main方法就会抛出 UndeclaredThrowableException。

执行结果如下图:

看到这里你可能会很奇怪,代理类的方法抛出的异常明明是Exception,那么为什么我们应用程序中,收到的却是UndeclaredThrowableException呢?有问题看"源码",这里的源码并不是我们写的这些java代码,而是jvm执行的字节码,看看生成的代理类是如何抛出 UndeclaredThrowableException的。

细心的小伙伴已经发现了,我在main方中配置的一个property:

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

这段代码就是将生成的代理类的字节码写到磁盘,代理类的名称 $Proxy0.class,使用反编译工具查看一下具体内容:

public final void fly() throws IOException {try {super.h.invoke(this, m3, (Object[])null);} catch (RuntimeException | IOException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}

其中这段代码是抛出 UndeclaredThrowableException的关键地方:这里只对RuntimeException,Error,IOException三种异常进行了处理,如果抛出了这三种异常之外的异常,那么异常就会被包装成 UndeclaredThrowableException 抛出去。看到这里应该就知道 UndeclaredThrowableException是怎么抛出来的了吧。

总结来说就是:如果抛出RuntimeExcepton,Error和声明的异常以及其子类外的其他异常,都会被统一转换成 UndeclaredThrowableException 进行抛出,这也是实现“子类不能抛出比父类更广泛异常"的规则。

更详细的内容可以参考 :https://docs.oracle.com/javase/7/docs/api/java/lang/reflect/UndeclaredThrowableException.html

解决方案

通过抛出异常的堆栈可以看出,当转换成了UndeclaredThrowableException后,十分不利于异常问题的排查。那么对于这个问题该怎么解决呢? 知道了问题发生的原因,其实问题也就比较容易解决了,常用的方法有以下几种:
1.将可能抛出声明异常外的其他异常的地方,进行异常捕获处理,避免此类异常的抛出。
2.在InvocationHandler的 invoke方法中进行异常捕获,然后统一抛出运行时异常。

一文搞懂 UndeclaredThrowableException相关推荐

  1. 一文搞懂RNN(循环神经网络)

    基础篇|一文搞懂RNN(循环神经网络) https://mp.weixin.qq.com/s/va1gmavl2ZESgnM7biORQg 神经网络基础 神经网络可以当做是能够拟合任意函数的黑盒子,只 ...

  2. 一文搞懂 Python 的 import 机制

    一.前言 希望能够让读者一文搞懂 Python 的 import 机制 1.什么是 import 机制? 通常来讲,在一段 Python 代码中去执行引用另一个模块中的代码,就需要使用 Python ...

  3. python语言语句快的标记是什么_一文搞懂Python程序语句

    原标题:一文搞懂Python程序语句 程序流 Python 程序中常用的基本数据类型,包括: 内置的数值数据类型 Tuple 容器类型 String 容器类型 List 容器类型 自然的顺序是从页面或 ...

  4. 一文搞懂 Java 线程中断

    转载自   一文搞懂 Java 线程中断 在之前的一文<如何"优雅"地终止一个线程>中详细说明了 stop 终止线程的坏处及如何优雅地终止线程,那么还有别的可以终止线程 ...

  5. 一文搞懂HMM(隐马尔可夫模型)-Viterbi algorithm

    ***一文搞懂HMM(隐马尔可夫模型)*** 简单来说,熵是表示物质系统状态的一种度量,用它老表征系统的无序程度.熵越大,系统越无序,意味着系统结构和运动的不确定和无规则:反之,,熵越小,系统越有序, ...

  6. 一文搞懂如何使用Node.js进行TCP网络通信

    摘要: 网络是通信互联的基础,Node.js提供了net.http.dgram等模块,分别用来实现TCP.HTTP.UDP的通信,本文主要对使用Node.js的TCP通信部份进行实践记录. 本文分享自 ...

  7. 【UE·蓝图底层篇】一文搞懂NativeClass、GeneratedClass、BlueprintClass、ParentClass

    本文将对蓝图类UBlueprint的几个UClass成员变量NativeClass.GeneratedClass.BlueprintClass.ParentClass进行比较深入的讲解,看完之后对蓝图 ...

  8. 一文搞懂AWS EC2, IGW, RT, NAT, SG 基础篇下

    B站实操视频更新 跟着拉面学习AWS--EC2, IGW, RT, NAT, SG 简介 长文多图预警,看结论可以直接拖到"总结"部分 本文承接上一篇文章介绍以下 AWS 基础概念 ...

  9. 一文搞懂CAN FD总线协议帧格式

    目录 1.为什么会出现CAN FD? 2.什么是CAN FD? 3.CAN FD和CAN总线协议帧异同 4.解析CAN FD帧结构 4.1.帧起始 4.2.仲裁段 4.3.控制段 4.4.数据段 4. ...

  10. 一文搞懂 Traefik2.1 的使用

    原文链接:一文搞懂 Traefik2.1 的使用 一文搞懂 Traefik2.1 的使用 核心概念 安装 ACME 中间件 灰度发布 流量复制 TCP 简单 TCP 服务 带 TLS 证书的 TCP ...

最新文章

  1. anaconda与python的idle冲突吗_anaconda(python3)与python2共存
  2. Android 三角形控件
  3. Request库的安装与使用
  4. Android开发中如何使用RecyclerView
  5. 排队机叫号系统服务器安装设置,易达办事大厅排队叫号系统
  6. 防火墙(3)——iptables(1)
  7. linux下面的安卓模拟器genymotion运行taptap游戏-还没弄完
  8. 我的iOS学习历程 - OC第九天
  9. 游戏中的“战争黑雾”和现实中的程序员处境
  10. 企业可视化大屏如何搭建
  11. CSDN VIP文章
  12. 米家扫地机器人按键没反应_小米扫地机系统重置键在哪?
  13. 卡巴斯基 7.0 免费激活码使用方法!
  14. 解空间树及其相关算法
  15. Linux终端收听mms流媒体电台
  16. 电子基础知识:测判三极管的口诀
  17. 金蝶云苍穹笔记(三)
  18. 烤仔的朋友们 | 从薯片到卫生纸,进入NFT市场的10大传统品牌大盘点
  19. 新技术又又又又叒叒来了?
  20. 使用腾讯云clb实现https转发

热门文章

  1. Access-Control-Allow-Origin跨域
  2. Qt创建桌面快捷方式
  3. windows逆向工程学习
  4. Mac命令行上传代码至GitHub
  5. 星空特效HTML代码,HTML5特效库 HTML5+JS全屏星空特效源码
  6. VMware虚拟机启动失败问题解决WMware-tray.exe无法正常启动
  7. IMX8MQ MEK 开发板安卓 8.1-2.0.0 环境搭建过程记录
  8. 水晶报表 php,VS2015环境下Crystal Reports(水晶报表)的安装使用
  9. memory repair
  10. JZOJ 6841. 【2020.11.5提高组模拟】淘淘蓝蓝之树林(凸包+最短路)