Java异常打印输出中常见方法的分析

Java异常是在Java应用中的警报器,在出现异常的情况下,可以帮助我们程序猿们快速定位问题的类型以及位置。但是一般在我们的项目中,由于经验阅历等多方面的原因,依然有若干的童鞋在代码中没有正确的使用异常打印方法,导致在项目的后台日志中,没有收到日志或者日志信息不完整等情况的发生,这些都给项目埋下了若干隐患。本文将深入分析在异常日志打印过程中的若干情况,并给出若干的使用建议。

1. Java异常Exception的结构分析

我们通常所说的Exception主要是继承于Throwable而来,可以参见如下的结构图示:

主要的Throwable分为异常和错误两种,然后异常Exception和错误Error做为基类,分别被具体个性化以及衍生出NullPointerException、EOFException等等异常信息类。

基于Java中的源代码来分析,Error和Exception仅仅是继承了Throwable,做了构造函数的拓展,没有进行额外方法的延展;Exception输出的主要核心方法都是定义在Throwable中的,感兴趣的童鞋可以尝试阅读JDK的源代码。

下面将介绍一下关键的几个异常类方法:

  • getMessage(): String

    输出异常的描述信息

  • getLocalizedMessage()

    输出本地化的描述信息,一般此方法可被子类所覆盖,缺省实现与getMessage()输出信息一致

  • printStackTrace()

    将异常栈打印到输出流中,此为一类方法,默认打印到console控制台,也可以显式指定输出流。

  • 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 something interesting....");

try {

throw new NullPointerException("I am an exception in the code.");

} catch (Exception e) {

System.out.println("We got unexpected:" + e.getMessage());

System.out.println("We got unexpected:" + e.getLocalizedMessage());

}

}

输出结果:

We are going to do testing interesting....

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 something interesting....");

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 something interesting....");

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.demo.TestException.testCase3(TestException.java:39)

at org.demo.TestException.main(TestException.java:12)

我们可以发现,整个异常栈信息由两个部分组成:

>> 异常中的message, 类似getMessage()输出的信息,

>> 使用logger.info之类的方法,将异常信息写入到日志流中.

以下为log4j的声明,这里以slf4j为例来示例:

private static final Logger logger = LoggerFactory.getLogger(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的若干方法,利用这些方法来保留尽可能多的日志信息,方便我们后续针对日志中的异常信息,追查和解决问题。

----------------------罪恶的分割线----------------------------------------------


本文系CDSN的博主<<木小鱼的笔记>>个人原创,如要转载,请保留原始链接和原作者信息,支持原创,尊重原创,让知识世界更美好。

作者本人也维护了一个今日头条上的头条号:程序加油站,欢迎大家关注。
时间: 2024-10-09 00:13:27

Java异常打印输出中常见方法的分析的相关文章

Java程序开发中的简单内存分析

首先说明内存总体分为了4个部分, 包括 1.stack segment (栈区存储基本数据类型的局部变量,对象的引用名) 2.heap segment(堆区,一般用于存储java中new 出来的对象) 3.code segment (代码段) 4.data segment (数据段,静态数据常量) 其中我们程序中用关键字new出来的东西都是存放在heap segment: 程序中的局部变量存放在stack segment,这些局部变量是在具体方法执行结束之后,系统自动释放内存资源(而heap s

【Java】Collections中sort方法Comparator的重写

很多人只会用Collections中不带比较器Comparator的sort方法完成一些对存储整形Integer的动态数组ArrayList的简单排序,包括我之前,此前仅仅在<[Java]Java中的Collections类--Java中升级版的数据结构>(点击打开链接)介绍Collections中sort方法的简单用法. igz 面对eclipse所给出的说明,根本就不知道这个sort方法如何重写,如果我对一个存储Student类的动态数组进行以学生的学号为根据,姓名为根据进行排序,那该如何

Java线程并发中常见的锁--自旋锁 偏向锁

随着互联网的蓬勃发展,越来越多的互联网企业面临着用户量膨胀而带来的并发安全问题.本文着重介绍了在java并发中常见的几种锁机制. 1.偏向锁 偏向锁是JDK1.6提出来的一种锁优化的机制.其核心的思想是,如果程序没有竞争,则取消之前已经取得锁的线程同步操作.也就是说,若某一锁被线程获取后,便进入偏向模式,当线程再次请求这个锁时,就无需再进行相关的同步操作了,从而节约了操作时间,如果在此之间有其他的线程进行了锁请求,则锁退出偏向模式.在JVM中使用-XX:+UseBiasedLocking pac

Java线程并发中常见的锁

随着互联网的蓬勃发展,越来越多的互联网企业面临着用户量膨胀而带来的并发安全问题.本文着重介绍了在java并发中常见的几种锁机制. 1.偏向锁 偏向锁是JDK1.6提出来的一种锁优化的机制.其核心的思想是,如果程序没有竞争,则取消之前已经取得锁的线程同步操作.也就是说,若某一锁被线程获取后,便进入偏向模式,当线程再次请求这个锁时,就无需再进行相关的同步操作了,从而节约了操作时间,如果在此之间有其他的线程进行了锁请求,则锁退出偏向模式.在JVM中使用-XX:+UseBiasedLocking pac

java项目开发中常见异常

我相信,在我们做项目的时候,console框中常常出现各种的异常,时间久了,就会发现常见的也就几个,下面我就总结一下常见的异常,供大家参考. 1 java.lang.UnsupportedClassVersionError 从字面上我们就可以看出来是类版本不支持错误,换句话就是jdk版本不兼容,原因是因为我们在高版本上编译class,在低版本的jvm中运行,这是我搜到的答案.理解起来还好.但是我发现我的myeclipse是高版本的jdk,引进的项目是低版本的,这样应该可以支持的,可是同样出现了这

java集合_collection 中的方法 通过Allaylist来体现

import java.util.*; /* Collection定义了集合框架的共性功能.1,添加    add(e);    addAll(collection); 2,删除    remove(e);    removeAll(collection);     clear(); 3,判断.    contains(e);    isEmpty(); 4,获取    iterator();    size(); 5,获取交集.    retainAll(); 6,集合变数组.    toAr

黑马程序员——java——Date类的常见方法演示

Date 中的常用方法演示 import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class DateDemos { public static void main(String[] args) throws Exception { // 获取系统当前的时间的毫秒值 long l = System.

框架中常见的注解分析

按照注解的来源,也是分为3类1. 内置注解 如@Override,@Deprecated 等等2. 第三方注解,如Hibernate, Struts等等3. 自定义注解,如fahibernate的自定义注解 在工作中,大部分都使用的是第三方注解.那么接下来就介绍一些常用的第三方注解如:@RequestMapping.@ResponseBody.@Autowired .@Scope.@Controller.@Service.@Repository.@Component.@RequiresPermi

二、数字电路中常见器件应用分析-三极管

 一. 三极管简介  种类 根据构成三极管中的三块半导体类型的不同,有NPN型和PNP型两种:根据材料的不同有硅管和锗管两种,常见的硅管,Vbe间的压降是0.7V,锗管则是0.2V. 作用   三极管有三种状态,放大.截止和饱和,是一种控制电流的半导体器件.利用不同的状态特性,三极管的主要作用有两个: 1.将微弱信号放大成幅值较大的电信号 2.用作无触点开关-即开关特性.  二. 三极管用作开关  开关条件 不管是NPN型还是PNP型三极管,三极管的导通条件是:箭头朝内PNP,箭尾的电压高于箭头