【Java】如何理解Java中的异常机制?

1 异常的概念

  1. 程序在执行过程中出现非正常线性,导致JVM非正常停止
  2. 异常不是语法错误

2 异常的分类

  1. Throwable是所有错误或异常的超类
  2. ··········Exception是编译期间异常(写代码时IDE会报错)
  3. ····················RuntimeException时运行期异常,程序运行时出现的问题
  4. ··········Error错误 必须修改代码才能继续执行

3 异常的产生过程

  1. 例如在某个方法体中,代码的不当操作导致了异常,运行程序时,JVM会检测出异常。
  2. JVM会根据异常产生的原因创建一个异常对象,包含异常产生的内容、原因和位置。
  3. 如果程序的方法体中没有在try-catch中定义异常处理逻辑,那么JVM就会把异常对象抛出给调用者该方法的main来处理这个异常。
  4. JVM接收到异常对象以红色字体打印在控制台并终止程序。(中断处理)

4 处理异常的方式

  1. throws虚拟机处理异常:中断程序,抛出错误,打印在控制台
  2. try-catch自己定义异常处理逻辑:程序可以继续执行

4.1 throw关键字

作用:在指定方法中抛出指定异常

使用格式:throw new xxxException(“异常产生原因”)

注意事项

  • 必须写在方法内部
  • new的对象要是Exception 或 Exception的子类
  • 如果抛出指定的异常对象,必须处理这个异常对象。
  • 如果创建的时RuntimeException或其子类,默认交给JVM处理(打印异常对象,中断程序)
  • 如果创建的时编译异常,就必须处理这个异常,必须throws或者try-catch

小贴士:必须首先对方法传递来的参数进行合法性校验,如果参数不合法,必须抛出异常,告知方法调用者,传递的参数有问题。

示例
运行时异常:NullPointerException

public class ThrowException {
    
    
    public static void main(String[] args) {
    
    
//      int[] arr = null;
        int arr [] = {
    
    1,2,3};
        int element = getElement(arr, 10);
        System.out.println(element);

    }
    public static int getElement(int[] arr, int index){
    
    
        if(arr == null){
    
    
            throw new NullPointerException("传递的数组值是null");
            //空指针异常是运行期异常 默认交给JVM处理
        }
        if(index < 0 || index > arr.length){
    
    
            throw new ArrayIndexOutOfBoundsException("数组越界");
        }//运行期异常
        int element = arr[index];
        return element;
    }
}

在这里插入图片描述

4.2 Objects非空判断

Objects类的静态方法

public static <T> T requireNonNull(T obj) {
    
    
	if (obj == null)
		throw new NullPointerException()  ;
	return obj;
}

简化非空判断

public static void main(String[] args) {
    
    
        method(null);
    }
    public static void method(Object obj){
    
    
        Objects.requireNonNull(obj,"传递对象为空");
    }

4.3 throws声明异常

异常处理的第一种方式,交给别人处理

