异常
文章目录
1. 认识异常(※)
通过如下代码看看异常:
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int a = scanner.nextInt();
System.out.println(a);
}
按照逻辑是在输入时输入整型,但是当输入其他类型时:
![](https://i-blog.csdnimg.cn/blog_migrate/faf3d5ec2082ac9fbea04820fb32e90d.png)
注意当出现这种运行时异常时,程序直接终止!不会执行后面代码。
2. 处理异常
2.1 if-else 处理异常
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
if(!scanner.hasNextInt()){
System.out.println("输入的不是整型!");
}else{
System.out.println(scanner.nextInt());
}
}
if-else处理异常的缺点:
-
但是如果随着业务复杂,代码会变得臃肿且混乱,业务代码和异常处理混在一起;
-
可读性差;
-
维护成本大,要想办法解决异常;
2.1 try-catch 处理异常
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
int a =scanner.nextInt();
System.out.println(10/a);
}catch (InputMismatchException ex){
System.out.println("输入数据不是整数");
}
System.out.println("hello Exception!");
}
catch结果:
1.correct
2.exception
3.exception
小结:try后面的代码和catch后面的代码
-
将可能出现异常的代码放入try代码块中;
-
catch(异常对象),catch接收对应的异常后,执行对应代码块里的代码。
-
注意,try中出现异常后,直接catch,如果catch能接收,出现异常的语句后的代码不会被执行,正如上面不会输出整型变量a的值System.out.println(a)。
如果catch不能接收,且异常向上抛出main方法,整个程序直接终止,由JVM处理,也即异常的语句后的代码不会被执行System.out.println(a)。
但是如果try没有出现异常,则try代码块会被执行,那么System.out.println(a)被执行。
-
注意try-catch后面的代码,受catch影响。就是说,如果没有异常,那肯定会被执行;
如果出现异常被捕获到了,也会被执行;
但是如果没有被catch接收,且异常向上抛出main方法,则会被JVM处理,则会在出现异常的语句处直接终止,并抛出异常。
catch的处理方法
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
int a =scanner.nextInt();
System.out.println(10/a);
}catch (InputMismatchException ex){
System.out.println("输入数据不是整数");// 直接打印输出内容
}
System.out.println("hello Exception!");
}
1.打印异常信息,如果异常了,也不会影响try-catch后面的代码
catch (InputMismatchException ex){
ex.printStackTrace();// 打印异常信息
System.out.println("输入数据不是整数");
}
2.多个catch异常,三种方式:
1.
catch (InputMismatchException |ArithmeticException ex ){
ex.printStackTrace();
System.out.println("输入数据有误");
}
2.
catch (InputMismatchException ex ){
ex.printStackTrace();
System.out.println("输入数据不是整数");
}catch (ArithmeticException ex){
ex.printStackTrace();
System.out.println("输入数据为0");
}
3.
catch (Exception ex ){// 其他异常类型的父类
ex.printStackTrace();
System.out.println("输入数据有误");
}
2.3 finally
紧跟着try{}或者catch{}的。
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
int a =scanner.nextInt();
System.out.println(10/a);
}catch (InputMismatchException ex){
System.out.println("输入数据不是整数");
}finally {
System.out.println("hello Exception!");
}
}
小结:
只要是finally中的代码,它一定会被执行。一般用于执行关闭连接、关闭文件和释放线程等操作。
注意:
- 如果try中有return,finally中无,这个执行顺序是,先finally在return;
- 如果finally也有,则只执行finally中的return。
2.4 try-catch异常处理流程(※※):
- 程序先执行 try 中的代码
- 如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.
- 如果找到匹配的异常类型, 就会执行 catch 中的代码
- 如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.
- 无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).
- 如果上层调用者也没有处理的了异常, 就继续向上传递.
- 一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止
3. 抛出异常(throw)
为什么要抛出异常?
抛出异常也是处理问题的一种方式,例如条件问题,如下面除法运算:
public static void main(String[] args) {
System.out.println(divide(10, 0));
}
public static int divide(int x, int y) {
if (y == 0) {
throw new ArithmeticException("抛出除 0 异常");
}
return x / y;
}
这里的throw异常的操作流程和上面异常处理流程的后两步是一样的,抛出了异常,调用者不处理,如果向上抛出了main方法,则 JVM 处理,即异常终止。
不处理异常:
加上异常处理
注意:只要触发异常了,那么异常后面的代码是无法被执行的,这和try中的处理是一样的。
4. 异常声明(throws)
处理异常的时候, 通常希望知道这段代码中究竟会出现哪些可能的异常,告诉方法调用者这些异常,以便处理异常。
在异常体系中,又分为受查异常和非受查异常,throw这两种异常时:
- 受查异常若不try-catch则必须异常声明,否则连编译器都不能通过!。
- 非受查异常,你声不声明都行,抛出异常自己处理也行,交给JVM也行。
public static int divide(int x, int y) throws ArithmeticException {
if (y == 0) {
throw new ArithmeticException("抛出除 0 异常");
}
return x / y;
}
如果声明多个异常类型,用**”,“逗号**隔开
... func() throws NullPointerException,ArithmeticException {
}
5. throw和throws的区别
位置 | 内容 | 作用 | |
---|---|---|---|
throw | 方法内部 | 异常对象 | 抛出异常 |
throws | 方法声明处 | 异常类型 | 声明异常 |
6. 异常体系
![体系](https://i-blog.csdnimg.cn/blog_migrate/3d073804420369815afc1ca3404ff1af.png)
受查异常和非受查异常(※※)
Error类和RuntimeException类及其他们的子类都为非受查异常。(蓝色区域)
其他的异常则为受查异常。(红色区域)
注意:如果是受查异常,则必须显示进行处理。编译器会检查它,也就是说,当程序中可能出现这类异常,要么用try-catch语句捕获它,要么用throws子句声明抛出它由JVM处理,否则编译不会通过。比如实现类的clone方法时。
而非受查异常是非必须处理的。
例子:
1.非受查异常(RuntimeException)
// 运行时异常
public static int divide(int x, int y) {
if (y == 0) {
throw new ArithmeticException("抛出除 0 异常");
}
return x / y;
}
2.受查异常(Exception)
// 受查异常1
public static int divide(int x, int y) {
if (y == 0) {
try {
throw new Exception("抛出除 0 异常");
} catch (Exception e) {
e.printStackTrace();
}
}
return x / y;
}
// 受查异常2
public static int divide(int x, int y) throws Exception {
if (y == 0) {
throw new Exception("抛出除 0 异常");
}
return x / y;
}
不处理受查异常,编译器警告:
7. 自定义异常
自定义异常即自己定义一个异常类,定义时要选择继承受查异常还是非受查异常,然后选择构造方法。
处理自定义异常的方法和处理受查异常和非受查异常的方式是一样的!
例子:
1.继承非受查
class MyException extends RuntimeException {
public MyException(String message) {
super(message);
}
}
2.继承受查
class MyException extends Exception {
public MyException(String message) {
super(message);
}
}
构造方法:(Fn+Alt+Enter)
![体系](https://i-blog.csdnimg.cn/blog_migrate/a3d9953ba0cf5c8c418ae00d80b1b858.png)
测试非受查异常:
public static int divide(int x, int y) {
if (y == 0) {
throw new MyException("抛出除 0 异常");
}
return x / y;
}
public static void main(String[] args) {
System.out.println(divide(10, 0));
}
结果: