java 通过异常处理错误(上)

java编程思想第12章笔记

1.概念

异常指的是在程序运行过程中发生的异常事件,通常是由硬件问题或者程序设计问题所导致的。在Java等面向对象的编程语言中异常属于对象。
早期的C语言的异常处理机制,通常是我们人为的对返回结果加一些标志来进行判定,比如发生错误返回什么标志,正常情况下我们又是返回什么标记,而这些都不是语言本身所赋予我们的,这种机制的问题在于,C语言的异常处理机制全是我们人为的定义,这样就会造成业务逻辑的主线受到异常处理的牵制,或者说是我们难免会将注意力转移,并且造成业务逻辑与异常处理之间有很大程度上的缠绕。

2. 基本异常

异常清醒是指阻止当前方法或者作用域继续执行的问题.
在java中异常同样也是对象, 使用new可以在堆上创建异常对象
举个异常最常见的例子好了, 我们都知道除数是不能为零的, , 所以做除法之前要做一个必要的检查, 检查除数不能为零, 否则将抛出异常
另一种情况就是当使用一个对象的时候, 这个对象可能为null, 并没有初始化, 遇到这样的情况就需要抛出异常:

if (t == null)
 throw newNull PointerExcept();

2.1 异常参数

所有标准的异常类都有两个构造器, 一个是默认构造器, 另一个是字符串为参数, 以便能把相关信息方法异常对象的构造器

3 捕获异常

3.1 try块

如果在方法内部抛出了异常, 那么这个方法将在抛出异常的过程中结束, 如果不想方法结束, 需要将可能产生异常的代码放入try代码块中, 如果产生异常就会被try块捕获

3.2 异常处理程序

当异常抛出后, 需要被处理, 处理需要在catch块中进行, 对于每个catch自居, 接收一个且仅接收一个特殊类型的的参数的方法, catch块就是异常处理程序, 且异常处理程序必须紧跟try

4 创建自定义异常

自定义的异常类必须继承异常类, 最简单的就是继承Exception类了, 来看一个简单的示例:

class SimpleException extends Exception {}

public class InheritingExceptions {
    public void f() throws SimpleException {
        System.out.println("Throw SimpleException from f()");
        throw new SimpleException();
    }

    public static void main(String[] args) {
        InheritingExceptions sed = new InheritingExceptions();
        try {
            sed.f();
        } catch (SimpleException e) {
            System.out.println("Caught it");
        }
    }
}

Throw SimpleException from f()
Caught it

下面的例子将结果打印到了控制台上了, 更好的做法是答应道System.err, 因为System.out可能会被重定向, 同时这个例子也展示了一个接受字符串参数的构造器:

class MyException extends Exception {
    public MyException() {}
    public MyException(String msg) {
        super(msg);
    }
}

public class FullConstructors {
    public static void f() throws MyException {
        System.out.println("Throwing MyException from f()");
        throw new MyException();
    }
    public static void g() throws MyException {
        System.out.println("Throwing MyException from g()");
        throw new MyException("Originated in g()");
    }

    public static void main(String[] args) {
        try {
            f();
        } catch (MyException e) {
            e.printStackTrace(System.out);
        }
        try {
            g();
        } catch (MyException e) {
            e.printStackTrace(System.out);
        }
    }
}

输出结果:

Throwing MyException from f()
MyException
    at FullConstructors.f(FullConstructors.java:11)
    at FullConstructors.main(FullConstructors.java:20)
Throwing MyException from g()
MyException: Originated in g()
    at FullConstructors.g(FullConstructors.java:15)
    at FullConstructors.main(FullConstructors.java:25)

说一下这个printStackTrace()方法, 这个方法将打印"从方法调用出知道异常跑出处"的方法调用序列, 这里是将信息发送到了System.out, 无参数版本就是答应道标准错误流

4 异常与记录日志

使用java.util.Logging工具可以将输出记录到日记中, 基本的使用如下:

