Java 异常


异常简介

异常概念

异常,就是程序出现了不正常的情况。

如果程序出现了问题,我们没有做任何处理,那么最终 JVM 会做默认的处理,其处理方式有如下两个步骤:

  1. 把异常的名称、错误原因及异常出现的位置等信息输出在了控制台。
  2. 程序停止执行。

控制台在打印异常信息时,会打印异常类名、异常出现的原因、异常出现的位置等信息。我们在 debug 时,可以根据提示,找到异常出现的位置,分析原因,修改异常代码。

image

异常体系

image

除了 RuntimeException 和其子类,以及 error 和其子类,其他所有异常都是 checkedException

  • Error:是程序无法处理的错误,表示运行应用程序中较严重问题。
    • 大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。
    • 这些错误是不可检查的,因为它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况,比如 OutOfMemoryError 和 StackOverflowError 等。
  • Exception:是需要通过代码处理(声明或捕获)的。

如何区分 Error 与 Exception:

  • 如果程序出现了不正常的信息的类名是以 Error 结尾的(除了一个 ThreadDeath),那么肯定是一个错误。
  • 如果是以 Exception 结尾的,那么肯定是一个异常。

编译时异常和运行时异常

  • 编译时异常(CheckedException)

    • 都是 Exception 类及其子类。
    • 如果一个方法内部抛出了一个编译时异常对象,那么方法上一定要声明,调用者也必须处理。
    • 除了运行时异常,都是编译时异常。
  • 运行时异常(RuntimeException)

    • 都是 RuntimeException 类及其子类。
    • 如果一个方法内部抛出了一个运行时异常对象,那么方法上可以声明也可以不声明,调用者可以处理也可以不处理。
  • 问:为什么 JAVA 编译器会如此严格要求编译时异常,对运行时异常如此宽松?
    • 因为运行时异常都是可以通过良好的编程习惯去避免(如事先进行条件判断语句)。
    • 而编译时异常很多时候是没法通过编程去避免的(如磁盘不够导致 I/O 出错等)。

image


常见异常

RuntimeException

序号 异常名称 异常描述
1 ArrayindexOutOfBoundsException 数组越界异常
2 NullPointerException 空指针异常
3 IllegalArqumentException 非法参数异常
4 NegativeArraySizeException 数组长度为负异常
5 llleaalStateException 非法状态异常
6 ClassCastException 类型转换异常

UncheckedException

序号 异常名称 异常描述
1 NoSuchFieldException 表示该类没有指定名称抛出来的异常
2 NoSuchMethodException 表示该类没有指定方法抛出来的异常
3 IllegalAccessException 不允许访问某个类的异常
4 ClassNotFoundException 类没有找到执出异常

throws 方式处理异常

格式:

public void 方法() throws 异常类名 {
    
}
  • 编译时异常必须要显式进行处理,有两种处理方案:try … catch … 或者 throws。如果采用 throws 这种方案,那么要在方法上进行显示声明,将来谁调用这个方法谁就要处理。

  • 运行时异常因为在运行时才会发生,所以在方法后面可以不写,出现运行时异常时则默认交给 JVM 处理。

示例:

public class ExceptionDemo {
    public static void main(String[] args) throws ParseException{
        System.out.println("开始");
//        method();
          method2();
        System.out.println("结束");
    }

    // 编译时异常(日期格式解析错误)
    public static void method2() throws ParseException {
        String s = "2048-08-09";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date d = sdf.parse(s);
        System.out.println(d);
    }

    // 运行时异常(索引越界)
    public static void method() throws ArrayIndexOutOfBoundsException {
        int[] arr = {1, 2, 3};
        System.out.println(arr[3]);
    }
}

try … catch 方式处理异常

格式:

try {
	可能出现异常的代码;
} catch(异常类名 变量名) {
	异常的处理代码;
}

执行流程:

  1. 程序从 try 里面的代码开始执行。
  2. 出现异常,就会跳转到对应的 catch 里面去执行。
  3. 执行完毕之后,程序还可以继续往下执行。

示例代码:

public class ExceptionDemo {
    public static void main(String[] args) {
        System.out.println("开始");
        method();
        System.out.println("结束");
    }

    public static void method() {
        try {
            int[] arr = {1, 2, 3};
            System.out.println(arr[3]);
            System.out.println("这里能够访问到吗");
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("你访问的数组索引不存在,请回去修改为正确的索引");
        }
    }
}

注意:

  1. 如果 try 中没有遇到问题,怎么执行?

    • 会把 try 中所有的代码全部执行完毕,不会执行 catch 里面的代码。
  2. 如果 try 中遇到了问题,那么 try 下面的代码还会执行吗?

    • 那么会直接跳转到对应的 catch 语句中,try 中接下来的代码就不会再执行了。
    • 当 catch 里面的语句全部执行完毕,表示整个体系全部执行完全,继续执行下面的代码。
  3. 如果出现的问题没有被捕获,那么程序如何运行?

    • 那么 try...catch 就相当于没有写,也就是自己没有处理,默认交给虚拟机处理。
  4. 同时有可能出现多个异常怎么处理?

    • 出现多个异常,那么就写多个 catch 就可以了。
    • 注意点:如果多个异常之间存在子父类关系,那么父类一定要写在下面(若从子到父,后面的异常即是废话)。

