Java中异常打印输出的常见⽅法总结
前⾔
Java异常是在Java应⽤中的警报器,在出现异常的情况下,可以帮助我们程序猿们快速定位问题的类型以及位置。但是⼀般在我们的项⽬中,由于经验阅历等多⽅⾯的原因,依然有若⼲的童鞋在代码中没有正确的使⽤异常打印⽅法,导致在项⽬的后台⽇志中,没有收到⽇志或者⽇志信息不完整等情况的发⽣,这些都给项⽬埋下了若⼲隐患。本⽂将深⼊分析在异常⽇志打印过程中的若⼲情况,并给出若⼲的使⽤建议。
1. Java异常Exception的结构分析
我们通常所说的Exception主要是继承于Throwable⽽来,可以参见如下的结构图⽰:
主要的Throwable分为异常和错误两种,然后异常Exception和错误Error做为基类,分别被具体个性化以及衍⽣出NullPointerException、EOFException等等异常信息类。
基于Java中的源代码来分析,Error和Exception仅仅是继承了Throwable,做了构造函数的拓展,没有进⾏额外⽅法的延展;Exception输出的主要核⼼⽅法都是定义在Throwable中的,感兴趣的童鞋可以尝试阅读JDK的源代码。
下⾯将介绍⼀下关键的⼏个异常类⽅法:
1、getMessage(): String
输出异常的描述信息
2、getLocalizedMessage()
输出本地化的描述信息,⼀般此⽅法可被⼦类所覆盖,缺省实现与getMessage()输出信息⼀致
3、printStackTrace()
将异常栈打印到输出流中,此为⼀类⽅法,默认打印到console控制台,也可以显式指定输出流。
4、fillInStackTrace()
将当前的异常栈保存到⼀个Throwable中,返回这个Throwable。⼤部分情况下,⽤在保留异常栈嵌套调⽤的情况,尝试保留完整的异常栈,⽆需使⽤该⽅法。
2. Error vs Exception
Error在Java体系中定义为不可控制的问题,往往⽤来描述系统错误或者底层的问题,⽐如虚拟机错误,例如内存空间不⾜,⽅法调⽤栈溢等。我们以上图中列举出的内存溢出错误(StackOverflowError)为例,
它是在JVM层⾯发⽣的错误,已经游离于java应⽤层之外;在应⽤程序层⾯是⽆法进⾏捕获,且⽆法从错误中恢复的。⼀般⼀旦发了类似问题,⼀般都是直接宕机,应⽤停⽌正常的⼯作,需要重新启动或者修复问题之后,⽅可重新正常⼯作。
Exception⼀般发⽣在应⽤层,即在由项⽬中的Java代码层⾯引发的问题,且可以尝试进⾏捕获,此类问题不会影响到应⽤程序的正常⼯作的,即不会导致宕机现象的发⽣。我们在⼯作或者代码中常见的都是Exception衍⽣出来的各类异常。
这⾥需要强调说明⼀下,JVM是Java语⾔的运⾏环境和平台,但是并不是Java语⾔体系的⼀个部分;在JVM平台上,还可以运⾏Groovy, JPython, JRuby, Closure,Scala等等遵守Java语⾔规范(JavaLanguage Specification)的编程语⾔,故我们可以将Error理解为脱离Java应⽤之外的问题。
3. Exception中的运⾏时异常(RuntimeException)和受控异常(checked exception)
运⾏时异常(RuntimeException)是指在运⾏之时发⽣的异常,⽆需显式地进⾏捕获;如果程序中发⽣类似的异常,JVM会直接抛出此类异常,并打出响应的异常栈信息。此类异常也通常被称为unchecked exception,未受控异常。
受控异常(checked Exception)是我们最常见的异常种类,在代码中使⽤的异常基本上都是此类异常,
此类异常会在代码编译阶段由Java编译器进⾏语法检查,如果未显式进⾏异常捕获,则会报出相应的编译异常信息。
4. 如何在代码中正确打印异常信息
下⾯我们将通过⼀系列的例⼦来说明上述⼏个Exception中⽅法的使⽤技巧。
Case 1: getMessage()/getLocalizedMessage()
public void testCase1() {
System.out.println("We are going to do ");
try {
throw new NullPointerException("I am an exception in the code.");
} catch (Exception e) {
System.out.println("We got unexpected:" + e.getMessage());
nullpointerexception为什么异常System.out.println("We got unexpected:" + e.getLocalizedMessage());
}
}
输出结果:
We are going to do
We got unexpected in getMessage==> I am an exception in the code.
We got unexpected in getLocalizedMessage==> I am an exception in the code.
基于结果来分析,上述两个⽅法只是将异常对象中的Message打印出来,这些信息对于我们追踪问题和调试帮助有限。Case 2:e.printStackTrace()
public void testCase2() {
System.out.println("We are going to do ");
try {
throw new Exception("I am an exception in the code.");
} catch (Exception e) {
e.printStackTrace();
}
}
运⾏结果:
运⾏结果图
printStackTrace()可以打印出整个异常栈,但是异常栈信息将输出到默认的输出流中,绝⼤多数情况下是系统的控制台,⽽在实际项⽬中,都是需要将异常栈输出到⽇志⽂件的,如果不显式指定,则会丢失异常信息,在⽇志⽂件中⽆从追查。
Case 3: 基于log4j/slf4j等输出到⽇志⽂件
在实际项⽬中,⼀般会使⽤log4j/log4j2/JDK logging/slf4j/logback等⽇志系统来存放⽇志,那如何来讲⽇志的异常栈存⼊⽇志⽂件呢,我们来看⽰例。
public void testCase3() {
System.out.println("We are going to do ");
try {
throw new NullPointerException("abcedfeg");
} catch (Exception e) {
logger.info("Unexpected error in ", e);
}
}
我们需要到⽇志⽂件中,到相应的异常信息,异常信息如下:
12:24:45.387 [main] INFO org.demo.TestException - Unexpected error in
java.lang.NullPointerException: abcedfeg
at org.stCase3(TestException.java:39)
at org.demo.TestException.main(TestException.java:12)
我们可以发现,整个异常栈信息由两个部分组成:
>> 异常中的message, 类似getMessage()输出的信息,
>> 使⽤logger.info之类的⽅法,将异常信息写⼊到⽇志流中.
以下为log4j的声明,这⾥以slf4j为例来⽰例:
private static final Logger logger = Logger(TestException.class);
Case 4: fillInStackTrace()
public class FillInExceptionTest {
public static void main(String[] args) {
FillInExceptionTest fit = new FillInExceptionTest();
try {
fit.outerMehtod();
} catch (Exception e) {
System.out.println("\n==========I am the one evil separation line==============");
e.printStackTrace();
}
}
public void innerMethod() throws Exception {
throw new Exception("I got exception in an inner method.");
}
public void outerMehtod() throws Exception {
try {
innerMethod(); //invoke inner method.
} catch (Exception e) {
e.printStackTrace();
throw (Exception)e.fillInStackTrace();
}
}
}
运⾏结果:
基于上述的运⾏结果可知,fillInStackTrace()只提取了当下的异常栈信息,⽽⾮完整的异常栈信息,这个就是此⽅法带给我们的特殊之处。
如果我们需要在最外层将完整的异常栈打印出来,该如何做呢?将下述的语句:
throw (Exception)e.fillInStackTrace();
替换为:
throw e;
重新运⾏程序,我们就可以在最外层得到完整的异常栈信息了。
5. 总结
在本⽂中,我们介绍了异常类的继承体系,不同类型的异常区别与使⽤场景;并将基于代码⽰例展⽰了如何使⽤Exception的若⼲⽅法,利⽤这些⽅法来保留尽可能多的⽇志信息,⽅便我们后续针对⽇志中的异常信息,追查和解决问题。
好了,以上就是这篇⽂章的全部内容了,希望本⽂的内容对⼤家的学习或者⼯作能带来⼀定的帮助,如果有疑问⼤家可以留⾔交流,谢谢⼤家对的⽀持。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论