import java.util.logging.*;
import java.io.*;

class LoggingException extends Exception {
    private static Logger logger = Logger.getLogger("LoggingException");
    public LoggingException() {
        StringWriter trace = new StringWriter();
        printStackTrace(new PrintWriter(trace));
        logger.severe(trace.toString());
    }
}

public class LoggingExceptions {
    public static void main(String[] args) {
        try {
            throw new LoggingException();

        }catch (LoggingException e) {
            System.err.println("Caught " + e);
        }

        try {
            throw new LoggingException();
        } catch (LoggingException e) {
            System.err.println("Caught " + e);
        }
    }
}

输出如下

十二月 01, 2016 9:14:31 下午 LoggingException <init>
严重: LoggingException
    at LoggingExceptions.main(LoggingExceptions.java:16)

Caught LoggingException
十二月 01, 2016 9:14:31 下午 LoggingException <init>
严重: LoggingException
    at LoggingExceptions.main(LoggingExceptions.java:23)

Caught LoggingException

静态的Logger.getLogger()方法创建了一个String参数相关的Logger对象, 通常与错误相关的类名一致, 这个Logger对象会将其输出发送到System.err.
severe()方法表示级别, 很明显, severe表示严重的
但是更常见的情况是我们需要捕获和记录别人编写的异常, 比如说这样:

import java.util.logging.*;
import java.io.*;

public class LoggingException2 {
    private static Logger logger = Logger.getLogger("LoggingException2");
    static void logException(Exception e) {
        StringWriter trace = new StringWriter();
        e.printStackTrace(new PrintWriter(trace));
        logger.severe(trace.toString());
    }

    public static void main(String[] args) {
        try {
            throw new NullPointerException();
        } catch (NullPointerException e) {
            logException(e);
        }
    }
}

输出结果为:

十二月 01, 2016 9:24:49 下午 LoggingException2 logException
严重: java.lang.NullPointerException
    at LoggingException2.main(LoggingException2.java:14)

当然可以进一步自定义异常, 比如加入额外的构造器和成员, 得到更强大的功能.

5.异常说明

使用throws关键字, 紧跟在方法后面, 就像上面的程序例子一样, 说明了这个方法可能会抛出的异常类型, 这就是异常说明

6. 捕获所有的异常

可以只使用一个异常处理程序来捕获所有类型的异常, 因为异常类型的基类都是Exception;

catch(Exception e) {
    System.out.println("Caught an Exception");
}

6.1 栈轨迹

printStackTrace()方法所提供的信息可以通过getStackTrace()方法来直接访问, 这个方法返回一个由栈轨迹中的元素所构成的数组, 其中每一个元素表示栈中的一帧, 元素0是栈顶元素, 并且是调用序列中最后一个被调用的方法, 数组中的最后一个元素是调用序列中的第一个方法
下面是一个简单的演示程序:

public class WhoCalled {
    static void f() {
        try {
            throw new Exception();
        } catch(Exception e) {
            for(StackTraceElement ste : e.getStackTrace()) {
                System.out.println(ste.getMethodName());
            }
        }
    }
    static void g() {
        f();
    }
    static void h() {
        g();
    }

    public static void main(String[] args) {
        f();
        System.out.println("--------------------------------------");
        g();
        System.out.println("--------------------------------------");
        h();
    }
}

输出结果:

f
main
--------------------------------------
f
g
main
--------------------------------------
f
g
h
main

6.2 重新抛出异常

有时候需要将刚捕获的异常重新抛出, 尤其是使用Exception捕获所有异常的时候, 可以直接把它重新抛出, 重新抛出异常会把异常抛给上一级环境中的异常处理程序, 此外异常对象的所有信息都得以保存.
如果只是重新把当前的异常对象抛出, 那么printStrackTrace()方法现实的将是原来异常抛出点的调用栈信息, 并非重新抛出点的信息.

public class Rethrowing {
    public static void f() throws Exception {
        System.out.println("Originating the exception in f()");
        throw new Exception("thrown from f()");
    }

