Day38--捕获和抛出异常

Day38--捕获和抛出异常

异常处理机制

  • 抛出异常
  • 捕获异常
  • 异常处理五个关键字
  • try、catch、finally、throw、throws

例如:

在Test里面,a=1、b=0,输出a/b,看结果-----一定会有异常

package com.liu.exception;

public class Test {
    public static void main(String[] args) {
        int a=1;
        int b=0;
        System.out.println(a/b);
    }
}
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at com.liu.exception.Test.main(Test.java:11)

我们能不能想办法捕获异常,然后返回一些信息呢?

​ 通过try-catch-finally 结构

package com.liu.exception;

public class Test {
    public static void main(String[] args) {
        int a=1;
        int b=0;
        try {
            System.out.println(a/b);
        } catch (ArithmeticException e) {
            System.out.println("程序出现异常,变量b不能为0");
        } finally {
            System.out.println("finally");
        }
    }
}

输出结果:

程序出现异常,变量b不能为0
finally

以下是对这段代码中 try-catch-finally 结构部分的详细解释:

try 语句块

try {
    System.out.println(a / b);
}
  • 作用
    • try 语句块用于包裹可能会抛出异常的代码段。在这个例子中,System.out.println(a / b); 这行代码是有可能出现异常的操作。因为这里是进行除法运算 a / b,而从前面代码的定义可知 b = 0,在数学运算中,除数不能为零,所以这个除法操作在程序运行时极有可能引发异常。

catch 语句块

catch (ArithmeticException e) {
    System.out.println("程序出现异常,变量b不能为0");
}
  • 作用
    • catch 语句块用于捕获在 try 语句块中抛出的特定类型的异常。在这里,它捕获的是 ArithmeticException 类型的异常,这是Java中专门用于表示算术运算异常的类型,当出现如除数为零这样的算术错误时,就会抛出这种异常。
    • try 语句块中的代码抛出 ArithmeticException 异常时,程序的执行流程会立即跳转到这个 catch 语句块中。然后执行 catch 语句块内的代码,也就是会输出 "程序出现异常,变量b不能为0" 这条提示信息,这样就可以让程序员或者用户知道程序在算术运算方面出现了问题,并且明确指出是因为变量 b 不能为零导致的。
    • e 就是用于在 catch 语句块中接住并能处理从 try 语句块抛出的 ArithmeticException 异常对象的一个变量。

finally 语句块

finally {
    System.out.println("finally");
}
  • 作用
    • finally 语句块中的代码无论 try 语句块中是否抛出异常,都会被执行。也就是说,不管前面的除法运算 a / b 是正常执行完毕还是抛出了 ArithmeticException 异常被 catch 语句块捕获处理,最终都会执行 finally 语句块内的代码,在这里就是输出 "finally" 这条信息。
    • finally 语句块通常用于执行一些无论如何都需要完成的清理工作,比如关闭文件流、释放数据库连接等资源,确保程序在各种情况下都能正确地完成这些必要的收尾操作,不过在这个简单例子里只是简单地输出一条信息来展示其执行特性。

当catch里面的异常与try里面的代码异常不一样时,运行结果是什么样的?

示例:

try代码块抛出的错误是ArithmeticException,但是catch代码块捕获的是NullPointerException,两者不匹配

package com.liu.exception;

public class Test {
    public static void main(String[] args) {
        int a=1;
        int b=0;
        try {
            System.out.println(a/b);
        } catch (NullPointerException e) {
            System.out.println("程序出现异常,变量b不能为0");
        } finally {
            System.out.println("finally");
        }
    }
}
finally
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at com.liu.exception.Test.main(Test.java:8)

从上面可知,当 catch 里面的异常与 try 里面的代码实际抛出的异常不一样时,catch 块不会处理该异常,finally 块会执行,然后异常会继续向外抛出导致程序可能出现异常终止的情况(如果没有在更外层进行捕获处理的话)。

try-catch-finally 结构的详细语法及相关要点:

基本语法形式

try {
    // 可能会抛出异常的代码块
} catch (ExceptionType1 e1) {
    // 处理ExceptionType1类型异常的代码块
} catch (ExceptionType2 e2) {
    // 处理ExceptionType2类型异常的代码块
}...
finally {
    // 无论是否发生异常,最终都会执行的代码块
}

各部分说明

