异常处理

🎈概述

  • Error(错误): Java虚拟机无法解决的严重问 题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError(栈溢出)和OOM(OutOfMemory:堆溢出)。一般不编写针对性的代码进行处理。

    一般不针对性的写解决Error的代码

  • Exception(异常): 其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。

    1. 空指针访问
    2. 试图读取不存在的文件
    3. 网络连接中断
    4. 数组角标越界
  • 对于这些错误,一般有两种解决方法

    1. 一是遇到错误就终止程序的运行。
    2. 程序员在编写程序时,就考虑到错误的检测、错误消息的提示,以及错误的处理。
  • 捕获错误最理想的是在编译期间,但有的错误只有在运行时才会发生。

    比如:除数为0,数组下标越界等

🎈分类:

  • 编译时(checked)异常(RuntimeException)

    1. 是指编译器要求必须处置的异常。即程序在运行时由于外界因素造成的一般性异常。编译器要求Java程序必须捕获或声明所有编译时异常。
    2. 对于这类异常,如果程序不处理,可能会带来意想不到的结果。
  • 运行时(unchecked)异常(RuntimeException)

    1. 是指编译器不要求强制处置的异常。一般是指编程时的逻辑错误,是程序员应该积极避免其出现的异常。java.lang.RuntimeException类及它的子类都是运行时异常。

    2. 对于这类异常,可以不作处理,因为这类异常很普遍,若全处理可能会对

      程序的可读性和运行效率产生影响。

🎈常见异常举例

  • RuntimeException(可以编译)

    1. NullPointerException

      1
      2
      String a = null;
      System.out.println(a.charAt(0));
    2. ArrayOutOfBoundsException

      1
      2
      int[] arr = new int[5];
      System.out.println(arr[6]);
    3. ClassCastException(类强制转换异常)

      1
      2
      Object obj = new Object();
      String str = (String) obj;
    4. NumberFormatException

      1
      2
      3
      4
      5
      String str="123";
      str="abc";
      int num=Integer.parseInt(str);
      //Integer.parseInt(String);
      //String 必须为纯数字
    5. InputMismatchException

      1
      2
      Scanner in = new Scanner(System.in);
      int score = in.nextInt(); // key in dfsd
    6. ArithmeticException(算术异常)

      1
      2
      int a = 14, b = 0;
      System.out.println(a / b);
  • CompileTimeException(编译时异常)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package src.SUMMER_java;
    public class Day0529 {
    public static void main(String[] args) {

    }

    public void test() throws IOException {
    File file = new File("hello.txt");
    FileInputStream fis = new FileInputStream("hello.txt");

    int data = fis.read();
    while (data != -1) {
    System.out.print((char) data);
    }
    fis.close();
    }
    }
    //报错:IOException cannot be resolved to a type
    //FileInputStream cannot be resolved to a type

🎈异常处理方式

在编写程序时,经常要在可能出现错误的地方加上检测的代码,如进行x/y运算时,要检测分母为0,数据为空,输入的不是数据而是字符等。过多的if-else分支会导致程序的代码加长、臃肿,可读性差。因此采用异常处理机制。

⭐try-catch-finally

  1. Java提供的是异常处理的抓抛模型

  2. 一旦抛出异常后,其后的代码不在执行

  3. Java程序的执行过程中如出现异常,会生成一个异常类对象,

    该异常对象将被提交给Java运行时系统,这个过程称为抛出

    (throw)异常。

  4. 异常对象的生成

    1. 由虚拟机自动生成:程序运行过程中,虚拟机检测到程序发生了问题,如果在当前代码中没有找到相应的处理程序,就会在后台自动创建一个对应异常类的实例对象并抛出——自动抛出。
    2. 由开发人员手动创建:Exception exception = new ClassCastException();——创建好的异常对象不抛出对程序没有任何影响,和创建一个普通对象一样。
  5. 如果一个方法内抛出异常,该异常对象会被抛给调用者方法中处

    理。如果异常没有在调用者方法中处理,它继续被抛给这个调用

    方法的上层方法。这个过程将一直继续下去,直到异常被处理。

    这一过程称为捕获(catch)异常。

  6. 如果一个异常回到main()方法,并且main()也不处理,则程序运

    行终止。

👉使用说明
  1. 使用try将可能出现异常的代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据对象的类型,去catch中进行匹配。

  2. catch 中可以很多个,一旦try中的异常对象被catch匹配,就会进入catch中进行异常的处理,一旦处理完成(执行完catch中的代码),就会跳出try-catch结构(没有finally的话),继续执行下面的代码。

  3. catch中的异常类型如果没有子父类关系,那么声明位置无所谓。

    如果满足子父类关系,则要求子类要声明在父类的前面,否则会报错

    //Thrown by a Scanner to indicate that the token retrieved does not match the pattern for the expected type, or that the token is out of range for the expected type.

  4. 常用的异常对象的处理方式:

    • String getMessage() //String getMessage();
    • printStacTrace() //跟报错一样显示错误信息
  5. 在try中声明的变量,出了try之后不能再调用。(想要使用,定义在try外面即可)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package src.SUMMER_java;

