今天来介绍了两个陌生又熟悉的异常类,熟悉是因为我们经常会遇到它们,陌生是好像又从来不知道它们是做什么的

假定读者已经清楚了Java的异常分类:

  1. 一是程序不能处理的错误(Error),
  2. 二是程序应该避免而可以不去捕获的运行时异常(RuntimeException),
  3. 三是必须捕获的非运行时异常,也叫受检异常或必检异常

InvocationTargetException

反射操作过程中,调用目标类的方法可能抛出异常,可以使用Throwable接受,但是太宽泛了

InvocationTargetException就是为解决这个问题而设计的,当反射操作的目标方法中出现异常时,都统一包装成一个必检异常 InvocationTargetException

InvocationTargetException的target属性保存了原始的异常

package exception;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; public class ExceptionTest{ public static void makeInvocationTargetException() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException{ Class<ExceptionTest> exceptionTestClass = ExceptionTest.class;
Method throwExceptionMethod = exceptionTestClass.getMethod( "throwExceptionMethod" );
throwExceptionMethod.invoke( new ExceptionTest() );
} public void throwExceptionMethod(){ int i = 1 / 0;
} public static void main( String[] args ) throws Exception{ makeInvocationTargetException();
}
}

输出以下异常,可以看出首先是打印的是InvocationTargetException,后续接着打印了真正异常发生的位置

Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at exception.ExceptionTest.makeInvocationTargetException(ExceptionTest.java:12)
at exception.ExceptionTest.main(ExceptionTest.java:22)
Caused by: java.lang.ArithmeticException: / by zero
at exception.ExceptionTest.throwExceptionMethod(ExceptionTest.java:17)
... 6 more

UndeclaredThrowableException

如果子类中要重写父类中的方法,那么子类方法中抛出的必检异常必须是父类方法中声明过的类型。

代理类一般有两种实现方式:实现目标类相同的接口或者继续目标类

  • 如果代理类和被代理类实现了共同的接口,那代理类方法抛出的受检异常必须是共同接口中声明过的
  • 如果代理类是被代理类的子类,则代理类方法报错的受检异常必须是被代理类的方法中声明过的

可以代理类难免会抛出一些接口或者父类没有声明的受检异常,这时候不抛出,它是受检异常,必须抛出;抛出,没有声明

所以可以把这些受检异常包装为免检异常UndeclaredThrowableException抛出

package exception;

import java.io.IOException;

public interface IPerson{

	void sayHello() throws IOException;
}
package exception;

public class Person implements IPerson{

	@Override
public void sayHello(){ System.out.println( "Hello" );
}
}
package exception;

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths; public class PersonRecord implements InvocationHandler{ Object object; public PersonRecord( Object object ){ this.object = object;
} @Override
public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable{ System.out.println( "record start" );
Object rs = method.invoke( object, args );
System.out.println( "record end" );
throw new Exception();
}
}
package exception;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; public class UndeclaredExceptionTest{ public static void makeUndeclaredThrowableException() throws IOException{ IPerson o = (IPerson)Proxy.newProxyInstance( UndeclaredExceptionTest.class.getClassLoader(), new Class[]{ IPerson.class },
new PersonRecord(new Person()) );
o.sayHello();
} public static void main( String[] args ) throws Exception{ makeUndeclaredThrowableException();
}
}

会有以下输出

record start
Hello
record end
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
at com.sun.proxy.$Proxy0.sayHello(Unknown Source)
at exception.UndeclaredExceptionTest.makeUndeclaredThrowableException(UndeclaredExceptionTest.java:14)
at exception.UndeclaredExceptionTest.main(UndeclaredExceptionTest.java:21)
Caused by: java.lang.Exception
at exception.PersonRecord.invoke(PersonRecord.java:25)
... 3 more

同时发生

这两个异常可以同时发现,也就是在代理类中使用了反射操作,然后异常被包装为InvocationTargetException,而InvocationTargetException没有被接口或者父类声明过,于是又被保证为UndeclaredThrowableException