1. try 块

  • 作用

    • try 块用于包含那些可能会抛出异常的代码语句。这些代码在正常执行时会按照顺序依次执行,但如果在执行过程中遇到了异常情况,程序的执行流程就会立即跳转到相应的 catch 块(如果有匹配的异常类型被抛出)。

2. catch 块

  • 语法
    • catch 块紧跟在 try 块之后,用于捕获并处理特定类型的异常。它的语法形式是 catch (ExceptionType e),其中 ExceptionType 是具体的异常类型,必须是 Throwable 类或其子类(Java 中所有的异常类都继承自 Throwable 类)。例如,常见的异常类型有 ArithmeticException(算术异常)、NullPointerException(空指针异常)、IOException(输入输出异常)等。
    • e 是一个引用变量,用于接收从 try 块中抛出的对应类型的异常对象,通过这个变量可以在 catch 块内进一步获取和处理该异常对象包含的信息,比如输出异常详细信息、进行一些补救措施等。
  • 多个 catch 块
    • 可以有多个 catch 块跟在 try 块后面,用于捕获不同类型的异常。当 try 块中抛出异常时,程序会按照 catch 块的顺序依次检查每个 catch 块所指定的异常类型,一旦找到与抛出异常类型匹配的 catch 块,就会执行该 catch 块内的代码,并且不再检查后面的 catch 块。所以在编写多个 catch 块时,通常要将更具体的异常类型放在前面,更笼统的异常类型放在后面,也就是将范围小的异常类型写在上面,范围大的异常类型写在下面,以确保异常能够被正确处理。

3. finally 块

  • 作用
    • finally 块位于 catch 块之后(如果有多个 catch 块,就在最后一个 catch 块之后),无论 try 块中的代码是否抛出异常,也无论抛出的异常是否被 catch 块成功捕获并处理,finally 块中的代码都会被执行。
    • 它通常用于执行一些必须要完成的清理工作,比如关闭文件流、释放数据库连接、清理内存资源等,以确保程序在各种情况下都能正确地完成这些必要的收尾操作。

try、catch一定要有,finally可以没有!!!!!!!!!!!!!!!

再比如:

​ 创建a、b两个方法:a方法是引用b方法;b方法是引用a方法。然后在main方法里面使用a方法

package com.liu.exception;

public class Test {
    public static void main(String[] args) {
        int a=1;
        int b=0;
//        try {
//            System.out.println(a/b);
//        } catch (NullPointerException e) {
//            System.out.println("程序出现异常,变量b不能为0");
//        } finally {
//            System.out.println("finally");
//        }


        new Test().a();
    }


    public void a(){b();}
    public void b(){a();}

}

运行结果:

Exception in thread "main" java.lang.StackOverflowError   //栈溢出

然后使用try-catch-finally 结构

package com.liu.exception;

public class Test {
    public static void main(String[] args) {
        int a=1;
        int b=0;


        try {
            new Test().a();
        } catch (StackOverflowError e) {
            System.out.println("程序出现异常");
        } finally {
            System.out.println("finally");
        }
    }


    public void a(){b();}
    public void b(){a();}

}

运行结果:

程序出现异常
finally

如果将异常的类型改为Throwable,运行结果是什么样的?

package com.liu.exception;

public class Test {
    int a=1;
    int b=0;

    public static void main(String[] args) {
        Test test = new Test();
        int a= test.a;
        int b= test.b;


        try{//try监控区域
            test.a();
        } catch (Throwable e) {  //catch捕获异常
            System.out.println("程序出现异常");
        }finally {  //finally   善后
            System.out.println("finally");
        }


    }
    //创建两个方法
    public void a(){b();}
    public void b(){a();}
}

程序出现异常
finally

结果一样

​ 这是因为,catch (ExceptionType e),ExceptionType 是具体的异常类型,必须是 Throwable 类或其子类

catch类型可以写多个,最好是按照将范围小的异常类型写在上面,范围大的异常类型写在下面的原则写

例如:

package com.liu.exception;

public class Test {
    int a=1;
    int b=0;

    public static void main(String[] args) {
        Test test = new Test();
        int a= test.a;
        int b= test.b;


        try{//try监控区域
            test.a();
        } catch (StackOverflowError e) {  //catch捕获异常
            System.out.println("StackOverflowError程序出现异常");
        }catch (Error e){
            System.out.println("Error程序出现异常");
        }catch (Throwable e){
            System.out.println("Throwable程序出现异常");
        }finally {  //finally   善后
            System.out.println("finally");
        }


    }
    //创建两个方法
    public void a(){b();}
    public void b(){a();}
}

