异常

104 阅读3分钟
├── java.lang.Throwable               
│   ├── java.lang.Error           # 一般不编写针对性的代码进行处理
│   └── java.lang.Exception       # 可以进行异常的处理                     
│       ├── 编译时异常(checked)
│       │      ├── IOException
│       │      |   ├── FileNotFoundException
│       │      ├── ClassNotFoundException
│       │      ├── ...
│       ├── 运行时异常(unchecked)
│       │      ├── NullPointerException
│       │      ├── ArrayIndexOutOfBoundsException
│       │      ├── StringIndexOutOfBoundsException
│       │      ├── ClassCastException
│       │      ├── NumberFormatException
│       │      ├── InputMismatchException
│       │      ├── ArithmeticException
│       │      ├── ...

Error

Error不在处理范围之内(不能处理),错了就改

    public static void main(String[] args) {
        // 栈溢出:Exception in thread "main" java.lang.StackOverflowError
        // main(args);

        // 堆溢出:Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        Integer[] arr = new Integer[1024 * 1024 * 1024];
        int num = 1024*1024*1024*2 - 1;
        System.out.println(num);
    }

Exception

Exception 分为编译时异常和运行时异常。

运行时异常(改代码)

运行时异常在开发中一般不考虑 try-catch 处理。因为使用 try-catch处理,在编译时不会报错,但是运行时还是会报错,相当于将报错延后到运行时出现。

常见的运行时异常如下:

// 
public class ExceptionDemoTest {
    public static void main(String[] args) {
        // 输入不匹配:Exception in thread "main" java.util.InputMismatchException
        System.out.print("请输入分数:");
        Scanner scanner = new Scanner(System.in);
        int score = scanner.nextInt();
        System.out.println(score);
    }
    @Test
    public void test1() {
        int[] numbers = null;
        // 空指针:Exception in thread "main" java.lang.NullPointerException
        System.out.println(numbers[2]);
    }

    @Test
    public void test4() {
        String str = null;
        // 空指针:java.lang.NullPointerException
        System.out.println(str.charAt(0));
    }

    @Test
    public void test2() {
        // 数组角标越界:java.lang.ArrayIndexOutOfBoundsException: 3
        int[] numbers1 = new int[3];
        System.out.println(numbers1[3]);
    }

    @Test
    public void test3() {
        String str0 = "abc";
        // 字符串角标越界:java.lang.StringIndexOutOfBoundsException: String index out of range: 3
        System.out.println(str0.charAt(0) + " - " + str0.charAt(1) + " - " + str0.charAt(2)+ " - " + str0.charAt(3));
    }

    @Test
    public void test5() {
        // java.lang.ClassCastException: java.util.Date cannot be cast to java.lang.String
        Object object = new Date();
        String str = (String)object;
    }

    @Test
    public void test6() {
        String str = "123a";
        // java.lang.NumberFormatException: For input string: "123a"
        int i = Integer.parseInt(str);
        System.out.println(i);
    }

    @Test
    public void test7() {
        System.out.print("请输入分数:");
        // InputMismatchException
        Scanner scanner = new Scanner(System.in);
        int score = scanner.nextInt();
        System.out.println(score);
    }

    @Test
    public void test9() {
        // java.lang.ArithmeticException: / by zero
        System.out.println(10 / 0);
    }
}
编译时异常

处理异常的方式try-catch 或者 try-catch-finally / throws 异常类型

  1. try-catch-finally
  • 语法
    try {
        // 可能出现异常的代码
    } catch (异常类型1 变量名1) {
        // 处理异常方式1
        
        // String msg = 变量名1.getMessage();
        // System.out.println(msg);
        
        // e.printStackTrace();
    } catch (异常类型2 变量名2) {
        // 处理异常方式2
    }
    // ...
    finally { // 可选
        // 一定会执行的代码
    }
  • 说明
    1. finally 是可选的,finally 中的代码一定会被执行
    2. 使用 try 将可能出现异常的代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去 catch 中进行匹配
    3. 一旦匹配到某一个 catch,会进到 catch 中进行异常的处理。一旦处理完成,就跳出当前的 try-catch 结构(无 finally )继续执行后面的代码
  1. throws 异常类型
  • 语法
public void 方法名() throws 异常类型1, 异常类型2 {
    // ...
}
  • 说明
    1. throws 异常类型1, 异常类型2... 指明此方法执行时,可能抛出的异常类型。
    2. 一旦方法体的某一行出现异常,仍会在这一行生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。异常代码后面的代码,将不再执行!

开发中如何选择异常的处理方式

方法重写的关于抛出异常的规则:子类重写的方法抛出的异常不大于父类被重写的方法抛出的异常

  • 如果父类被重写的方法没有抛出异常,则子类重写父类的方法也不能抛出异常,子类中有异常必须要try-catch
  • 如果多个方法(a, b, c)都有异常,并且在另一个方法(d)中调用这些方法(a的返回结果被b用到了,b的返回结果被c用到了),可以(a, b, c)都throws, 然后在d方法中整体对a,b,c进行try-catch

手动生成异常对象,并抛出(throw)

class Demo {
    private Double radius;
    
    public int compareTo(Object obj) throws Exception { // 0相等,1比传进来的大,-1比传进来的小
        if (this == obj) return 0; 
        if (!(obj instanceof Demo)) throw new Exception("传入的数据类型不正确!");
        
        Demo demo = (Demo)obj;
        return this.getRadius().compareTo(demo.getRadius());
    }
}

用户自定义异常类

class MyException extends Exception {
    static final long serialVersionUID = -703489564654659L;
    
    public MyException() {}
    
    public MyException(String msg) {
        super(msg);
    }
}