Java 中的异常

异常:就是出现的问题。

在Java中异常被当成对象进行处理,所有的异常类都继承于Throwable类,如果Java提供的异常类并不能满足需求,用户还可以自己定义一个异常类。

下面是异常体系结构:

7d1e2d697c3e4834a5cf8e0401abd22a.png

Throwable又分成了Error和Exception。本文仅讨论Exception及其子类,因为Error出现的话也不是我们能够处理的。

Exception又分为RuntimeException和其他异常。

由程序逻辑错误导致的异常属于RuntimeException及其子类异常,也叫不受查(unchecked)异常并不需要进行异常处理,当然也可以处理并不会报错。比如以下几种常见的异常:

①错误的类型转换;

②数组索引越界;

③访问空指针。

而程序本身没有其他问题,由于像IO这类问题导致的异常属于其他异常,也叫受查(checked)异常,必须显示地进行异常处理(捕获或者抛出异常),否则会报错。比如以下这种情况:

①读取本地不存在的文件。

②网络断开连接。

当出现异常时有两种解决方式:

1、捕获异常进行处理,即try - catch捕获异常。

2、将异常传递给方法调用者,即抛出异常。

如果调用了一个抛出异常的方法,那么必须进行处理或传递,如何选择呢?

如果知道如何处理的异常可以进行捕获,而那些不知道怎样处理的异常继续向上抛出。

向上抛出

什么时候需要向上抛出异常?

(1)传递一个危险信号,需要让调用者知道;

(2)本方法没有处理异常的能力,方法调用者有能力处理;

(3)抛出是框架层面的选择。

这里介绍一下JVM默认的异常处理方式:

①结束程序的运行,即异常下面的代码就不会执行了;

②将异常信息、异常原因和异常位置以红色字体输出在控制台。

什么是抛出异常?

1、throws

  • 在方法的后面使用throws关键字将可能会抛出的异常进行声明,表示此方法不处理异常,一旦发生异常会将异常进行抛出,交由方法的调用处进行处理。

具体格式如下:

public Image loadImage(String s) throws IOException {

}

2、throw

  • 使用关键字 throw 手动抛出一个异常,即手动进行异常类对象的实例化操作。
throw new EOFException();

关于throws和throw两个的细节:

  1. 都是抛出异常,可同时使用,也可单独使用;
  2. 都将异常传递给方法的调用者;
  3. 都会结束当前方法中剩余代码的执行。

捕获异常

使用try-catch语句。捕获单个异常格式如下:

try {     
    编写可能会出现异常的代码
} catch (异常类型  e)
{     
    处理异常的代码     //记录日志/打印异常信息/继续抛出异常
}

在一个try语句块中可以捕获多个异常类型,并对不同类型的异常做出不同的处理。可以按照下列方式为每个异常类型使用一个单独的catch子句:

try{
     编写可能会出现异常的代码
}catch(FileNotFoundException e) {  //当try中出现FileNotFoundException 类型异常,就用该catch来捕获
     处理异常的代码
     //记录日志/打印异常信息/继续抛出异常
}catch(UnknownHostException e) {  //当try中出现UnknownHostException 类型异常,就用该catch来捕获
     处理异常的代码
     //记录日志/打印异常信息/继续抛出异常
}

注意多个catch语句中的异常类型不能一样,并且父类的异常应该放在子类的后面,否则会报错,因为如果写在最前面的话,异常在第一个岔路口就被抓住了,后面的捕获就多余了,依据Java的严谨性,不允许出现这种情况。

而且异常对象可能包含与异常本身有关的信息。要想获得对象的更多信息,可以试着使用e.getMessage()或者e.printStackTrace()得到详细的错误信息(如果有的话),或者使用 e.getClass().getName() 得到异常对象的实际类型。

在Java SE7中,同一个 catch 子句中可以捕获多个异常类型,中间用 | 进行隔开。例如,假设对应缺少文件和未知主机异常的动作是一样的,就可以合并catch子句:

try {
    编写可能会出现异常的代码
} catch (FileNotFoundException| UnknownHostException e) {
    处理异常的代码
     //记录日志/打印异常信息/继续抛出异常
} catch(I0Exception e) {
    处理异常的代码
     //记录日志/打印异常信息/继续抛出异常
}

那么什么时候抛出异常?什么时候捕获异常呢?

解释1

一句话回答就是“没有金刚钻,不揽瓷器活”。

如果你发现了某个问题,你又没能力解决,你就该汇报给上级,这就是【抛出异常】。

如果你接到了下属汇报的问题,你也没能力解决,你就只好再汇报给你的上级,这就是【继续抛出】。