    public static void g() throws Exception {
        try {
            f();
        } catch (Exception e) {
            System.out.println("Inside g(), e.printStackTrace()");
            e.printStackTrace(System.out);
            throw e;
        }
    }

    public static void h() throws Exception {
        try {
            g();
        } catch (Exception e) {
            System.out.println("inside h(), e.printStackTrace()");
            e.printStackTrace(System.out);
            // throw (Exception) e.fillInStackTrace();
            throw e;
        }
    }
    public static void main(String[] args) {
        try {
            g();
        } catch (Exception e) {
            System.out.println("main : printStackTrace()");
            e.printStackTrace(System.out);
        }

        try {
            h();
        } catch (Exception e) {
            System.out.println("main : printStackTrace()");
            e.printStackTrace(System.out);
        }
    }

}

首先执行这个结果

Originating the exception in f()
Inside g(), e.printStackTrace()
java.lang.Exception: thrown from f()
    at Rethrowing.f(Rethrowing.java:4)
    at Rethrowing.g(Rethrowing.java:9)
    at Rethrowing.main(Rethrowing.java:29)
main : printStackTrace()
java.lang.Exception: thrown from f()
    at Rethrowing.f(Rethrowing.java:4)
    at Rethrowing.g(Rethrowing.java:9)
    at Rethrowing.main(Rethrowing.java:29)
Originating the exception in f()
Inside g(), e.printStackTrace()
java.lang.Exception: thrown from f()
    at Rethrowing.f(Rethrowing.java:4)
    at Rethrowing.g(Rethrowing.java:9)
    at Rethrowing.h(Rethrowing.java:19)
    at Rethrowing.main(Rethrowing.java:36)
inside h(), e.printStackTrace()
java.lang.Exception: thrown from f()
    at Rethrowing.f(Rethrowing.java:4)
    at Rethrowing.g(Rethrowing.java:9)
    at Rethrowing.h(Rethrowing.java:19)
    at Rethrowing.main(Rethrowing.java:36)
main : printStackTrace()
java.lang.Exception: thrown from f()
    at Rethrowing.f(Rethrowing.java:4)
    at Rethrowing.g(Rethrowing.java:9)
    at Rethrowing.h(Rethrowing.java:19)
    at Rethrowing.main(Rethrowing.java:36)

去掉注释并且将throw e;注释掉, 结果是:

Originating the exception in f()
Inside g(), e.printStackTrace()
java.lang.Exception: thrown from f()
    at Rethrowing.f(Rethrowing.java:4)
    at Rethrowing.g(Rethrowing.java:9)
    at Rethrowing.main(Rethrowing.java:29)
main : printStackTrace()
java.lang.Exception: thrown from f()
    at Rethrowing.f(Rethrowing.java:4)
    at Rethrowing.g(Rethrowing.java:9)
    at Rethrowing.main(Rethrowing.java:29)
Originating the exception in f()
Inside g(), e.printStackTrace()
java.lang.Exception: thrown from f()
    at Rethrowing.f(Rethrowing.java:4)
    at Rethrowing.g(Rethrowing.java:9)
    at Rethrowing.h(Rethrowing.java:19)
    at Rethrowing.main(Rethrowing.java:36)
inside h(), e.printStackTrace()
java.lang.Exception: thrown from f()
    at Rethrowing.f(Rethrowing.java:4)
    at Rethrowing.g(Rethrowing.java:9)
    at Rethrowing.h(Rethrowing.java:19)
    at Rethrowing.main(Rethrowing.java:36)
main : printStackTrace()
java.lang.Exception: thrown from f()
    at Rethrowing.h(Rethrowing.java:23)
    at Rethrowing.main(Rethrowing.java:36)

对比第二个结果比第一个结果少了倒数三四行, fillInStackTrace方法会将栈轨迹清空并填充当前栈轨迹

6.3 异常链

