专注Java教育14年 全国咨询/投诉热线:444-1124-454
星辉LOGO图
始于2009,口口相传的Java黄埔军校
首页 学习攻略 Java学习 Java中的异常处理机制

Java中的异常处理机制

更新时间:2022-12-27 12:09:13 来源:星辉 浏览852次

1.基础概念

不同于编译出错,这里的异常指的是运行时异常,指的是程序已经编译通过得到 class 文件了, 再由 JVM 执行过程中出现的错误,如我们之前常见的空指针异常、除0异常、数组越界异常等

如: 空指针异常

   public static void main(String[] args) {
        String str = null;
        System.out.println(str.equals("1"));
}

执行结果:

NullPointerException 即为空指针异常

JDK中的内置异常类均在java.lang包下

java.lang.Throwable类是Java中所有异常或者错误的超类

2.避免异常的两种方式

LBYL:

Look Before You Leap. 在操作之前就做充分的检查.

EAFP:

It's Easier to Ask Forgiveness than Permission. "事后获取原谅比事前获取许可更容易". 也就是先操作, 遇到问题再处理.

在java中主要是用EAFP方法,先做,如果有异常,我们再对异常处理

通常是用try - catch - finally进行捕获异常

如果某方法可能存在异常,但不想在这里处理,我们就可以使用throws抛出异常给调用它的上一级,上一级在调用该方法时使用try - catch - finally 捕获异常

3.异常的基本语法:

try - catch -finally捕获异常

        try{
//            有可能出现异常的代码块
        }catch ((异常类 异常对象)Exception e){
//            捕获到该异常后执行的代码
        }finally{
//            无论是否有异常发生都会执行的代码,善后处理
        }
    }

注意:

(1)catch语句可有0 - n个,即可以不写catch语句,也可以写多个,如果可能抛出的异常不止一个,就可以写多个catch语句

(2)finally语句可有0或1个,无论是否捕获到异常,均会执行finally语句,finally语句通常用于资源的关闭释放等操作

(3)catch语句中,catch的小括号里即是写捕获到的异常类的异常对象,如上述代码中的 Exception e,就是Exception 异常类的对象e

(4)这里要说明,Exception类是所有异常类的共同父类,所以当可能有多个异常或者具体是说明异常自己也不清楚的时候,可以用Exception类代替,但并不推荐这种写法,因为这样写不便于排查异常原因,所以通常还是选择用多个catch语句捕获异常

异常处理流程:

程序先执行 try 中的代码

如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.

如果找到匹配的异常类型, 就会执行 catch 中的代码如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.

无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).

如果上层调用者也没有处理的了异常, 就继续向上传递.一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止.

4.异常的体系:

(1)Error类是程序内部错误,程序员无法捕获处理,所以 我们主要关注Exception类及其子类

(2)异常按照是否必须显式处理又分为非受查异常和受查异常

非受查异常:

不强制要求处理,只有Error类和RuntimeException类及其子类是非受查异常,其他都是受查异常

受查异常:

必须使用try -catch捕获异常或者throws抛出异常,否则会编译报错!!!

5.关于异常的调用链

如果一个方法产生了异常,但并没有对该异常进行处理,则会将异常抛出给其方法调用者,也就是说,只要异常没有遇到处理方法,就会一直抛给调用它的上一级,直至被处理或者最后扔给JVM

在 JVM 中有一块内存空间称为 "虚拟机栈" 专门存储方法之间的调用关系. 当代码中出现异常的时候, 我们就可以使用 e.printStackTrace(); 的方式查看出现异常代码的调用栈.

6.关键字 throw 和throws

throws:用在方法声明上,明确表示该方法会产生该异常,但方法本身并没有对异常进行处理,所以该方法的异常会抛给调用它的上一级,所以 throws 关键字, 把可能抛出的异常显式的标注在方法定义的位置. 从而提醒调用者要注意捕获这些异常.

throw:用在方法内部,表示人为抛出异常,通常和自定义异常结合使用,在抛出异常后,该方法就会结束

如下面这段代码块,就是throw 和 throws 的用法

//    throws用于方法声明,提醒调用者注意对这一异常捕获处理
    public static int divide(int x, int y) throws ArithmeticException {
        if (y == 0) {
//            throw用于方法内部,人为抛出该异常
            throw new ArithmeticException("抛出除 0 异常");
        }
        return x / y;
    }

7.自定义异常类

Java 中虽然已经内置了丰富的异常类, 但是我们实际场景中可能还有一些情况需要我们对异常类进行扩展, 创建符合我们实际情况的异常.

例如可能我们会用到具体的登录异常,密码错误异常等,这些都需要我们自己去定义

自定义异常通常会继承自 Exception 或者 RuntimeException

继承自 Exception 的异常默认是受查异常

继承自 RuntimeException 的异常默认是非受查异常

下面以登录异常为例,我们自己创建两个异常类

import java.util.Scanner; 
//自定义异常类
//以用户登录为例
public class Login {
    private static String name = "阿衡";
    private static String password = "123456"; 
    public static void main(String[] args) {
        try {
            login();
            System.out.println("登录成功!");
        } catch (nameException e) {
            System.out.println("用户名错误!");
//            打印错误堆栈信息
            e.printStackTrace();
        } 
    } 
    //    因为nameException继承自受查异常,所以这里在抛出该异常时,方法声明必须throws该异常交至上一级try-catch,否则会编译报错
    public static void login() throws nameException {
        System.out.println("请输入您的用户名:");
        Scanner scanner = new Scanner(System.in);
        String name1 = scanner.next();
        System.out.println("请输入用户密码:");
        String password1 = scanner.next();
//        如果输入的用户名和预定的不一致,则抛出用户名错误异常
        if (!name1.equals(name)) {
            throw new nameException("用户名错误!");
        }
//        如果密码不一致,则抛出密码错误异常
        if (!password1.equals(password)) {
            throw new passwordException("密码错误!");
        }
    }
} 
//用户名错误异常
//受查异常,必须显示使用try-catch或throws抛出
class nameException extends Exception {
    public nameException(String msg) {
        super(msg);
    }
}
//密码错误异常
//非受查异常,可以只用于抛出而不使用try-catch或throws处理
class passwordException extends RuntimeException {
    public passwordException(String msg) {
        super(msg);
    }
}

值得注意的是,密码错误异常继承自非受查异常,所以我们可以不在login方法中用throws显式抛给上一级,在main中也没有显式的用catch语句捕获处理

但是,对于受查异常,我们看到,如果不处理会编译报错

最后,综上,我们来个重点小总结:

(1)java用try-catch-finally捕获异常进行处理

(2)throw关键字用于抛出异常

(3)throws关键字则是声明有该异常且我未处理

(4)对于本方法的异常没有处理的或者直接使用throw的我们都会抛给上一级让上一级捕获处理异常

(5)同时,我们可以自定义异常类

(6)最后,就是注意区分非受查异常(Runtimexception及其子类)和受查异常

提交申请后,顾问老师会电话与您沟通安排学习

免费课程推荐 >>
技术文档推荐 >>