作用:当方法内部抛出异常对象时,必须处理异常对象,可以使用throws关键字处理,会把异常对象抛给方法调用者处理,自己不处理,交给他人处理,最终交给JVM处理。(JVM中断处理

使用方式:在方法声明时使用

注意

  • 必须写在方法声明处
  • 必须是Exception或其子类
  • 抛出多个异常,声明处要声明多个异常,如果是父子异常,声明父异常即可
  • 调用了声明异常的方法必须处理声明的异常。throws抛出最终交给JVM处理或者try-catch定义异常处理逻辑。

编译时异常:FileNotFoundException

    public static void main(String[] args) throws FileNotFoundException {
    
    
        method("c:\\d.txt");
    }
    public static void method(String filename) throws FileNotFoundException {
    
    
        if(!filename.equals("c:\\\\a.txt")){
    
    
            throw new FileNotFoundException("文件路径错误");
        }
    }

在这里插入图片描述


FileNotFoundException extends IOException extends Exception

    public static void main(String[] args) throws IOException {
    
    
        readFile("c:\\a.doc");
    }
    public static void readFile(String filename) throws IOException {
    
    
        if(!filename.endsWith(".txt")){
    
    
            throw new IOException("文件后缀不对");
        }
    }

在这里插入图片描述

4.4 try-catch捕获异常

异常处理的第二种方式,自己处理,程序不中断

public static void main(String[] args) throws IOException {
    
    
        try{
    
    
            readFile("d:\\a.tx");
        }catch (IOException e){
    
    
            System.out.println("文件后缀有误");
        }
        System.out.println("程序不中断,后续代码继续执行");
    }
    public static void readFile(String filename) throws IOException{
    
    
        if(!filename.endsWith(".txt")){
    
    
            throw new IOException("文件后缀有误");
        }
        System.out.println("读取文件");
    }

4.5 Throwable类中三个异常处理方法

  • String getMessage()

打印Message: throw new IOException(Message);

public static void main(String[] args) {
    
    
        try{
    
    
            readFile("d:\\a.tx");
        }catch (IOException e){
    
    
            System.out.println(e.getMessage());
        }
        System.out.println("程序不中断,后续代码继续执行");
    }
    public static void readFile(String filename) throws IOException{
    
    
        if(!filename.endsWith(".txt")){
    
    
            throw new IOException("文件后缀有误");
        }
        System.out.println("读取文件");
    }

在这里插入图片描述


  • String toString()

重写Object类的toString

public static void main(String[] args){
    
    
        try{
    
    
            readFile("d:\\a.tx");
        }catch (IOException e){
    
    
            System.out.println(e.toString());
        }
        System.out.println("程序不中断,后续代码继续执行");
    }
    public static void readFile(String filename) throws IOException{
    
    
        if(!filename.endsWith(".txt")){
    
    
            throw new IOException("文件后缀有误");
        }
        System.out.println("读取文件");
    }

在这里插入图片描述


  • void printStackTrace()
public static void main(String[] args) {
    
    
        try{
    
    
            readFile("d:\\a.tx");
        }catch (IOException e){
    
    
            e.printStackTrace();
        }
        System.out.println("程序不中断,后续代码继续执行");
    }
    public static void readFile(String filename) throws IOException{
    
    
        if(!filename.endsWith(".txt")){
    
    
            throw new IOException("文件后缀有误");
        }
        System.out.println("读取文件");
    }

在这里插入图片描述

4.6 finally代码块

public static void main(String[] args){
    
    
        try {
    
    
            readFile("d:\\a.tx");
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            System.out.println("异常出现后依旧需要执行的代码");
        }
        System.out.println("程序不中断,后续代码继续执行");
    }
    public static void readFile(String filename) throws IOException{
    
    
        if(!filename.endsWith(".txt")){
    
    
            throw new IOException("文件后缀有误");
        }
        System.out.println("读取文件");
    }

4.7 多异常捕获

分别捕获,分别处理

public static void main(String[] args) {
    
    
        int[] arr = {
    
    1,2,3};
        List<Integer> list = new ArrayList<>();
        list.add(1);
        try{
    
    
            System.out.println(arr[3]);
        }catch (ArrayIndexOutOfBoundsException e){
    
    
            e.printStackTrace();
        }

        try{
    
    
            System.out.println(list.get(1));
        }catch (IndexOutOfBoundsException e){
    
    
            e.printStackTrace();
        }

        System.out.println("后续代码继续执行...");
    }

在这里插入图片描述


一次捕获,多次处理

public static void main(String[] args) {
    
    
        try{
    
    
            int[] arr = {
    
    1,2,3};
            List<Integer> list = new ArrayList<>();
            System.out.println(arr[3]);
            list.add(1);
            System.out.println(list.get(1));
        }catch (ArrayIndexOutOfBoundsException e){
    
    
            e.printStackTrace();
        }catch (IndexOutOfBoundsException e){
    
    
            e.printStackTrace();
        }
        System.out.println("后续代码继续执行...");
    }

在这里插入图片描述
注意事项:一个try多个catch,catch中的异常变量如果有父子关系,子类异常变量必须写在上面,否则会报错。(XXXException has already been caught)


一次捕获,一次处理

public static void main(String[] args) {
    
    
        try {
    
    
            int[] arr = {
    
    1, 2, 3};
            List<Integer> list = new ArrayList<>();
            System.out.println(arr[3]);
            list.add(1);
            System.out.println(list.get(1));
        }catch (Exception e){
    
    
            e.printStackTrace();
        }
        System.out.println("后续代码继续执行...");
    }

运行时异常被抛出可以不处理。即不捕获也不声明抛出。

如果finally有return语句,永远返回finally中的结果,避免该情况。

如果父类抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常。

父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理【try-catch】,不能声明抛出。

5 自定义异常

  • 自定义异常类一部都是以Exception结尾
  • 必须继承Exception(编译期异常,要么try-catch要么throws)或者继承RuntimeException(运行期异常交给JVM中断处理)

模拟注册操作

  1. 定义异常类 继承Exception
public class RegisterException extends Exception {
    
    
    public RegisterException() {
    
    
        super();
    }
    public RegisterException(String message) {
    
    
        super(message);
    }
}
  1. 定义测试类

throws

public class Register {
    
    
    static String[] usernames = {
    
    "张三","李四","王五"};

    public static void main(String[] args) throws RegisterException {
    
    
        System.out.print("请输入待注册用户名:");
        Scanner sc = new Scanner(System.in);
        String username = sc.next();
        checkUserName(username);
        System.out.println("恭喜,注册成功!");
    }
    //继承Exception是编译期异常 throws 处理
    public static void checkUserName(String name) throws RegisterException {
    
    
        for(String n: usernames){
    
    
            if(n.equals(name)){
    
    
                throw new RegisterException("该用户名已注册");
            }
        }
    }
}

try-catch

public class Register {
    
    
    static String[] usernames = {
    
    "张三", "李四", "王五"};

    public static void main(String[] args) {
    
    
        System.out.print("请输入待注册用户名:");
        Scanner sc = new Scanner(System.in);
        String username = sc.next();
        try {
    
    
            checkUserName(username);
        } catch (RegisterException e) {
    
    
            e.printStackTrace();
            return;
        }
        System.out.println("恭喜,注册成功!");
    }
    //继承Exception是编译期异常 throws 处理
    public static void checkUserName(String name) throws RegisterException {
    
    
        for(String n: usernames){
    
    
            if(n.equals(name)){
    
    
                throw new RegisterException("该用户名已注册");
            }
        }
    }
}


  1. 定义异常类 继承自RuntimeException
public class RegisterException extends RuntimeException {
    
    
    public RegisterException() {
    
    
        super();
    }
    public RegisterException(String message) {
    
    
        super(message);
    }
}
  1. 定义测试类:不需要处理运行期异常
public class Register {
    
    
    static String[] usernames = {
    
    "张三","李四","王五"};
    public static void main(String[] args){
    
    
        System.out.print("请输入待注册用户名:");
        Scanner sc = new Scanner(System.in);
        String username = sc.next();
        checkUserName(username);
        System.out.println("恭喜,注册成功!");
    }
    //继承RuntimeExceptionq
    public static void checkUserName(String name) throws RegisterException {
    
    
        for(String n: usernames){
    
    
            if(n.equals(name)){
    
    
                throw new RegisterException("该用户名已注册");//抛出给JVM处理
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/xd963625627/article/details/105297046