常常会想要在捕获一个异常之后抛出另一个异常, 并想吧原始异常的信息保存下来, 这样称为异常链, throwable中initCause()方法可以接受一个cause对象作为参数, 这个cause就是用来表示原始异常的, 这样通过把原始异常传递给新的异常, 是的即使在当前位置创建并抛出了新的异常, 也能通过这个异常链追踪到最初发生异常的地方.
下面看一个可以动态的扩展字段的dome:

class DynamicFieldsException extends Exception{}

public class DynamicFields {
    private Object[][] fields;

    public DynamicFields(int initialSize) {
        fields = new Object[initialSize][2];
        for (int i = 0;i < initialSize; i++) {
            fields[i] = new Object[] {null, null};
        }
    }

    public String toString() {
        StringBuilder result = new StringBuilder();
        for(Object[] obj : fields) {
            result.append(obj[0]);
            result.append(":");
            result.append(obj[1]);
            result.append("\n");
        }
        return result.toString();
    }
    // 判断是否存在该字段, 存在返回下标, 如果不存在该字段返回-1
    private int hasField(String id) {
        for (int i = 0; i < fields.length; i++) {
            if (id.equals(fields[i][0])) {
                return i;
            }
        }
        return -1;
    }
    // 返回该字段标志符的下标, 如果不存在就抛出异常
    private int getFieldNumber(String id) throws NoSuchFieldException {
        int fieldNum = hasField(id);
        if (fieldNum == -1) {
            throw new NoSuchFieldException();
        }
        return fieldNum;
    }

    // 动态的扩展
    private int makeField(String id) {
        for (int i = 0; i < fields.length; i++) {
            if (fields[i][0] == null) {
                fields[i][0] = id;
                return i;
            }
        }

        Object[][] temp = new Object[fields.length +1][2];
        for (int i = 0; i < fields.length; i++) {
            temp[i] = fields[i];
        }
        for (int i = fields.length; i < temp.length; i++) {
            temp[i] = new Object[] {null, null};
        }
        fields = temp;
        return makeField(id);
    }
    
    // 得到该字段标识符的对应的字段值
    public Object getField(String id) throws NoSuchFieldException {
        return fields[getFieldNumber(id)][1];
    }
    // 设置字段
    public Object setField(String id, Object value) throws DynamicFieldsException {
        // 字段值为空的话
        if (value == null) {
            DynamicFieldsException dfe = new DynamicFieldsException();
            // 将此throwable的case初始化为指定值
            dfe.initCause(new NullPointerException());
            throw dfe;
        }
        int fieldNumber = hasField(id);
        // 如果该字段对应字段标识符不存在的话
        if (fieldNumber == -1) {
            fieldNumber = makeField(id);
        }
        Object result = null;
        try {
            result = getField(id);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }
        fields[fieldNumber][1] = value;
        return result;
    }

    public static void main(String[] args) {
        DynamicFields df = new DynamicFields(3);
        System.out.println(df);
        try {
            df.setField("d", "A value for d");
            df.setField("number", 47);
            df.setField("number2", 48);
            System.out.println(df);
            df.setField("d", "A new value for d");
            df.setField("number3", 11);
            System.out.println(df);
            System.out.println("df.getField(\"d\")" + df.getField("d"));
            Object field = df.setField("d", null);      // 产生异常
        } catch (NoSuchFieldException e) {
            e.printStackTrace(System.out);
        } catch (DynamicFieldsException e) {
            e.printStackTrace(System.out);
        }
    }
}

运行后的输出结果为:

null:null
null:null
null:null

d:A value for d
number:47
number2:48

d:A new value for d
number:47
number2:48
number3:11

df.getField("d")A new value for d
DynamicFieldsException
    at DynamicFields.setField(DynamicFields.java:64)
    at DynamicFields.main(DynamicFields.java:94)
Caused by: java.lang.NullPointerException
    at DynamicFields.setField(DynamicFields.java:65)
    ... 1 more
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容