java基础_异常处理机制

异常处理机制说明

自计算机语言诞生以来,程序异常处理永远是一个无法绕过去的话题。无论是C、C++还是JAVA程序设计语言。都或多或少的支持程序的异常处理。

JAVA最主要的作用就是用来构建大型应用系统;因此JAVA所面临的异常场景必然是多种多样和复杂的。还好,java程序的设计者为我们提供了简单、易用的异常处理机制,使得程序员可以通过非常简洁、高效的代码来处理程序异常。

try/catch异常处理

我们需要设计一个函数实现除法功能,这听起来非常的简单,但是我们必须要考虑一种场景,那就是分母为0的情况,应该怎么处理?分母为0这种场景我们就称为异常场景(因为它并没有任何实质性的意义)。在此例子中的异常场景我们只需要返回 -1 即可。

设计这个函数的目的事为了让读者了解JAVA程序的异常处理机制。

/**
 *
 * @param numerator 分子
 * @param denominator 分母
 * */
public static double division(long numerator,long denominator){
    //捕获异常
    try{
        return numerator/denominator;
    }catch (Exception ex){
        //异常处理程序
        ex.printStackTrace();
        return -1;
    }
}

1、我们可以看到Java中通过try{}catch(){}的方式来对异常进行处理;try负责捕获异常,catch负责处理异常情况。

2、被try{} 包裹的区域我们称为监控区域,表明这块区域在程序运行过程中是有可能出现异常情况的。当虚拟机在程序运行过程中捕捉到异常之后,会根据捕捉到的异常类型(上面例子为Exception),跳转到对应的异常处理程序中去执行。

3、Exception是JAVA对同类型异常的一个封装,我们在这里不过多的深入;会在下面 “java标准异常” 章节中详细的介绍这个抽象类。

throws(异常说明)和throw(异常抛出)

上面简单介绍了使用try/catch来进行异常处理;但是在很多场景下,程序员或是无法处理此种异常或是需要将此异常抛出到使用方去处理。

throw(异常抛出):表明当前异常不在当前方法中去处理,而是抛出给使用此方法的程序去处理。

throws(异常说明):表明当前方法在使用过程中可能会发生某种异常,需要使用方去处理。


/**
 * 将字符串转换为Date
 * */
public static Date strToDate(String str) throws ParseException{
    SimpleDateFormat simpleDateFormat
            =  new SimpleDateFormat("yyyy-MM-dd");
    try {
        return simpleDateFormat.parse(str);
    } catch (ParseException ex) {
        //此方法,不进行异常处理,直接抛出给使用方去处理
       throw ex;
    }
}

上面是一个将字符串转换为日期类型的函数,对于ParseException解析异常,我们将不在函数里面去处理,而是抛出给调用的程序去处理;这种异常处理方式,使得程序员在编写java程序的过程中有了更多的选择,相当的完美。

当使用throw抛出了ParseException异常之后,该方法必须使用throws声明异常(告诉使用方使用此方法有可能会发生异常),否则程序无法编译通过。

当然并不是所有的异常被抛出之后都需要throws进行异常声明;从RuntimeException继承的异常被抛出的时候,不需要异常声明;

关于RuntimeException 不需要异常声明的原因我们会在 “java标准异常” 章节进行说明。

使用finally 进行清理

对于一些代码,可能希望无论是否有异常抛出,他们都必须得到执行。这个时候我们可以使用finally子句,来达到这个目的。

这里我们以模仿文件操作为例子来进行说明:

public static boolean oprateFile(int i){
    System.out.println("打开文件...");
    try{
        if(i==1){
            System.out.println("读文件...");
            return true;
        }else if (i==2){
            System.out.println("写文件...");
            return true;
        }else{
            throw new FileNotFoundException("");
        }


    }catch (FileNotFoundException ex){
        System.out.println("该文件没有发现...");
        return false;
    }finally {
        System.out.println("关闭文件...");
    }
}

从上面的例子中我们可以发现,无论 i 为任何值,都会执行关闭文件操作。而且不论在任何地方执行return操作,都不会影响finally块代码的执行。

java标准异常

Throwable:这个java类是所有异常的父类。

Throwable 可以分为两种类型 Error 和 Exception ; Error表示系统错误(一般不用你关心),Exception是可以被抛出的所有类的基本类型,一般用户方法中的异常都是继承于这个类。

