Java 认识异常

1. 异常的背景

初识异常

算数异常

数组下标越界异常

空指针异常

防御式编程

异常的好处

2. 异常的基本用法

捕获异常

异常处理流程

抛出异常

异常说明

3. Java 异常体系

4. 自定义异常类


1. 异常的背景

=========

初识异常


在之前的学习中,我们其实已经接触到了Java当中的“异常”了。

算数异常


System.out.println(10 / 0);

// 执行结果

// Exception in thread "main" java.lang.ArithmeticException: / by zero

数组下标越界异常


int[] arr = {1, 2, 3};

System.out.println(arr[100]);

// 执行结果

// Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 100

空指针异常


public class Test {

    public int num = 10;

    public static void main(String[] args) {

        Test t = null;

        System.out.println(t.num);

    }

}

// 执行结果

// Exception in thread "main" java.lang.NullPointerException

所谓异常指的就是程序在 运行时 出现错误时通知调用者的一种机制.

关键字 “运行时”

有些错误是这样的, 例如将 System.out.println 拼写错了, 写成了 system.out.println. 此时编译过程中就会出错, 这是 “编译期” 出错.

运行时指的是程序已经编译通过得到 class 文件了, 再由 JVM 执行过程中出现的错误.

防御式编程


错误在代码中是客观存在的. 因此我们要让程序出现问题的时候及时通知程序猿. 我们有两种主要的方式:

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

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

其实很好理解,举个栗子:

比如说有一个你非常喜欢的女生,你想要去拉她的小手。

  1. 第一种情况:先问一下可以拉她的小手吗,获取同意后再拉。(LBYL)
  1. 第二种情况:直接拉上,大不了就是给你一巴掌呗,再道歉就完了 。(EAFP)

异常的好处


例如, 我们用伪代码演示一下开始一局王者荣耀的过程:

LBYL 风格的代码(不使用异常)


boolean ret = false;

ret = 登陆游戏();

if (!ret) {

    处理登陆游戏错误;

    return;

}

ret = 开始匹配();

if (!ret) {

    处理匹配错误;

    return;

}

ret = 游戏确认();

if (!ret) {

    处理游戏确认错误;

    return;

}

ret = 选择英雄();

if (!ret) {

    处理选择英雄错误;

    return;

}

ret = 载入游戏画面();

if (!ret) {

    处理载入游戏错误;

    return;

}

......

EAFP 风格的代码(使用异常)


try {

    登陆游戏();

    开始匹配();

    游戏确认();

    选择英雄();

    载入游戏画面();

    ...

} catch (登陆游戏异常) {

    处理登陆游戏异常;

} catch (开始匹配异常) {

    处理开始匹配异常;

} catch (游戏确认异常) {

    处理游戏确认异常;

} catch (选择英雄异常) {

    处理选择英雄异常;

} catch (载入游戏画面异常) {

    处理载入游戏画面异常;

}

......

对比两种不同风格的代码, 我们可以发现, 使用第一种方式, 正常流程和错误处理流程代码混在一起,

代码整体显的比较混乱. 而第二种方式正常流程和错误流程是分离开的, 更容易理解代码。

2. 异常的基本用法

===========

捕获异常


基本语法:


try{

    有可能出现异常的语句 ;

}catch (异常类型 异常对象) {

}

finally {

    异常的出口

}

  • try 代码块中放的是可能出现异常的代码.

  • catch 代码块中放的是出现异常后的处理行为.

  • finally 代码块中的代码用于处理善后工作, 会在最后执行.

  • 其中 catch 和 finally 都可以根据情况选择加或者不加.

示例1  不处理异常 :


int[] arr = {1, 2, 3};

System.out.println("before");

System.out.println(arr[100]);

System.out.println("after");

// 执行结果

before

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 100

我们发现一旦出现异常, 程序就终止了. after 没有正确输出。

示例2  使用 try catch 处理异常:


int[] arr = {1, 2, 3};

try {

    System.out.println("before");

    System.out.println(arr[100]);

    System.out.println("after");

} catch (ArrayIndexOutOfBoundsException e) {

    // 打印出现异常的调用栈

    e.printStackTrace();

}

System.out.println("after try catch");

// 执行结果

before

java.lang.ArrayIndexOutOfBoundsException: 100

    at demo02.Test.main(Test.java:10)

after try catch



我们发现, 一旦 try 中出现异常, 那么 try 代码块中的程序就不会继续执行, 而是交给 catch 中的代码来执行. catch 执行完毕会继续往下执行.

示例3  catch 只能处理对应种类的异常:


int[] arr = {1, 2, 3};

try {

    System.out.println("before");

    arr = null;

    System.out.println(arr[100]);

    System.out.println("after");

} catch (ArrayIndexOutOfBoundsException e) {

    e.printStackTrace();

}

System.out.println("after try catch");

// 执行结果

before

Exception in thread "main" java.lang.NullPointerException

at demo02.Test.main(Test.java:11)

此时, catch 语句不能捕获到刚才的空指针异常. 因为异常类型不匹配.

示例4  catch 可以有多个:


int[] arr = {1, 2, 3};

try {

    System.out.println("before");

    arr = null;

    System.out.println(arr[100]);

    System.out.println("after");

} catch (ArrayIndexOutOfBoundsException e) {

    System.out.println("这是个数组下标越界异常");

    e.printStackTrace();

} catch (NullPointerException e) {

    System.out.println("这是个空指针异常");

    e.printStackTrace();

}

System.out.println("after try catch");

// 执行结果

before

这是个空指针异常

java.lang.NullPointerException

at demo02.Test.main(Test.java:12)

after try catch

一段代码可能会抛出多种不同的异常, 不同的异常有不同的处理方式. 因此可以搭配多个 catch 代码块.

如果多个异常的处理方式是完全相同, 也可以写成这样:


catch (ArrayIndexOutOfBoundsException | NullPointerException e) {

    ...

}

示例5  也可以用一个 catch 捕获所有异常(不推荐):


int[] arr = {1, 2, 3};

try {

    System.out.println("before");

    arr = null;

    System.out.println(arr[100]);

    System.out.println("after");

} catch (Exception e) {

    e.printStackTrace();

}

System.out.println("after try catch");

// 执行结果

before

java.lang.NullPointerException

at demo02.Test.main(Test.java:12)

after try catch

由于 Exception 类是所有异常类的父类. 因此可以用这个类型表示捕捉所有异常。

注意:catch 进行类型匹配的时候, 不光会匹配相同类型的异常对象, 也会捕捉目标异常类型的子类对象.

如刚才的代码, NullPointerException 和 ArrayIndexOutOfBoundsException 都是 Exception 的子类, 因此都能被捕获到.

示例6  finally 表示最后的善后工作, 例如释放资源:


int[] arr = {1, 2, 3};

try {

    System.out.println("before");

    arr = null;

    System.out.println(arr[100]);

    System.out.println("after");

} catch (Exception e) {

    e.printStackTrace();

} finally {

    System.out.println("finally code");

}

// 执行结果

before

java.lang.NullPointerException

at demo02.Test.main(Test.java:12)

finally code

无论是否存在异常, finally 中的代码一定都会执行到.

示例7  使用 try 负责回收资源:


try (Scanner sc = new Scanner(System.in)) {

    int num = sc.nextInt();

    System.out.println("num = " + num);

} catch (Exception e) {

    e.printStackTrace();

}
  • 29
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值