跳转至

异常处理

一、异常的分类

  1. Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。必须StackOverflowErrorOOM。一般不编写针对性的代码进行处理

    1
    2
    3
    4
    5
    6
    7
    8
    public class ErrorTest {
        public static void main(String[] args) {
            // 栈溢出 java.lang.StackOverflowError
    //        main(args);
            // 堆溢出 ava.lang.OutOfMemoryError
    //        Integer[] arr = new Integer[1024 * 1024 * 1024];
        }
    }
    
  2. Exception:其他因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如

    1. 空指针访问
    2. 试图读取不存在的文件
    3. 网络连接中断
    4. 数组角标越界

二、常见异常

1. 编译时异常

2. 运行时异常

  1. NullPointerException
1
2
3
4
5
6
public class ExceptionTest {
    public static void main(String[] args) {
        int[] arr = null;
        System.out.println(arr[1]); // java.lang.NullPointerException
    }
}

2.ArrayIndexOutOfBoundsException

1
2
3
4
5
6
public class ExceptionTest {
    public static void main(String[] args) {
        int[] arr = new int[5];
        System.out.println(arr[5]);  // java.lang.ArrayIndexOutOfBoundsException: 5
    }
}

3.NumberFormatException

1
2
3
4
5
6
public class ExceptionTest {
    public static void main(String[] args) {
        String str = "abc";
        Integer.parseInt(str); // java.lang.NumberFormatException
    }
}

4.ArithmeticException

1
2
3
4
5
public class ExceptionTest {
    public static void main(String[] args) {
        System.out.println(5 / 0); // java.lang.ArithmeticException
    }
}

三、异常处理

1. try-catch

语法格式:

1
2
3
4
5
6
7
8
9
try {
    // 可能出现异常的代码
}catch (异常类型1 变量名1){
    // 处理异常的方式1
}catch (异常类型2 变量名2){
    // 处理异常的方式2
}finally {
    // 一定会执行的代码
}

示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class ExceptionTest {
    public static void main(String[] args) {
        try {
            Integer.parseInt("abc");
        }catch (NumberFormatException e){
            System.out.println("数值转换异常");
            // 两种异常处理的方式
            System.out.println(e.getMessage());  // For input string: "abc"
            e.printStackTrace(); // 常用
        }
    }
}

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

一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理,一旦处理完成,就跳出当前的try-catch结构。

catch中的异常类型如果没有子父类关系,在声明的先后顺序上无所谓,如果有子父类关系,要求子类声明的在父类的上面,否则报错。

try中声明的变量,出了try结构以后就不能再被调用。

2. try-catch-finally

finally中声明的是一定会执行的代码,即使catch中有异常、try中有return或catch中有return语句等情况。

像数据库连接、输入输出流、网络socket等资源,JVM不能自动回收,此时需要手动的进行资源的释放。此时资源的释放就需要声明在finally中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class ExceptionTest {
    public static void main(String[] args) {
        try {
            System.out.println(5 / 0);
        } catch (ArithmeticException e){
            e.printStackTrace();
            Integer.parseInt("abc");
        } finally {
            System.out.println("finally中的代码块");  // 执行了
        }
        System.out.println("try之外代码块"); // 没有执行
    }
}

try-catch-finally可以根据需要嵌套。

3. trows + 异常类型

写在方法的声明处,指明此方法执行时可能会抛出的异常类型。

一旦此方法执行时,出现异常时仍会在异常代码处生成一个异常类的对象,此时满足throws后异常类型时,就会被抛出,异常代码后续的代码就不再执行。

 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
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;


public class ExceptionTest {
    public static void main(String[] args) {
        try {
            method2();
        } catch (IOException e){
            e.printStackTrace();
        }

    }
    public static void method2() throws FileNotFoundException, IOException{
        method1();
    }
    public static void method1() throws FileNotFoundException, IOException {
        File file = new File("hello.txt");
        FileInputStream fis = new FileInputStream(file);

        int data = fis.read();
        while (data != -1){
            System.out.println((char) data);
            data = fis.read();
        }
        fis.close();
    }
}

四、重写方法异常抛出

子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型。

 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
import java.io.FileNotFoundException;
import java.io.IOException;

public class OverrideTest {
    public static void main(String[] args) {
        OverrideTest test = new OverrideTest();
        test.display(new SubClass());
    }

    public void display(SuperClass s){
        try {
            s.method();
        } catch (IOException e){
            e.printStackTrace();
        }
    }
}

class SuperClass{
    public void method() throws IOException{

    }
}

class SubClass extends SuperClass {
    public void method() throws FileNotFoundException {

    }
}

五、手动抛出异常

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class StudentTest {
    public static void main(String[] args) {
        try {
            Student stu = new Student();
            stu.register(-1);
            System.out.println(stu);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}

class Student {
    private int id;

    public void register(int id) throws Exception {
        if (id > 0) {
            this.id = id;
        } else {
            throw new Exception("非法数据");
        }
    }
}

六、自定义异常类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
//1.继承现有的异常类RuntimeException、Exception
//2.提供全局常量 serialVersionUID
//3.提供重载的构造器
public class MyException extends RuntimeException {
    static final long serialVersionUID = -7034897190745766939L;

    public MyException(){

    }
    public MyException(String msg){
        super(msg);
    }
}

七、开发中如何选择使用try-catch还是throws

  1. 如果父类中被重写的方法没有throws方法处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中有异常,必须使用try-catch方式处理。
  2. 执行的方法A中,先后又调用了另外几个方法,这几个方法是递进关系执行的,建议这几个方法使用throws的方式进行处理,而执行的A方法考虑使用try-catch方式进行处理。