我们将Person的sayHello方法改一下:

package exception;

public class Person implements IPerson{

	@Override
public void sayHello(){ System.out.println( "Hello" );
int i = 1/0;
}
}

这样就会同时触发这两种异常

record start
Hello
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
at com.sun.proxy.$Proxy0.sayHello(Unknown Source)
at exception.UndeclaredExceptionTest.makeUndeclaredThrowableException(UndeclaredExceptionTest.java:14)
at exception.UndeclaredExceptionTest.main(UndeclaredExceptionTest.java:21)
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at exception.PersonRecord.invoke(PersonRecord.java:23)
... 3 more
Caused by: java.lang.ArithmeticException: / by zero
at exception.Person.sayHello(Person.java:9)
... 8 more

总结

  1. InvocationTargetException是在使用反射中发生的异常,包裹了原始的异常
  2. UndeclaredThrowableException是在代理中发生的异常,包裹了原始的异常

在工作的过程中,会经常的遇到这两个异常,以前只是选择性的把它忽略了,在我们了解了他们的组成和原理之后可以更好的找到原始异常

下面是一个Mybatis的工具类,目的是从包装异常类中找到原始的异常:

/**
* Copyright 2009-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.reflection; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException; /**
* @author Clinton Begin
*/
public class ExceptionUtil { private ExceptionUtil() {
// Prevent Instantiation
} /**
* 拆解InvocationTargetException和UndeclaredThrowableException异常的包装,从而得到被包装的真正异常
* @param wrapped 包装后的异常
* @return 拆解出的被包装异常
*/
public static Throwable unwrapThrowable(Throwable wrapped) {
// 该变量用以存放拆包得到的异常
Throwable unwrapped = wrapped;
while (true) {
if (unwrapped instanceof InvocationTargetException) {
// 拆包获得内部异常
unwrapped = ((InvocationTargetException) unwrapped).getTargetException();
} else if (unwrapped instanceof UndeclaredThrowableException) {
// 拆包获得内部异常
unwrapped = ((UndeclaredThrowableException) unwrapped).getUndeclaredThrowable();
} else {
// 该异常无需拆包
return unwrapped;
}
}
} }

