(9) javaSE --异常

1、Java的异常

![在这里插入图片描述](https://img-blog.csdnimg.cn/e76655e0c95e4c62878254d227a383ef.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5oqW56uLRkxBRw==,size_10,color_FFFFFF,t_70,g_se,x_16
小伙子,别跑啊。。。。

上面是Java内置异常类之间的继承关系

1.1 异常的分类

1. 粉红色的是受检查的异常(checked exceptions) — 编译时异常

其必须被 try{}catch语句块所捕获,或者在方法签名里通过throws子句声明.
受检查的异常必须在编译时被捕捉处理,命名为 Checked Exception
是因为Java编译器要进行检查,Java虚拟机也要进行检查,以确保这个规则得到遵守.

编译时异常强制进行异常处理,否则就无法编译通过

2. 绿色的异常是 非受检查异常 — 运行时异常 (runtime exceptions)

运行时才会发生异常,需要程序员自己分析代码决定是否捕获和处理,比如 空指针,被0除...
使用RuntimeException定义的异常可以由用户选择性的来进行异常处理。

3. 声明为Error

则属于严重错误,如系统崩溃、虚拟机错误、动态链接失败等,
这些错误无法恢复或者不可能捕捉,将导致应用程序中断,Error 无需我们关心,不需要捕捉。

我们常常所说的异常,本质上是 程序在 运行时 出现错误时通知调用者的一种机制。
运行时异常

运行时

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

那为啥是出错了才通知,而不是提前预防呢?

1.2 防御式编程

代码不出问题,基本上是不存在的,我们所能做的就是,防止出错

LBYL

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

EAFP:

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

所以说异常的思想是,遇到问题再解决,不然,就没万事大吉。

说的这么牛逼轰轰的,咋用呢?

2、 异常的用法

2.1 上菜

不管异常是否发生,finally块均会被执行。

import java.util.Scanner;

public class Test1 {

    private int a = 0;
    private int[] arr;
    private int input;
    public void  arithmetic(){
        Scanner scanner = new Scanner(System.in);
        try {
            // 1、try中存放可能会发生异常的代码
            input = scanner.nextInt();
            System.out.println("---1 start try ---");
            System.out.println("Step 1: 1/0 " + input / this.a);
            System.out.println("Step 2:");
            System.out.println("---1 end try ---");
        } catch (ArithmeticException e){  // catch 括号中放入可能出现的异常类型和异常对象
            // 2、catch代码块放入处理异常的代码
            //    并且只能处理对应种类的异常
            // 打印出现异常的调用栈
            e.printStackTrace();
            this.a += 1;
            System.out.println("Step catch 1: " + input / this.a);
        } finally {
            /** 6、 finally 表示异常出现情况下得善后工作,如释放资源等。
             *    C++中需要手动释放资源,也常用来作为异常出现后的资源释放
             *    需要注意: 不管异常是否发生,finally块均会被执行。
             * */
            scanner.close();
            System.out.println("不管是否发生异常, finally块均会被执行");
        }


    }

    public void  printArr(){
        arr = new int[]{ 1, 2, 3, 4};
        arr = null;
        try {
            System.out.println("---2 start try ---");
            System.out.println("Step 1: " + arr[100]);
            System.out.println("---2 end try ---");
        } catch (NullPointerException e){
             // 3、catch 括号中放入可能出现的异常类型和异常对象,并且只能处理对应种类的异常
            // arr = null ,
            // try代码执行时,会发生 空指针异常
            e.printStackTrace();
            System.out.println("Step catch 1: NullPointerException e ");
        } catch (ArrayIndexOutOfBoundsException|ArithmeticException e) {
            // 4、如果有多个异常的处理方式相同,则可以用 | 进行连接
            e.printStackTrace();
            System.out.println("Step catch 2: ArrayIndexOutOfBoundsException e ");
        }

        //5、也可以使用一个catch Exception类 捕获所有的异常 ,因为 Exception 类是所有异常类的父类,
        catch (Exception e) {
            e.printStackTrace();
            System.out.println("使用 Exception 捕获所有异常");
        }
    }
    
}

2.2 菜不够?

那我要是 在catch 括号中放入的异常类型 不是 实际出现异常的类型,
导致catch 无法捕获, 那 finally还会执行吗?

不管异常是否发生,finally块均会被执行。


public void  arithmetic1(){
    Scanner scanner = new Scanner(System.in);
    try {
        input = scanner.nextInt();
        System.out.println("Step 1: 1/0 " + input / this.a);
    } catch (NullPointerException e){  // catch 括号中放入可能出现的异常类型和异常对象
        // 正确异常类型      
        // ArithmeticException e 
        // NullPointerException 无法捕捉
     
        e.printStackTrace();
        this.a += 1;
        System.out.println("Step catch 1: " + input / this.a);
    } finally {
        /**   需要注意: 不管异常是否发生,finally块均会被执行。
         *    注意顺序: 无法catch 异常,catch中的语句不会执行, finally中代码执行后,再抛出异常
         *             这样确保了 finally 一定会被执行
         * */
        scanner.close();
        System.out.println("不管是否发生异常, finally块均会被执行");
    }
}

如果本方法没有对应的处理异常的方式,就无法在当前方法中捕获到异常,就会沿着调用栈向上传递
如果向上一直传递到main方法也都没有对应的异常处理方式,最终就会交给JVM处理,程序就会终止异常

2.3 加个蛋?

我直接在catch中 return, finally 还会执行吗?

不管异常是否发生,finally块均会被执行。

不敲code行不行? 行。


public int  arithmetic1(){
    Scanner scanner = new Scanner(System.in);
    try {
        input = scanner.nextInt();
        System.out.println("Step 1: 1/0 " + input / this.a);
        return input / this.a;
    } catch (NullPointerException e){  // catch 括号中放入可能出现的异常类型和异常对象
        // 正确异常类型
        // ArithmeticException e
        // NullPointerException 无法捕捉

        e.printStackTrace();
        this.a += 1;
        System.out.println("Step catch 1: " + input / this.a);
    } finally {
        /**   需要注意: 不管异常是否发生,finally块均会被执行。
         *             那我要是在try中return, finally还会执行吗?
         *             --- 仍然会执行
         *                 并且如果 finally 中也含有 return,func 会返回 finally 中 return的值
         *                 因此,避免在return 中 return。。
         *                 
         *    注意顺序: 无法catch 异常,catch中的语句不会执行, finally中代码执行后,再抛出异常
         *             这样确保了 finally 一定会被执行
         * */
        scanner.close();
        System.out.println("不管是否发生异常, finally块均会被执行,那我要是在try中return, finally还会执行吗?");
        return 10;
    }
}

3、 声明 和 抛出异常

声明异常: throws 关键字

我是社畜

抛出异常: throw 关键字

滚犊子,你就是个社畜

捕获异常: catch

抓住这个社畜

1. throw用于方法内部,主要表示手动产生异常类对象抛出,而非JVM抛出。

2. throws主要在方法声明上使用,明确告诉用户本方法可能产生的异常,同时该方法可能不处理此异常。

在进行方法定义的时候,
如果要告诉调用者  本方法可能产生哪些异常,就可以使用throws方法进行声明。
即,如果该方法出现问题后不希望自行处理,就使用throws抛出。


如果现在调用了throws声明的方法,那么在调用时必须明确的使用try..catch..进行捕获,
因为该方法有可能产生异常,所以必须按照异常的方式来进行处理。


Main方法本身也属于一个方法,所以主方法上也可以使用throws进行异常抛出,
这个时候如果产生了异常就会交给JVM处理。

throw是直接编写在语句之中,表示人为进行异常的抛出。
如果现在异常类对象实例化不希望由JVM产生而由用户产生,就可以使用throw来完成

4、RuntimeException (重点)

public class Test {public static void main(String[] args){
    String str = "100" ;
    int num = Integer.parseInt(str) ;
    System.out.println(num * 2);
}

这个方法上已经明确抛出异常,但是在进行行调用的时候发现,即使没有进行异常处理也可以正常执行。这个就属于RuntimeException的范畴。
很多的代码上都可能出现异常,例如"10/0"都可能产生异常,如果所有可能产生异常的地方都强制性异常处理理,这个代码就太复杂了。
所以在异常设计的时候,考虑到一些异常可能是简单问题,所以将这类异常称为RuntimeException,也就是使用RuntimeException定义的异常类可以不需要强制性进行异常处理理。

5、自定义异常

在Java里,针对于可能出现的公共的程序问题都会提供有相应的异常信息,
但是很多时候这些异常信息往不不够我们使用。

例如,现在有需求:

在进行加法运算时,如果发现两个数相加内容为50,那么就应当抛出一个AddException异常。
这种异常Java不会提供,所以就必须定义一个属于自己的异常类。

自定义异常类可以继承两种父类: Exception、 RuntimeException。

class AddException extends Exception{
    public AddException(String message) {
        super(message);
    }
}

class MyNullPointerException extends  RuntimeException{
    public MyNullPointerException(String message) {
        super(message);
    }
}

public class TestException {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int excep = scanner.nextInt();
        try {
            if(excep == 1) {
                throw new MyNullPointerException("抛出自定义 MyNullPointerException!");
            } else {
                throw new AddException("抛出自定义 AddException!");
            }
        } catch (MyNullPointerException e) {
            e.printStackTrace();
        } catch (AddException e) {
            e.printStackTrace();
        }
    }
}

AddException: 抛出自定义 AddException!
	at Main.main(Main.java:22)
 
MyNullPointerException: 抛出自定义 MyNullPointerException!
	at Main.main(Main.java:28)

倘若不对继承自 Exception的异常进行捕获,最终会报错,
因为继承自Exceptionde的异常属于编译异常,受异常检查,必须捕获。

对于继承自 RuntimeException,异常若在方法栈调用中没有没有处理,最终会被JVM处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值