finally

finally 保证了 try...catch 代码块中即使有异常,也能执行 finally 代码块。

public static void main(String[] args) {
    try{
        int i = 1/0;
    } catch (Exception e) {
        e.printStackTrace();
        throw e;  // 即使在catch又有异常,finally代码块也能执行
    } finally {
        System.out.println("无finally会执行吗");
    }
}

总结:

  1. 与 finally 相对应的 try 语句得到执行的情况下,finally 才有可能执行。
  2. finally 执行前,若程序或线程终止,则 finally 不会执行。

throw 抛出异常

格式throw new 异常();

注意

  1. 如果一个方法的内部抛出了一个编译时异常对象,那么必须要在此方法上声明抛出。
  2. 如果调用了一个声明抛出异常的方法,那么调用者必须要处理。
  3. 如果一个方法内部抛出了一个异常对象,那么 throw 语句后面的代码都不会执行了(一个方法遇到 throw 语句,这个方法也会马上停止执行)。
  4. 在一种情况(条件)下,只能抛出一个异常对象。

throws 和 throw 的区别:

throws throw
用在方法声明上 用在方法内部
后跟异常类型 后跟异常对象
后可声明多个异常类型的异常 后只能有一个异常对象

示例代码:

public class ExceptionDemo8 {
    public static void main(String[] args) {
        //int [] arr = {1,2,3,4,5};
        int [] arr = null;
        printArr(arr);  // 此行会接收到一个异常,因此在此还需要自己处理一下异常
    }

    private static void printArr(int[] arr) {
        if(arr == null){
            // 调用者知道成功打印了吗?
            System.out.println("参数不能为null");
			// 当参数为 null 的时候,手动创建了一个异常对象,抛给调用者
            throw new NullPointerException();  
        }else{
            for (int i = 0; i < arr.length; i++) {
                System.out.println(arr[i]);
            }
        }
    }

}

Throwable 成员方法

常用方法:

方法名 说明
public String getMessage() 返回此 throwable 的详细消息字符串
public String toString() 返回此可抛出的简短描述
public void printStackTrace() 把异常的错误信息输出在控制台

示例代码:

public class ExceptionDemo {
	
    public static void main(String[] args) {
        System.out.println("开始");
        method();
        System.out.println("结束");
    }

    public static void method() {
        try {
            int[] arr = {1, 2, 3};
            System.out.println(arr[3]);  // new ArrayIndexOutOfBoundsException();
            System.out.println("这里能够访问到吗");
        } catch (ArrayIndexOutOfBoundsException e) {
            e.printStackTrace();

            // public String getMessage():返回此 throwable 的详细消息字符串
            System.out.println(e.getMessage());  //Index 3 out of bounds for length 3

            // public String toString():返回此可抛出的简短描述
            System.out.println(e.toString());  // java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3

            // public void printStackTrace():把异常的错误信息输出在控制台
            e.printStackTrace();  // 打印信息如下:
//            java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
//            at com.itheima_02.ExceptionDemo02.method(ExceptionDemo02.java:18)
//            at com.itheima_02.ExceptionDemo02.main(ExceptionDemo02.java:11)
        }
    }
}

自定义异常

当 Java 中提供的异常不能满足我们的需求时,我们就可以自定义异常。

实现步骤:

  1. 定义异常类
  2. 写继承关系
  3. 提供空参构造
  4. 提供带参构造

代码实现:

  • 异常类:
public class AgeOutOfBoundsException extends RuntimeException {
    public AgeOutOfBoundsException() {
    }

    public AgeOutOfBoundsException(String message) {
        super(message);
    }
}
  • 学生类:
public class Student {
	
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if(age >= 18 && age <= 25){
            this.age = age;
        }else{
            //如果Java中提供的异常不能满足我们的需求,我们可以使用自定义的异常
            throw new AgeOutOfBoundsException("年龄超出了范围");
        }
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  • 测试类:
public class ExceptionDemo12 {
    public static void main(String[] args) {
        // 键盘录入学生的姓名和年龄,其中年龄为 18 - 25岁
        // 超出这个范围是异常数据不能赋值,需要重新录入,一直录到正确为止。

        Student s = new Student();

        Scanner sc = new Scanner(System.in);
        System.out.println("请输入姓名:");
        String name = sc.nextLine();
        s.setName(name);
       while(true){
           System.out.println("请输入年龄:");
           String ageStr = sc.nextLine();
           try {
               int age = Integer.parseInt(ageStr);
               s.setAge(age);
               break;
           } catch (NumberFormatException e) {
               System.out.println("输入有误,请输入一个整数");
               continue;
           } catch (AgeOutOfBoundsException e) {
               System.out.println(e.toString());
               System.out.println("输入有误,请输入一个符合范围的年龄");
               continue;
           }
           /*if(age >= 18 && age <=25){
               s.setAge(age);
               break;
           }else{
               System.out.println("输入有误,请输入符合要求的年龄");
               continue;
           }*/
       }
        System.out.println(s);
    }
}
posted @ 2021-09-21 22:42  Juno3550  阅读(68)  评论(0编辑  收藏  举报