第六十三条 在细节消息中包含能捕获失败的信息


程序为了捕获异常,如果失败了,系统会自动打印该异常的信息,在堆栈中包含该异常的字符串表示法,即它的toString方法的调用结果,包含了一系列信息:该异常的类名和细节消息。基本上,这是程序员在调查程序失败原因的必要的信息。如果这个失败不易重现,这失败的信息就异常重要,因此,异常类型的toString方法应该尽可能多地返回有关失败原因的信息,这些属于log日志的范畴,是需要好好分析的,为了更好的搭建程序。

为了分析异常,我们捕获的异常细节应该是能分析出异常原因的数据。比如,IndexOutOfBoundsException异常的细节消息应该包含下界、上界以及没有落在界内的下标值,这三个值任何一个出错,程序就可能会错,尤其是三个参数都有问题。数组的索引可能为负数,也可能大于数组元素个数,或者是个无效的值,也可能数组的下界大于了上届,属于误写的情况,每个情况都会导致这个错误。所以我们需要打印捕获出相关的三个参数,然后进行数据分析,看看到底是怎么触发的。

虽然在异常的细节消息中包含所有相关的“硬数据(hard data)”是非常重要的,但是包含大量的描述信息没有什么意义。我们需要知道抛出该异常的确切文件和行数,以及其他方法调用该方法所在的代码的文件和行数,关于失败的描述信息通常是不必要的,这些信息可以通过阅读源代码而获得,我们只需要关键信息。异常的细节消息不应该与“用户层次的错误消息”混为一谈,后者对于最终用户而言必须是可理解的。与用户层次的错误消息不同,异常的字符串表示法主要是让程序员或者域服务人员用来分析失败的原因。因此,信息的内容比可理解性要重要得多。

总之,出现异常,就要记录有用的信息,能让程序员发现问题的触发点和原因,否则就失去了它的意义,通常程序员会再次搭建异常框架,把这些异常再次添加信息,统一处理。为了确保在异常的细节消息中包含足够的能捕获失败的信息,一种办法是在异常的构造器而不是字符串细节消息中引入这些信息。然后,有了这些信息,只要把它们放到消息描述中,就可以自动产生细节消息。例如 IndexOutOfBoundsException 并不是有个String构造器,而是有个这样的构造器:

// 现有的
class IndexOutOfBoundsException extends RuntimeException {
    private static final long serialVersionUID = 234122996006267687L;

    public IndexOutOfBoundsException() {
        super();
    }

    public IndexOutOfBoundsException(String s) {
        super(s);
    }
}

// 理想的
public class IndexOutOfBoundsException extends RuntimeException{
    public IndexOutOfBoundsException(int lowerBound, int upperBound, int index) {
        super("Lower bound:" + lowerBound +
                ",Upper bound:" + upperBound +
                ",Index:" + index);
        this.lowerBound = lowerBound;
        this.upperBound = upperBound;
        this.index = index;
    }
}


捕获失败异常的关键信息越多,我们分析错误日志越方便,积累的经验和见识也越多。总之,预防比后期处理要省力,但既然出现了问题,我们处理之前,要获取足够的信息,才能药到病除。
 

猜你喜欢

转载自blog.csdn.net/Deaht_Huimie/article/details/84248103