目录
1、Java的异常

 小伙子,别跑啊。。。。
上面是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处理。










