Java 异常处理


转载请标明出处:
http://blog.csdn.net/tyzlmjj/article/details/49498757
本文出自:【M家杰的博客】

概述
学习JAVA老长一段时间了,但是一直没有对java的异常处理机制做一个很好的了解,趁着空下来好好学习了一下,并做下记录!


异常分类

首先在看一下java中异常的层次结构
异常层次结构图

java中所有异常对象都继承于Throwable类。向下分为两类,Error和Exception。

  • Error
    表示了程序运行时的系统内部错误等,这在一个完善的程序中应该是很少出现的,但是一旦出现这种状况,程序员能做的处理都是有限的。

  • Exception
    这是需要程序员重点关注的异常,它也可以大致分为两类:

    • 由程序错误导致的异常RuntimeException:(所有RuntimeException异常都是开发人员的错误!)
      1. 错误的类型转换
      2. 数组访问越界
      3. 访问空指针
    • 程序本身没有错误而导致的异常(如I/O异常):
      1. 读取文件尾部后面的数据
      2. 打开一个不存在的文件
      3. 用一个字符串查找Class,但这个类不存在

java将所有继承Error类和RuntimeException类的异常称为:未检查异常(unchecked);所有其他的异常称为:已检查异常(checked)

注意,所有的未检查异常、都需要开发人员对程序进行优化而避免产生这种异常。而所有的已检查异常并不是可以通过修改代码避免的,需要在产生异常的时候进行相关处理。


声明异常

声明异常一般是指声明已检查异常(checked)。某些情况下也可以自定义异常。声明异常可以让调用者清楚的知道会产生哪些异常以做相应处理。

声明异常需要用到关键字throws
比如java源码中FileInputStream类的一个构造器

public FileInputStream(String name) throws FileNotFoundException {
        this(name != null ? new File(name) : null);
}

这个声明很浅显易懂:在找不到参数name所指定的文件时抛出FileNotFoundException异常。

总结:一个方法必须声明所有可能抛出的已检查异常,对于未检查异常应该在通过修正代码去规避(如数组下标越界)。

注意:假如一个子类覆盖超类的一个方法,并且超类中这个方法声明了IOException异常,那么在子类中这个方法只能声明IOException或IOException的子类,不能声明其它异常。


抛出异常

在一个方法中抛出异常之前需要先声明异常或者捕获异常。

声明异常需要用到关键字throw
任然拿java源码中FileInputStream类的一个构造器来说明:

public FileInputStream(File file) throws FileNotFoundException {
        ……
        if (file.isInvalid()) {
            throw new FileNotFoundException("Invalid file path");
        }
        ……
}

上面代码的意思是,当参数file无效时抛出FileNotFoundException异常。


自定义异常类

开发中可能会遇到默认的异常类无法描述的异常,比如网络请求超时。定义异常类一般可以继承Exception类或者Exception的子类,并且可以包含一个带描述信息的构造器(可以直接调用toString打印)。

例子:

public class TimeOutException extends Exception {
    public TimeOutException(){}
    public TimeOutException(String text){
        super(text);
    }
}

捕获异常

声明和抛出都完成之后,最终是需要捕获的,如果一个异常抛出而没有任何地方进去捕获,那么程序就会终止。所以,捕获异常是必须要做的步骤。
捕获异常需要用到try/catch语句块。

try {
    //这里执行可能会抛出异常的代码
} catch (Exception e) {
    //产生Exception异常时执行
}finally {
    //当前面的语句块都完成之后执行,不管有没异常
}

捕获多个异常

在try语句块中可能产生多个异常,并且希望做出不同的处理,那么可以通过多个catch语句块进行处理:
这样写的时候要将子类写在前面父类写后面。

try {

}
catch(EOFException e){

}
catch(FileNotFoundException e){

}
catch(IOException e){

}

还可以这样:

try {

}
catch(EOFException | FileNotFoundException e){

}
catch(IOException e){

}

再次抛出异常

在catch中再次的抛出一个异常,这样可以改变异常的类型,并且调用者在捕获这个异常时可以通过getCause()获取原始异常。

try {

}
catch(IOException e){
    FileNotFoundException se = new FileNotFoundException("123456");
    se.initCause(e);
    throw se;

}

小技巧

finally子句在try/catch语句块中一般做收尾工作,如回收资源等。用下面的写法,在代码可读性上更好,并且finally语句块中的异常也会被捕获。但是,如果在try和finally中都有return,那么执行的是finally中的return。(如果只用finally回收标准资源,那么请看第二个技巧)

try {
    try {

    } finally {

    }
}
catch(IOException e){

}

如果想在try/catch语句执行完之后回收资源,比较好的方法是,将资源在try()中声明,那么所有实现了AutoCloseable接口的资源类型都将在try/catch语句执行完之后自动的关闭。要传入多个资源可以用;分隔。当然,如果还想做别的操作也可以加上finally,关闭资源默认会在finally之前执行完。

try (InputStream inputStream = new FileInputStream("1111")){        

}
catch(IOException e){

}

异常总结

  1. 如果一个异常可以通过修正代码去避免,那么就不要捕获或者抛出异常,避免它!
  2. 在一个方法中有多行代码都会抛出异常,那么就将他们写在同一个try/catch语句块中,不要写多个try/caych
  3. 标准异常中没有适合的异常描述类时应该自定义异常类
  4. 不要只捕获Exception异常这种范围很广的异常,尽量将异常的表达范围缩小。
  5. 在自己封装一些方法以供以后调用的时候,不要在方法中捕获异常,应该在调用的时候处理。(除非那个异常一万年才发生一次!)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值