目录
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处理。