异常的分类

在代码运行时,可能会出现异常种类有很多,为了对不同异常或者错误进行很好的分类管理,Java内部维护了一个异常的体系结构:

JavaSE-46-1

注意事项:

  • Error指的是Java虚拟机无法解决的严重问题,比如:JVM的内部错误、资源耗尽、内部异常等。靠程序本身是无法解决的,所以我们不关注

  • Exception与Error不同,异常产生后程序员可以通过代码进行处理,使程序继续执行

  • 异常可能在编译时发生,也可能在程序运行时发生,根据发生的时机不同,可以将异常分为受检查异常和运行时异常

    • 受检查异常,也称为编译时异常。从名字我们就能够理解,就是程序在编译的时候发生的异常(程序已经编译通过得到字节码文件了,再由JVM执行过程中出现的错误)
    • 运行时异常,指的是在程序执行期间发生的异常
  • 程序中语法错误、逻辑错误都不属于上面的Error和Exception

运行时异常

public class Test {
    public static void main(String[] args) {
        int[] arr = {1,2,3};
        System.out.println(arr.length);
        int[] arr2 = null;
        System.out.println(arr2.length);
        System.out.println(arr[10]);
    }
}

程序执行结果为:

3
Exception in thread "main" java.lang.NullPointerException
	at com.test.Test.main(Test.java:8)

受检查异常

  1. 嵌套try-catch

    public class Test {
        public static void main(String[] args) {
            try {
                try {
                    Class.forName("com.test.Test").newInstance();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    
  2. 多重catch

    public class Test {
        public static void main(String[] args) {
            try {
                Class.forName("com.test.Test").newInstance();
            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    
  3. throws

    public class Test {
        public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
            Class.forName("com.test.Test").newInstance();
        }
    }
    

自定义异常

自定义的异常可以继承自运行时异常也可以继承自受检查异常。

需要注意的是:

  • 如果继承的是运行时异常,那么在使用的时候无需额外处理
  • 如果继承的是受检查异常,那么使用的时候需要 try-catch 捕获或者 throws 向上抛

继承自运行时异常

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

测试代码如下:

public class Test {
    public void checkValue(int value) {
        if (value < 0) {
            throw new MyException("值不能小于0");
        }
    }

    public static void main(String[] args) {
        Test test = new Test();
        try {
            test.checkValue(-1);
        } catch (MyException e) {
            System.out.println("捕获到异常: " + e.getMessage());
        }
    }
}

程序执行结果如下:

捕获到异常: 值不能小于0

继承自受检查异常

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

测试代码如下:

public class Test {
    // 由于MyException是受检查异常,所以这里需要声明throws MyException
    public void checkValue(int value) throws MyException {
        if (value < 0) {
            throw new MyException("值不能小于0");
        }
    }

    public static void main(String[] args) {
        Test test = new Test();
        try {
            test.checkValue(-1);
        } catch (MyException e) {
            System.out.println("捕获到异常: " + e.getMessage());
        }
    }
}

程序执行结果如下:

捕获到异常: 值不能小于0

重载与重写的异常

在之前学习重载和重写的区别时,有提到过在抛出异常方面的区别:

抛出异常
重载 无关
重写 小于等于

意思是对于重载而言,可以根据需要抛出不同的异常;而对于重写而言,子类重写的函数抛出的异常要小于等于父类中该函数抛出的异常。