只有你有能力解决的情况下,你才可以把这个问题解决掉,这就是【捕获异常】。

总结就是:谁遇到困难,谁抛出异常。谁解决问题,谁捕获异常。

下层抛出上层进行捕获。

细节1:

如果一个异常没有在任何地方被捕获,那么就会由虚拟机调用默认的异常处理机制,结束程序的运行,并在控制台打印异常有关信息。

前面还没懂吗,你不用throw抛出异常,调用者就接收不到异常,就无法捕获异常并处理,最后会交给虚拟机处理,这样程序就会停止运行,之后的代码无法运行。

给出一个代码例子: 

public class test05 {
    public static void main(String[] args) {
        int[] arr = null;
        try {
            int max = getMax(arr);
        } catch (NullPointerException e) {
            System.out.println("空指针异常");
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("索引越界异常");
        }
    }
    public static int getMax (int[] arr) throws NullPointerException, ArrayIndexOutOfBoundsException{
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }
        }
        return max;
    }
}

finally

不论是否有异常被捕获,finally子句都将被执行。比如打开了一些资源,但是异常发生时,希望这些文件可以关闭,就可以使用finally。下面举一个例子,其中的1,2...表示代码:

InputStream in = new FileInputStream(...);
try {
    // 1
    code that throw exceptions
    // 2
} catch {
    // 3
    show error message
    // 4
} finally {
    // 5 
    in.close();
}
// 6

1、try语句中是否有异常发生? 

①try语句中没有异常发生。那么try语句中的所有代码都会被执行,然后执行finally语句中的所有代码,接着执行finally后面的代码,即执行标注的1,2,5,6。

2、try语句中有异常发生,但是catch语句是否可以捕获?

(1)try语句中发生一个可以由catch语句捕获的异常,而catch语句中是否又抛出了异常?

①catch语句中没有抛出了异常。首先会执行try语句中异常发生之前的代码,然后执行catch语句中的所有代码,接着是finally语句中的所有代码,最后执行finally后面的代码,即执行标注的1,3,4,5,6。

②catch语句抛出了异常。还会执行finally子句吗?必须执行,要不然finally还有什么用呢,就是不会实现finally子句后面的语句了,结束方法的运行。即1,3,5。

警告:当 finally 子句包含return语句时,将会出现一种意想不到的结果。假设使用return语句从try语句块中退出。在方法返回前,finally子句的内容将被执行。如果 finally 子句中也有一个return语句,这个返回值将会覆盖原始的返回值。下面是一个例子:

public static int f(int n) {
    try {
        int r= n* n;
        return r;
    } 
    finally {
        if(n == 2)
            return 0;
    }
}

如果调用f(2),那么 try语句块的计算结果为r=4,并执行return 语句。然而,在方法真正返回前,还要执行 finally 子句。finally 子句将使得方法返回 0,这个返回值覆盖了原始的返回值 4。

总结就是:假如try和finally子句中都带有return返回值,最终会返回finally子句中的返回值。

  • 17
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java异常机制是一种用于处理程序执行期间可能出现的错误情况的机制。Java异常分为两类:受检异常(Checked Exception)和非受检异常(Unchecked Exception)。其,受检异常必须在代码进行捕获和处理,否则编译器将会报错。而非受检异常则不需要在代码进行捕获和处理,但是如果不进行处理,会导致程序的异常终止。 Java异常机制通过抛出异常对象来表示程序出现的异常情况,通常情况下,异常对象包含了异常的类型、异常的消息以及异常发生的位置等信息。当程序执行过程出现异常情况时,就会抛出相应的异常对象,这个异常对象会被传递给调用栈上的上层方法,直到被捕获或者到达程序的顶层方法。 Java异常机制主要由三个关键字来实现:try、catch和finally。try块包含可能抛出异常的代码,catch块用于捕获并处理异常,finally块则用于执行一些必须要完成的代码,无论是否抛出异常都会执行。 下面是一个简单的Java异常处理的例子: ``` public class ExceptionDemo { public static void main(String[] args) { try { int num = Integer.parseInt(args[0]); System.out.println("10 / " + num + " = " + (10 / num)); } catch (ArithmeticException e) { System.out.println("除数不能为0"); } catch (NumberFormatException e) { System.out.println("请输入数字"); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("请至少输入一个参数"); } finally { System.out.println("程序执行完毕"); } } } ``` 在这个例子,我们使用了try-catch-finally关键字来处理用户输入的参数,当输入参数不满足条件时,就会抛出相应的异常。catch块用于捕获并处理异常,finally块则用于执行一些必须要完成的代码,无论是否抛出异常都会执行。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值