Java编程思想 第十二章:通过异常处理错误

这一章主要是Java中的异常处理,这一部分我在学习Java之前是完全没有接触过的,这是一个新的概念,之前在写Java程序中也会遇到处理异常的情况,但始终是一知半解的,这下终于有机会来揭开Java中异常处理这个神秘的面纱了。
Java中自带的有很多异常比如NullPointerException等等,所有类型的类型都是一个类,他们都继承自根类Throwable,当遇到一些常见错误时(比如:算术异常,空指针异常等),会自动抛出对应类型的异常,这些在Java都是已经定义好的,但是如果你想根据自己的逻辑来抛出异常,则就需要自己来new一个异常,并通过关键字throw来抛出这个异常。

...
throw new NullPointerException("t = null");
...

捕获异常

那么怎么来捕获异常呢?这就要用到try-catch-finally块了。如果在方法内部抛出了异常(或者在方法内部调用的其他办法抛出了异常),这个方法将在抛出异常的过程中结束。要是不想方法就此结束,则可以在方法内设置一个特殊的块来捕获异常,这个就是try块:

try {
	// 可能产生异常的代码块	
}

既然有了捕获异常的代码块,那么处理异常的代码块怎么声明呢?我们用catch关键字来处理异常,它必须紧跟在try后面,但它不是必须存在的,后面会详细讲到。在catch块中,你可以处理try中抛出的异常,对异常的处理完全由你决定,继续向上层抛出也是可以的。此外,还有一个finally块,这个块比较特殊,它是无论如何都会执行到的代码块,无论有没有catch块。接下来看几个特殊的代码:
正常的代码是这样的,throw相当于是一个特殊的返回值。

public int fun(int i){
        int result = 0;
        if(i == 0)
            throw new IllegalArgumentException("除数为0");
        result = 10/i; //若抛出异常,则该行及之后的代码都不再执行
        return result;
}

当抛出异常的代码块被try-catch包围时,try-catch之外的代码正常执行,前提是该代码块里面没有可达到的return语句。

public int fun(int i){
        int result;
        try{
            if(i == 0)
                throw new IllegalArgumentException("除数为0");
            result = 10/i;
        }catch (Exception e) {
            System.out.println(e);
        }
        System.out.println("here"); //无论是否有异常,都会执行到这里
        return result;
    }

也就是说,只有在该方法里没有未被处理的异常和可以到达的return语句,try-catch块之后的代码都可以执行到。如果你在catch里又抛出了新的异常,那么try-catch之后的代码同样也不会执行。
当try-catch-finally里都有返回语句时,那么较晚执行的return语句会覆盖它之前的return语句。下面的代码,无论有无异常,返回结果都是3。

public int fun(int i){
        int result = 0;
        try{
            if(i == 0)
                throw new IllegalArgumentException("除数为0");
            return 1;
        }catch (Exception e) {
            System.out.println(e);
            return 2;
        }finally {
            System.out.println(12);
            return 3;
        }
    }

方法继承中的异常覆盖

异常是一种特殊的类,所以它也能被继承。继承某个类之后,你覆盖的方法抛出的异常可以是该异常的基类吗?或者子类吗?构造器中抛出的异常继承关系和普通方法中的一样吗?
这些问题的答案,你可以在书上的例子上找到,对应的练习也能够帮助你理解。下面是自己实际敲代码总结出来的结果,代码就不在放了。

  • 基类或者接口中的方法未抛出异常的,子类中也不能抛出任何异常;
  • 基类或者接口中的方法抛出异常,子类对应的方法可以不抛出任何异常。若抛出异常,则必须是基类中抛出的异常的子类,或者就是它本身;这一点在implements的接口中也适用。
  • 如果一个类既继承了类又implements了某个接口,且它们都有相同方法签名(此处还包括抛出的异常是同一条异常类继承链上的)的方法,那么覆写该方法是ok的,抛出的异常是基类或者接口中继承链最长的异常类本身或者其子类。
  • 子类的构造器抛出的异常A要包含用到的基类的构造器所抛出的异常B(此处包含的意思是:A可以和B相同,或者A是B的父类,但A不能是B的子类,这刚好和方法异常的继承相反),当然你可以额外抛出自己想要抛出的异常。
  • 异常抛出的匹配机制是就近原则,派生类的对象也可以匹配其基类的处理程序,下面的程序是合法的:
try{
     throw new PopFoul();
 }catch(Foul e){ //Foul是PopFoul的父类
     //do something
 }catch(PopFoul e) {
     //执行不到这里,idea编译器会报错
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值