import java.util.InputMismatchException;
import java.util.Scanner;

public class Day0529 {
public static void main(String[] args) {
int num;
Scanner in = new Scanner(System.in);
try {
num = in.nextInt();
} catch (NullPointerException e) {
System.out.println("空指针异常! ");
} catch (InputMismatchException e) {
// InputMismatchException是RuntimeException 的子类的子类
System.out.println("输入的类型错误!,InputMismatchException");
e.printStackTrace();
System.out.println();
System.out.println(e.getMessage());
} catch (RuntimeException e) {
System.out.println("输入的类型错误!, RuntimeException");
} finally {
System.out.println("finall code must be carried");
}
System.out.println("程序结束");
}

}

👉finally 的使用
  1. finally 是可选的
  2. finally中的代码必定会执行。即便catch中出现了新的异常或者是catch中==有return语句==,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package src.SUMMER_java;

import java.util.InputMismatchException;
import java.util.Scanner;

import javax.xml.transform.stream.StreamSource;

public class Day0529 {
public static void main(String[] args) {
method1();
}

public static int method1() {
int num;
Scanner in = new Scanner(System.in);
try {
num = in.nextInt();
return 1;
} catch (NullPointerException e) {
System.out.println("空指针异常! ");
} catch (InputMismatchException e) {
// InputMismatchException是RuntimeException 的子类的子类
System.out.println("输入的类型错误!,InputMismatchException");
int[] arr = new int[5];
System.out.println(arr[8]);//选择性注释此句
return 2;
}
finally {
System.out.println("finall code must be carried");
}
System.out.println("程序结束");
return 1;
}
}

🎈try-catch-finally捕获异常总结

  • 主要处理编译时异常(例如打开文件,非语法错误),使得程序在编译时不报错,但是运行时仍然可能报错。相当于使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现
  • 开发中由于运行时异常较为常见,因此通常不针对运行时异常编写try-catch-finally,针对编译时的异常,一定要考虑异常的处理。

⭐throws + 异常类型

  1. 🎈"throws "+异常类型 要写在方法的执行处,指明此方法执行时,可能会抛出的异常类型,一旦当方法执行时,出现异常,仍然会在代码执行处生成一个异常类,如果此对象满足throws抛出的异常类型,就会向上一级抛出,异常后面的代码就不再执行。
  2. 🎈体会:
    • try-catch-finally真正的处理了异常
    • throws只是将异常抛给了方法的调用者,并没有真正处理异常
👉重写方法声明抛出异常的原则
  1. 子类重写的方法不能抛出比被重写方法范围更大的异常类型。

    FileNotFoundException < IOException,换句话说,子类抛出的异常应当能被父类方法抛出的异常兼容。

    如果父类方法没有throws方式抛出异常,子类方法也不能使用throws

    Exception IOException is not compatible with throws clause in Dad.method_1()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //Right code
    class Dad {
    // public void method_1() throwsFileNotFoundException { //wrong answer
    public void method_1() throws FileNotFoundException, IOException {

    }
    }

    class Son extends Dad {
    public void method_1() throws IOException {

    }
    }
  2. 执行的方法中,先后又调用了另外几个方法,这几个方法是递进关系执行的,我们建议这几个方法使用throws的方式进行处理,而执行的方法A,可以考虑yrt-catch-finally方式进行处理。

  3. 不建议两者同时使用。

⭐手动抛出异常

  • 前面两种讲的都是系统自动生成的异常类,Java异常类对象除在程序执行过程中出现异常时由系统自动生成并抛出,也可根据需要使用人工创建并抛出。

  • 步骤:

    1. 首先要生成异常类对象,然后通过throw语句实现抛出操作(提交给Java运行环境)。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      public static void main(String[] args) throws Exception {
      test02();
      }

      public static void test02() throws Exception {
      int id;
      Scanner in = new Scanner(System.in);
      id = in.nextInt();
      if (id < 0) {
      throw new Exception("您输入的数据非法!");
      }
      }
    2. 可以抛出的异常必须是Throwable或其子类的实例。下面的语句在编译时将会产生语法错误:

      1
      throw new String("want to throw");

⭐用户自定义异常类

  • 一般地,用户自定义异常类都是RuntimeException的子类。

  • 自定义异常类通常需要编写几个重载的构造器。

  • 自定义异常需要提供serialVersionUID

  • 自定义的异常通过throw抛出。

  • 自定义异常最重要的是异常类的名字,当异常出现时,可以根据

    名字判断异常类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

package src.SUMMER_java;

import java.io.*;
import java.util.Scanner;

public class Day0529 {
public static void main(String[] args) throws Exception {
test02();
}
public static void test02() throws Exception {
int id;
Scanner in = new Scanner(System.in);
id = in.nextInt();
if (id < 0) {
throw new MyException("您输入的数据非法!");
}
}

}

class MyException extends RuntimeException {
static final long serialVersionUID = -7034897745766939L; // 对类的标识 (涉及序列化)序列号
public MyException() {}
public MyException(String message) {
super(message);
}
}
//-4
//Exception in thread "main" src.SUMMER_java.MyException: 您输入的数据非法!

image-20220531093055632