首页 > 代码库 > Java编程思想(九) —— 通过异常处理错误(1)

Java编程思想(九) —— 通过异常处理错误(1)

前阵子看到一个编程的学习方法——橡皮鸭调试法,就是拿一只小黄鸭。


                   



面对面,跟他讲解你的编程思路,如果你没有橡皮鸭或者是一个能听你讲java,c,cpp,前端,io,系统内核,汇编,数据结构,计算机网络的女朋友,那么写博客也是你自己梳理好思路的一个好方法。


书中原话:java的基本理念是结构不佳的代码不能运行。


其实我之前也搞不懂为什么要弄个这样的东西出来,其实跟书上讲的一样,“异常”有“对此感到意外”的意思,问题出现了,我们不知道怎么处理,但又不能置之不理,可以停下来,看是否有其他地方处理这样的问题。这样能降低代码的复杂度,如果不使用异常,那你在程序中就要检查并处理这个错误,使用异常之后,就不用在方法下面进行检查,异常机制能够捕获错误。

所谓的“描述正常执行过程中做了什么事”和“出了问题怎么办”代码分离。


举个简单的例子:

public class Box {
    public static void main(String[] args) {
        int i[] = new int[4];
        Box.change(i, 5, 1);
    }
    static void change(int[] i ,int index,int value){
        i[index] = value;
    }
}

这个传参如果超越数组边界的话怎么办,这是错误的啊,所以你用在方法中处理好,添加:

    static void change(int[] i ,int index,int value){
        if(index>i.length){
            System.out.println("IndexOutOfBound");
            return;
        }
        i[index] = value;
    }

这里的处理比较简单,如果这个程序里面有好多这样的问题,那你岂不是要在每一处都写上这个解决方法,这样就太麻烦了,异常就是这样用的。

  if(index>i.length){
            throw new IndexOutOfBoundsException();
        }

交给后面再去处理。


1)捕获异常

方法内部抛出异常,方法将在抛出异常的过程中结束,如果不希望方法就此结束,可以用try块来捕获异常。这样就不用每个方法都去加错误检查的代码,把他们一起捕获,对于相同的异常一次性解决。

try{
            
}catch(xx){
            
}catch(xx){

}

一旦有同类型的异常,便进入catch语句里面处理。



2)创建自定义异常

public class MyException {
    public void f(){
        System.out.println("f() throw simple exception!");
        try {
            throw new SimpleException();
        } catch (SimpleException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        MyException me = new MyException();
        me.f();
    }
}

这个例子有很多东西可以讲,首先,如果用了
throw new SimpleException();
那么,就要像上面一样加上try和catch。

或者,直接在方法中抛出:

    public void f() throws SimpleException{
        System.out.println("f() throw simple exception!");
            throw new SimpleException();
    }
但是这样直接抛出之后,
me.f();
就有问题了,以为异常抛出了,要么你在main方法中继续抛出:

    public static void main(String[] args) throws SimpleException {
        MyException me = new MyException();
        me.f();
    }

要么你在main方法中捕获异常进行处理:

 public static void main(String[] args) {
        MyException me = new MyException();
        try {
            me.f();
        } catch (SimpleException e) {
            System.err.println("catch it!");
        }
    }
err输出为标准错误流,out输出为标准输出,err的话在控制台会以红色字体显示,比较醒目。

但你会发现输出结果是catch it! 然后才是f() throw simple exception。这个后面再提。

        try {
            me.f();
        } catch (SimpleException e) {
            e.printStackTrace(System.out);
        }
result:
son.SimpleException
    at son.MyException.f(MyException.java:8)
    at son.MyException.main(MyException.java:13)
printStackTrace(),栈跟踪,打印的是从方法调用处直到异常抛出处,默认无参数的话会输出到标准错误流。


3)记录日志

其实大型的网站,每天都有记录日志,哪里有错误再去日志里找,究竟发生了什么问题。 

public class LoggingException extends Exception{
    private static Logger logger = Logger.getLogger("LoggingException");
    public LoggingException(){
        StringWriter sw = new StringWriter();
        printStackTrace(new PrintWriter(sw));
        logger.severe(sw.toString());
    }
    public static void main(String[] args) {
        try {
            throw new LoggingException();
        } catch (LoggingException e) {
            System.err.println("catch "+e);
        }
    }
}
result:
九月 10, 2014 7:35:50 下午 son.LoggingException <init>
严重: son.LoggingException
    at son.LoggingException.main(LoggingException.java:16)

catch son.LoggingException
new PrintWriter(sw) 将输出抽取为String。
PrintWriter的话作为参数传进去,后面IO的时候再了解。
Logger,A Logger object is used to log messages for a specific system
or application component.

serve方法,Log a SEVERE message。


4)异常说明

Java鼓励人们把方法可能会抛出的异常告知使用此方法的客户端程序员。如果有源代码,那么客户端程序员可以通过查找throw语句获知抛出的异常。然而源代码一般不提供。


java提供了相应的语法,书上写得很有趣,说以礼貌的方式告诉客户端程序员某个方法可能会抛出的异常类型,他们可以进行相应的处理,这就是异常说明。

其实就是上面提到的不用try,catch而使用到的throws。


5)printStackTrace

我之前翻译成栈跟踪了,书上写的是栈轨迹,其实方法返回的是栈轨迹元素组成的数组,0为栈顶元素,先进后出,这是栈的特性。

public class TestElement {
    static void first(){
        try {
            throw new Exception();
        } catch (Exception e) {
            // 栈元素  拿到方法名
            for(StackTraceElement s : e.getStackTrace()){
                System.out.println(s.getMethodName());
            }
        }
    }
    static void second(){first();}
    static void third(){second();}
    public static void main(String[] args) {
        first();
        System.out.println("////////////////");
        second();
        System.out.println("////////////////");
        third();
    }
}
result:
first
main
////////////////
first
second
main
////////////////
first
second
third
main

整个栈元素的信息都可以逐一显示。


这篇先讲解基本的异常概念,理清一下为什么使用异常的思路,下一篇,讲到try,catch嵌套这更有深度的内容的时候,更加精彩!


Java编程思想(九) —— 通过异常处理错误(1)