InvocationTargetException和UndeclaredThrowableException异常介绍的更多相关文章

  1. [C#] C# 知识回顾 - 异常介绍

    异常介绍 我们平时在写程序时,无意中(或技术不够),而导致程序运行时出现意外(或异常),对于这个问题, C# 有专门的异常处理程序. 异常处理所涉及到的关键字有 try.catch 和 finally ...

  2. Java_异常介绍

    今日内容介绍: 掌握异常概述 理解异常的基础操作以及最简单的捕获处理 理解多异常捕获处理 理解声明抛出异常 掌握自定义异常 掌握异常处理注意事项 异常 什么是异常?Java代码在运行时期发生的问题就是 ...

  3. PendSV异常介绍、用于上下文切换

    在这里,非常感谢<cortex-cm3权威指南>的翻译者. PendSV 的典型使用场合是在上下文切换时(在不同任务之间切换). 例如, 一个系统中有两个就绪的任务,上下文切换被触发的场合 ...

  4. Java Exception异常介绍

     一:介绍java异常       异常指不期而至的各种状况,如:文件找不到.网络连接失败.非法参数等.异常是一个事件,它发生在程序运行期间,干扰了正常的指令流程.Java通 过API中Throwab ...

  5. oracle 10g 学习之游标使用和异常介绍(11)

    一.游标 1. 使用游标 要求: 打印出 80 部门的所有的员工的工资: salary: xxx declare --1. 定义游标 cursor salary_cursor is select sa ...

  6. java中异常介绍

    一.异常概述 异常处理已经成为衡量一门语言是否成熟的标准之一,目前的主流编程语言如C++.C#.Ruby.Python等,大都提供了异常处理机制.增加了异常处理机制后的程序有更好的容错性,更加健壮. ...

  7. Java 异常介绍

    Java标准库内建了一些通用的异常,这些类以 Throwable 为顶层父类.Throwable又派生出 Error 类和 Exception 类. 错误:Error类以及他的子类的实例,代表了JVM ...

  8. 出现java.lang.reflect.UndeclaredThrowableException异常

    解决方案:1.看导进来的项目是否有中文路径.2.看是否有get.set方法没写.3.和部署的环境有关.比如,是否写了构造函数.EJB需要.

  9. 获取UndeclaredThrowableException异常信息

    一.堆栈错误信息如下,要获取红框里的message 说明:ValidationException为自定义异常,继承自Exception 二.代码如下

  10. Checked 和 UnChecked 异常 的使用场合

    异常的概念  任何的异常都是Throwable类(为何不是接口??),并且在它之下包含两个子类Error / Exception,而Error仅在当在Java虚拟机中发生动态连接失败或其它的定位失败的 ...

随机推荐

  1. WEB服务与NGINX(26)- 实现Nginx高并发系统内核参数优化

    1. 实现Nginx高并发系统内核参数优化 由于默认的Linux内核参数考虑的是最通用场景,这明显不符合用于支持高并发访问的Web服务器的定义,所以需要修改Linux内核参数,使得Nginx可以拥有更 ...

  2. 🔥httpsok-v1.11.0支持CDN证书自动部署

    httpsok-v1.11.0支持CDN证书自动部署 介绍 httpsok 是一个便捷的 HTTPS 证书自动续签工具,专为 Nginx .OpenResty 服务器设计.已服务众多中小企业,稳定.安 ...

  3. URP(Universal Render Pipeline)渲染管线在使用中的一些分享

    本篇文章整理了URP管线使用中的一些简单的心得记述 1.使用ScriptableRendererFeature自定义渲染特性 在内建(Build-in)管线中可以使用CommandBuffer并添加到 ...

  4. C数据结构:二叉树的基本操作

    二叉树 树基本知识 二叉树的性质 满二叉树 完全二叉树 性质4.5的解释 顺序存储结构(利用性质4.5) 链式存储结构 结点结构体 建立二叉树 先序遍历 中序遍历 后序遍历 层次遍历 复制二叉树 计算 ...

  5. C语言:输入一串字符串,统计字符串中有多少个数字

    gets函数会在输入完字符后自动补上一个\0,所以用这个特性可以计算出字符串是否结束. 因为数字在字符中对应的ascii码就是0~9,只要遇到小于9的字符就是数字,所以计数器加一 #include&l ...

  6. docker 修改运行容器环境变量,如何修改容器中的环境变量env使长期有效

    @ 目录 前言 第一步:查看Docker Root目录 第二步:查到容器的长id(container id) 第三步:停止容器 第四步:编辑修改环境变量env 第五步:重载服务的配置文件 第六步:重启 ...

  7. 【wordpress开发必备】新增必填字段相关函数和钩子,适用6.1版本

    当表单包含多个必填字段时,它们的标签可能带有一个带有图例的星号,以说明这些字段是必填的.为了减少代码重复并帮助维护全局一致的标记,WordPress 有两个新函数:wp_required_field_ ...

  8. Android 13 - Media框架(28)- MediaCodec(三)

    关注公众号免费阅读全文,进入音视频开发技术分享群! 上一节我们了解到 ACodec 执行完 start 流程后,会把所有的 input buffer 都提交给 MediaCodec 层,MediaCo ...

  9. 一个 .NET 开源的地图组件库 - Mapsui

    前言 今天大姚给大家分享一个.NET开源(MIT License).免费.同时支持多平台框架(MAUI.WPF.Avalonia.Uno.Blazor.WinUI.Eto..NET Android 和 ...

  10. MyBatis完成CRUD 详细细节内容

    1. MyBatis完成CRUD 详细细节内容 @ 目录 1. MyBatis完成CRUD 详细细节内容 每博一文案 2. MyBatis工具类SqlSessionUtil的封装 3. 准备工作 3. ...