RuntimeException(特例): RuntimeException 和所有继承自 RuntimeException 的类,我们称为 “不受检查异常”;所谓不受检查异常,指的是程序员不需要对可能发生的RuntimeException异常进行异常说明(即可能发生RuntimeException异常的方法,不需要通过throws来进行声明)。why,java为什么要这么干?

如类似空引用检查这种:
if(t == null)
throw new NullPointrException();
如果对每个引用都执行这种异常检查,将会使得代码非常繁杂且不可维护;所以java的解决办法是对于这种大批量的运行时异常检查。都由JAVA虚拟机自动抛出,也不必再异常说明中罗列出来。

自定义异常

java不可能包含开发过程中遇到的所有的异常情况,所以有时候我们需要自己定义一些异常出来;

对于异常来说,最重要的就是类名本身,所以自定义异常相当的简单,只需要从已有的异常类继承(最好是选择意思相近的异常类)即可。

class MyException extends Exception{
    //定义一个无参数构造器
    public MyException(){}
    //定义一个有参数的构造器
    public MyException(String msg){super(msg);}
}

如上,我们只是需要继承Exception异常基类,然后提供一个无参或者参数为String类型的构造器即可。

栈轨迹

try {
    return simpleDateFormat.parse(str);
} catch (ParseException ex) {
    //打印栈调用信息 
    ex.printStackTrace();
}

可以通过printStackTrace方法来打印栈调用信息,将返回一个由栈轨迹中的元素构成的数组,其中每一个元素都表示栈中的一帧;第0个元素是栈顶元素,并且是调用序列中的最后一个方法调用。

异常链

我们首先看下面这个程序:

package com.java.basic.exception;

public class ExceptionDemo002 {

    public static void main(String[] args) {
        try {
            a();
        } catch (MyException_a myException_a) {
            myException_a.printStackTrace();
        }
    }

    public static void a() throws MyException_a{
        try {
            b();
        } catch (MyException_b myException_b) {
            throw new MyException_a();
        }

    }
    public static void b() throws MyException_b{
        throw new MyException_b();
    }

}

class MyException_a extends Exception{ }
class MyException_b extends Exception{ }

输出结果为:

com.java.basic.exception.MyException_a
	at com.java.basic.exception.ExceptionDemo002.a(ExceptionDemo002.java:20)
	at com.java.basic.exception.ExceptionDemo002.main(ExceptionDemo002.java:9)

Process finished with exit code 0

我们能够观察到,在这种连续抛出异常的场景下,第一次抛出的异常被丢失了。针对这种情况,异常链的概念被提出。

所谓异常链就是在捕获一个异常然后抛出另外一个异常的场景之下,将原始异常保存下来。

使用方法就是使用initCause()将其串起来,如下:

package com.java.basic.exception;

public class ExceptionDemo002 {

    public static void main(String[] args) {
        try {
            a();
        } catch (MyException_a myException_a) {
            myException_a.printStackTrace();
        }
    }

    public static void a() throws MyException_a{
        try {
            b();
        } catch (MyException_b myException_b) {
            MyException_a ex =  new MyException_a();
            ex.initCause(myException_b);
            throw ex;
        }

    }
    public static void b() throws MyException_b{
        throw new MyException_b();
    }

}

class MyException_a extends Exception{ }
class MyException_b extends Exception{ }

输出如下:

com.java.basic.exception.MyException_a
	at com.java.basic.exception.ExceptionDemo002.a(ExceptionDemo002.java:17)
	at com.java.basic.exception.ExceptionDemo002.main(ExceptionDemo002.java:7)
Caused by: com.java.basic.exception.MyException_b
	at com.java.basic.exception.ExceptionDemo002.b(ExceptionDemo002.java:24)
	at com.java.basic.exception.ExceptionDemo002.a(ExceptionDemo002.java:15)
	... 1 more

Process finished with exit code 0

异常匹配

try {
    ...
} catch (MyException_a a) {
    ...
} catch (MyException_b b) {
     ...
}

如上,如果存在多个异常处理块(catch),那么异常处理系统会根据异常处理类型(也包括基类)找到最近的(按书写顺序)异常处理块进行匹配;匹配成功之后,它就认为异常已经得到处理,不在继续向下查找。

java异常总结

1、可以看到,在java中,采用了将正常业务代码和异常处理代码相互分开的做法,这增加了代码的可读性,使其便于维护。

2、java 采用了 “被检查的异常”,强制程序员去处理异常和声明异常,这样做的好处是减少了新手程序员犯错误的可能,增加了程序的健壮性。

结尾

由于编者水平有限,所举案例和描述如有不恰当的地方,欢迎大家不吝指正。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值