异常信息捕获为null
在一次需求中,为了防止编写的代码出现异常影响正常功能的使用,便对代码进行了try…cache进行捕获;由于是明确知道哪些会出现异常信息,所有在记录日志中并没有打印错误信息的堆栈,而是记录捕获的具体错误信息,使用的是异常类的getmessage()进行获取。
但奇怪的时候却发生了,日志打印获取的异常信息为null。
出现了异常但返回信息确实null,代码哪里有问题,经过测试发现,有两种情况会出现调用e.getMessage() = null:
- 出现空指针异常
- 反射调用方法出现异常
空指针异常
空指针异常这个较好理解,在输出的异常信息中确实返回null,可以进行对比:
反射调用出现异常
编写测试代码:
public static void main(String[] args) {
try {
Class<?> aClass = Class.forName("com.test.test.Test01");
Object o = aClass.newInstance();
aClass.getMethod("sum", String.class).invoke(o,new Object[]{null});
} catch (Exception e) {
System.out.println("e.getMessage() = " + e.getMessage());
}
}
public void sum(String str) {
if (str == null || str.equals("")) {
throw new RuntimeException("参数值不合规");
}
}
查看打印结果:
然后重新修改代码,打印出堆栈信息:
从异常信息中可以看出,抛出的是InvocationTargetException异常,然后自定义的异常信息来自Caused by: java.lang.RuntimeException: 参数值不合规。
看到这里,首先第一个疑问就是为什么会抛出InvocationTargetException,或者怎么知道他会抛出这个异常的?这里其实可以将利用反射调用的代码写在一个方法中,并不对代码里的内容进行try…cache:
public void useMethod() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<?> aClass = Class.forName("com.test.test.Test01");
Object o = aClass.newInstance();
aClass.getMethod("sum", String.class).invoke(o,new Object[]{null});
}
从代码抛出的异常类看出InvocationTargetException 其实是调用方法执行时才会抛出的异常,或者可以进入invoke查看一下:
现在知道了调用反射执行的代码会出现 InvocationTargetException 信息,那为什么获取异常信息getMessage()为null呢?
可以看下 InvocationTargetException 异常类的源码:
public class InvocationTargetException extends ReflectiveOperationException {
private static final long serialVersionUID = 4085088731926701167L;
private Throwable target;
protected InvocationTargetException() {
super((Throwable)null); // Disallow initCause
}
// 调用了父类的构造器,设置为null
// 异常信息记录在了成员变量target属性中
public InvocationTargetException(Throwable target) {
super((Throwable)null); // Disallow initCause
this.target = target;
}
public InvocationTargetException(Throwable target, String s) {
super(s, null); // Disallow initCause
this.target = target;
}
public Throwable getTargetException() {
return target;
}
public Throwable getCause() {
return target;
}
}
根据源码就可以看出,原来是
现在已经知道了,如果反射调用出现了异常,可以对InvocationTargetException进行捕获,但是在记录日志的时候如何获取反射调用时出现的自定义的异常信息,于是我又作出了这个测试,除了可以调用getMessage()方法,还可以调用异常类提供的其他方法:
public static void main(String[] args) {
try {
Class<?> aClass = Class.forName("com.test.test.Test01");
Object o = aClass.newInstance();
aClass.getMethod("sum", String.class).invoke(o,new Object[]{null});
} catch (Exception e) {
System.out.println("e.getMessage() = " + e.getMessage());
System.out.println("e.getCause().getMessage() = " + e.getCause().getMessage());
}
}
public void sum(String str) {
if (str == null || str.equals("")) {
throw new RuntimeException("参数值不合规");
}
}
在这里调用的是 e.getCause().getMessage() 方法获取到了自定义抛出的信息。对于 e.getCause() 方法,在之前的开发中很少关注到,所以查看了API文档:
所以查看了API,但为什么可以这么做,只能在这里留个小尾巴,后续知识充沛了再来更新。
如果仅仅是打印错误的具体信息,对于排错还是不够的,仍需要打印调用的堆栈信息,最终捕获异常的代码:
public static void main(String[] args) {
try {
Class<?> aClass = Class.forName("com.test.test.Test01");
Object o = aClass.newInstance();
aClass.getMethod("sum", String.class).invoke(o, new Object[]{null});
} catch (Exception e) {
System.out.println("异常信息:" + getTrace(e));
}
}
public void sum(String str) {
if (str == null || str.equals("")) {
throw new RuntimeException("参数值不合规");
}
}
public static String getTrace(Throwable t) {
StringWriter stringWriter= new StringWriter();
// 将此一次性文件及其回溯打印到指定的打印的writer中
t.printStackTrace(new PrintWriter(stringWriter));
// 将信息输出
return stringWriter.getBuffer().toString();
}
查看打印的异常信息,注意观察异常颜色,如果不是通过输出语句输出的,应该是红色,现在是白色:
如果将错误信息打印到日志中,只需要在catch中调用getTrace方法获取异常信息记录到日志中即可。