java编程思想读书笔记 第十二章 通过异常处理错误(上)

java异常处理的目的在于通过使用少于目前数量的代码来简化大型、可靠的程序的生成,并且通过这种方式可以使你更加自信。本章将介绍如何编写正确的异常处理程序,并将展示当方法出问题的时候,如何产生自定义异常。

1.概念
如果不使用异常,那么就必须检查特定的错误,并在程序中的许多地方处理它。而如果使用异常,那就不必在方法调用处进行检查,因为异常机制将保证能够捕获这个错误。并且,只需在一个地方处理错误,即所谓的异常处理程序中。这种方式不仅节省代码,而且把“描述在正常执行过程中做什么事”的代码和“出了问题怎么办”的代码相分离。总之,与以前的错误处理方法相比,异常机制使代码的阅读、编写和调试工作更加井井有条。
2.基本异常
异常情形是指组织当前方法或作用域继续执行的问题。把异常情形与普通问题相区分很重要,所谓的普通问题是指,在当前环境下能够得到足够的信息,总能处理这个错误。而对于异常情形,就不能继续下去了,因为在当前环境下无法获得必要的信息来解决问题。你所能做的就是从当前环境跳出,并且把问题交给上一级环境。这就是抛出异常时所发生的事情。
当抛出异常后有几件事会发生。首先,同java中其他对象的创建一样,将使用new在堆上创建异常对象。然后,当前的执行路径被终止,并且从当前环境中弹出对异常对象的引用。此时,异常处理机制接管程序,并开始寻找一个恰当的地方来执行程序。这个恰当的地方就是处理异常程序,它的任务是将程序从错误状态中恢复,以使程序能要么换一种方式运行,要么继续执行下去。
异常允许我们强制程序停止运行,并告诉我们出现了什么问题,或者强制程序处理问题,并返回到稳定状态。
2.1异常参数
与使用java的其他对象一样,我们总是用new在堆上创建异常对象,这也伴随着存储空间的分配和构造器的调用。所有标准异常类都有两个构造器:一个是默认构造器,另一个是接受字符串作为参数,以便能把相关的信息放入异常对象的构造器:
throw new NullPointerException(“T = null”);
在使用new创建了异常对象之后,此对象的引用将传给throw。尽管返回的异常对象类型通常与方法设计的返回类型不同,但从效果上看,它就像是从方法“返回”的。可以简单地把异常处理看成是一种不同的返回机制,当然若过分调用这种类比的话,就会有麻烦了。另外还能用抛出异常的方式从当前的作用域退出。在这种情况下,将会返回一个异常对象,然后退出方法或作用域。
此外,能够抛出任意类型的Throwable对象,它是异常类型的根类。通常,对于不同类型的错误,要抛出相应的异常。错误信息可以保存在异常对象内部或者用异常类的名称来暗示。
3.捕获异常
要明白异常时如何被捕获的,必须先理解监控区域的概念。它是一段可能产生异常的代码,并且后面跟着处理这些异常的代码。
3.1 try块
如果在方法内部抛出了异常,中方法将在抛出异常的过程中结束。要是不希望方法就此结束,可以在方法内设置一个特殊的块来捕获异常。因为在这个块了“尝试”各种方法调用,所以称为try块。它是跟在try关键字之后的普通程序块:

try{
// Code that might generate exception
}

对于不支持异常处理的程序语言,要想仔细检查错误,就得在每个方法调用的前后加上设置和错误检查的代码,甚至在每次调用同一方法时也得这么做。有了异常处理机制,可以把所有动作都放在try块里,然后只需在一个地方就可以捕获所有异常。
3.2 异常处理程序
当然,抛出的异常必须在某处得到处理。这个“地点”就是异常处理程序,而且针对每个要捕获的异常,得准备相应的处理程序。异常处理程序紧跟在try块后,以关键字catch表示:

try{
 // Code that might generate exception
}catch(Type1 id1){
 // Handle exceptions of Type1
}catch(Type2 id2){
 // Handle exceptions of Type2
}catch(Type3 id3){
 // Handle exceptions of Type3
}
// etc...

每个catch子句看起来就像是接收一个且仅接受一个特殊类型的参数的方法。可以在处理程序的内部使用标识符,这与方法参数的使用很相似。有时可能用不到标识符,因为异常的类型已经给了你足够的信息来对异常进行处理,但标识符不可以省略。
异常处理程序必须紧跟在try块之后。当异常被抛出时,异常处理机制将负责搜寻参数与异常类型相匹配的第一个处理程序。然后进入catch子句执行,此时任务异常得到了处理。一旦catch子句接受,则处理程序的查找过程结束。注意,只有匹配的catch子句才能得到执行,这与switch语句不同,switch语句需要在每一个case后面跟一个break,以避免执行后续的case子句。
注意在try块的内部,许多不同的方法调用可能会产生类型相同的异常,而你只需要提供一个针对类型的异常处理程序。

4.创建自定义异常
要自己定义异常,必须从已有的异常类继承,最好是选择意思相近的异常类继承。建立新的异常类型最简单的方法就是让编译器为你产生默认构造器。

public class SimpleExcetion extends Exception{}
public class TestException{
        public void f() throws SimpleExcetion{
            System.out.println("Throw SimpleExcetion from f()");
            throw new SimpleExcetion();
        }
        public static void main(String[] args) {
            TestException testException = new TestException();
            try {
                testException.f();
            } catch (SimpleExcetion e) {
                System.out.println("caught it");
            }
        }
}
输出:Throw SimpleExcetion from f()
      caught it

编译器创建了默认的构造器,它将自动调用基类的默认构造器。在本例中,你可以看到最重要的部分就是类名。
也可以为异常类定义一个接受字符串参数的构造器:

public 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:6)
    at FullConstructors.main(FullConstructors.java:14)
throwing myException from g()
MyException: originated in g()
    at FullConstructors.g(FullConstructors.java:10)
    at FullConstructors.main(FullConstructors.java:19)

两个构造器定义了MyException类型对象的创建方式。对于第二个构造器,使用super关键字明确调用了其基类构造器,它接受一个字符串作为参数。
在异常处理程序中,调用了在Throwable类声明的printStackTrace()方法。就像从输出中看到的,它将打印“从方法调用处直到一次抛出处”的方法调用序列。这里,信息被送到了System.out,并自动地被捕获和显示输出中。但是,如果调用默认版本:e.printStackTrace();则信息将被输出到标准错误流。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值