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

假定读者已经清楚了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. JDK源码阅读-------自学笔记(六)(java.util.Arrays用法和描述浅析)

    工具类的使用 要多使用这个类,同时,很多算法包含其中,常见操作排序.查找.填充.打印等. 1.打印数组 ------Arrays.toString() 1 // 整型初始化 2 int[] integ ...

  2. 用 C 语言开发一门编程语言 — 抽象语法树

    目录 文章目录 目录 前文列表 抽象语法树的结构 使用递归来遍历树结构 实现求值计算 抽象语法树与行为树 前文列表 <用 C 语言开发一门编程语言 - 交互式解析器l> <用 C 语 ...

  3. pageoffice 6 实现数据区域填充(插入文本、图片、word、excel等)

    在实际的Word文档开发中,经常需要自动填充数据到Word模板中,以生成动态的Word文档. 例如: 1.我们可以根据数据库表中已保存的个人信息,设计好一个简历模板docx文件,然后通过代码将这些个人 ...

  4. layui表格内可编辑下拉框

    表格内可编辑下拉框扩展自别人的表格内下拉框 一.列模板,这是列配置的templet字段需要使用的. 1.inputdiv,输入框覆盖在下拉框上面左半部.这个样式用来调整输入框和下拉框不会超出单元格. ...

  5. Clip-跳过

    在 Stable Diffusion 1.x 模型中,CLIP 用作文本嵌入.CLIP模型由多层组成.他们一层一层地变得更加具体.过于简单化,第一层可以理解"人",第二层可以区分& ...

  6. MQTT 实践总结

    QMQX 文档:https://www.emqx.io/docs/zh/latest/ MQTT 入门:https://www.emqx.com/zh/mqtt-guide 通过案例理解 MQTT 主 ...

  7. uniapp uni-number-box组件 步长为1,还能输入小数思路分享

    正常情况,输入了步长为1,是无法在输入小数的.需求是要能输入一位小数,但如果直接步长设为0.1,又不能按1这样递增,输入数量上用起来肯定很麻烦. 于是我就想了一个折中方法,步长设为:1.01,然后值改 ...

  8. 【u8 login debug】u8 16.0 没有调试 login的解决办法

    16.0 没有调试 login,改一下注册表 就行[HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Ufsoft\WF\V8.700]"Enable.Debu ...

  9. go 使用 consul api filter 过滤注意点

    当你的value里面有-特殊符号的时候你应该像这样使用Service == "foo-bar"

  10. Aspire项目发布到远程k8s集群

    前提 你必须会创建aspire项目,不会的请先看微服务新体验之Aspire初体验 Aspirate (Aspir8) Aspirate 是将aspire项目发布到k8s集群的工具 安装aspirate ...