能不能将范围大的异常类型写在上面?我们来试一下

package com.liu.exception;

public class Test {
    int a=1;
    int b=0;

    public static void main(String[] args) {
        Test test = new Test();
        int a= test.a;
        int b= test.b;


        try{//try监控区域
            test.a();
        } catch (Error e) {  //catch捕获异常
            System.out.println("Error程序出现异常");
        }catch (Throwable e){
            System.out.println("Throwable程序出现异常");
        }catch (StackOverflowError e){ //------------------------------报错
            System.out.println("StackOverflowError程序出现异常");
        }finally {  //finally   善后
            System.out.println("finally");
        }


    }
    //创建两个方法
    public void a(){b();}
    public void b(){a();}
}

调换顺序之后,立刻报错

image-20241125153429500

catch有没有快捷键?

​ 有!!!!!!!!!!!!!

例如:

package com.liu.exception;

public class Test2 {
    public static void main(String[] args) {
        int a=1;
        int b=0;
        System.out.println(a/b);
    }
}

选中 System.out.println(a/b);----------Ctrl+Alt+T---------选中Try/Catch

package com.liu.exception;

public class Test2 {
    public static void main(String[] args) {
        int a=1;
        int b=0;
        try {
            System.out.println(a/b);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

在catch进行修改, e.printStackTrace();打印在控制台打印出异常的栈追踪信息,也就是能够清晰地显示出异常是在哪个类、哪个方法、哪一行代码处被抛出的,以及在到达这个抛出点之前经过了哪些方法的调用等情况。

package com.liu.exception.demo01;

public class Test {
    public static void main(String[] args) {
        int a=1;
        int b=0;
        try {
            System.out.println(a/b);
        } catch (ArithmeticException e) {

            e.printStackTrace();
        } finally {
            System.out.println("finally");
        }
    }
}
finally
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at com.liu.exception.Test.main(Test.java:8)

我们也可以操作,让catch发现异常之后,终止程序

package com.liu.exception;

public class Test2 {
    public static void main(String[] args) {
        int a=1;
        int b=0;
//        System.out.println(a/b);
        try {
            System.out.println(a/b);
        } catch (Throwable e) {
            System.exit(888);//----------终止程序
            
        } finally {
        }
    }
}

运行结果;

进程已结束,退出代码为 888

throws 和throw的区别

throws关键字

  • 含义throws用于在方法声明中,表示该方法可能会抛出某些类型的异常。它只是一种声明,告诉调用这个方法的代码,这个方法可能会抛出这些异常,调用者需要进行相应的处理(例如使用try - catch结构)。

  • 示例代码

    ​ 在Test类里面,创建方法test,参数int a, int b,声明:抛出ArithmeticException。方法体:如果b==0,抛出异常。

    ​ 然后在main方法里面,调用test方法,使用参数1、0

  • package com.liu.exception;
    
    public class Test {
        public static void main(String[] args) {
    
            try {
                new Test().test( 1,  0);
            } catch (ArithmeticException e) {
                e.printStackTrace();
            }
        }
    
        //假设这方法中,处理不了这个异常。方法上抛出异常
        public void test(int a, int b) throws ArithmeticException{
            if (b==0){ //throw throws
                throw new ArithmeticException(); //主动的抛出异常,一般在方法中使用
            }
        }
    }
    
  • 运行结果:

  • java.lang.ArithmeticException
    	at Test.test(Test.java:14)
    	at Test.main(Test.java:5)
    
  • throwthrows的基本概念

    • throw
      • throw是用于在方法内部显式地抛出一个异常对象。当执行throw语句时,当前方法的执行流程会立即停止,并将异常对象传递给调用这个方法的代码(如果有)进行处理。
    • throws
      • throws用于在方法声明中,表示该方法可能会抛出某些类型的异常。它只是一种声明,告诉调用这个方法的代码,这个方法可能会抛出这些异常,调用者需要进行相应的处理(例如使用try - catch结构)。
  1. 结合代码分析throwthrows的区别

    • 在上述Test类中:
      • test方法中的throw
        • test方法内有这样一行代码:throw new ArithmeticException();
        • 这里的throw是在方法test内部主动抛出一个ArithmeticException异常对象。当test方法被调用且参数b等于0时,就会执行这行throw语句,立即停止test方法的执行,并将ArithmeticException异常对象抛出。
      • test方法声明中的throws
        • test方法的声明中:public void test(int a, int b) throws ArithmeticException
        • 这里的throws ArithmeticException是在声明test方法可能会抛出ArithmeticException类型的异常。这是一种提前告知的方式,告诉调用test方法的代码(在这个例子中是main方法),test方法有抛出ArithmeticException的可能性,调用者(main方法)需要处理这种可能出现的异常。
    • main方法中的处理
      • main方法中,调用new Test().test(1, 0);时,由于test方法可能抛出ArithmeticException(由throws声明),所以main方法使用try - catch结构来捕获可能出现的异常。
      • test方法内部执行throw new ArithmeticException();(由throw引发)时,main方法中的catch块会捕获这个异常,并执行e.printStackTrace();来打印异常的栈追踪信息。
  2. 总结

    • throw是在方法内部实际抛出异常的操作,用于在方法执行过程中遇到错误情况时主动抛出异常,使程序流程转移到异常处理部分。
    • throws是在方法声明时使用,用于告知调用者这个方法可能会抛出某些类型的异常,让调用者负责处理这些可能出现的异常。简单地说,throw是实际抛出异常的动作,throws是对可能抛出异常的声明。

使用new关键字来创建异常对象并抛出是异常处理机制中很重要的一部分,以下是详细讲解:

语法格式

throw new [异常类名]([可选的构造参数]);
  • throw:这是 Java 中的关键字,用于明确地抛出一个异常对象,告知程序当前出现了异常情况,需要进行相应的处理。
  • new:同样是 Java 关键字,用于在内存中创建一个新的对象实例,在这里就是创建指定异常类的对象。
  • [异常类名]:要抛出的具体异常类的名称,Java 中有很多内置的异常类,比如ArithmeticException(算术异常,像除数为 0 时会抛出该异常)、NullPointerException(空指针异常,当使用了值为null的对象引用去访问对象的成员时抛出)、IndexOutOfBoundsException(索引越界异常,常见于数组、集合等访问越界时)等等。你也可以自定义异常类(继承自Exception或者其子类)来表示特定业务场景下的异常情况。
  • [可选的构造参数]:很多异常类都提供了多个构造函数,有的构造函数可以接收参数,这些参数通常用于传递一些和异常相关的详细信息,例如错误消息字符串等,方便后续在查看异常信息时能快速了解异常产生的原因。不过这个参数是可选的,如果不传递,异常类会使用默认的一些信息来表示异常情况。

示例说明

1. 内置异常类示例

ArithmeticException为例,假如有一个除法运算的方法,当除数为 0 时需要抛出异常,代码可以这样写:

public class MathOperation {
    public static int divide(int dividend, int divisor) {
        if (divisor == 0) {
            throw new ArithmeticException("除数不能为0");
        }
        return dividend / divisor;
    }
}

在上述代码中,divide方法用于进行两个整数的除法运算。如果传入的divisor(除数)的值为 0,就会通过throw new ArithmeticException("除数不能为0");语句抛出一个ArithmeticException异常对象,并且这个异常对象携带了 “除数不能为 0” 这条提示信息。当在其他地方调用这个divide方法时,如果出现除数为 0 的情况,就需要使用try-catch语句来捕获并处理这个异常,比如:

public class Main {
    public static void main(String[] args) {
        try {
            int result = MathOperation.divide(10, 0);
            System.out.println("结果: " + result);
        } catch (ArithmeticException e) {
            System.out.println("捕获到算术异常: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

运行这段代码,控制台会输出类似下面的内容:

捕获到算术异常: 除数不能为0
java.lang.ArithmeticException: 除数不能为0
    at MathOperation.divide(MathOperation.java:5)
    at Main.main(Main.java:7)

可以看到,先是通过e.getMessage()获取到了在创建异常对象时传入的 “除数不能为 0” 这条提示信息进行了输出,然后通过e.printStackTrace()打印出了详细的异常堆栈跟踪信息,展示了异常在哪个类、哪一行代码被抛出以及调用的层级关系等情况。

posted @ 2024-11-25 16:09  1hahahahahahahaha  阅读(30)  评论(0)    收藏  举报