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块(如果有匹配的异常类型被抛出)。
- try 块用于包含那些可能会抛出异常的代码语句。这些代码在正常执行时会按照顺序依次执行,但如果在执行过程中遇到了异常情况,程序的执行流程就会立即跳转到相应的
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();}
}
调换顺序之后,立刻报错
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) -
throw和throws的基本概念throw:throw是用于在方法内部显式地抛出一个异常对象。当执行throw语句时,当前方法的执行流程会立即停止,并将异常对象传递给调用这个方法的代码(如果有)进行处理。
throws:throws用于在方法声明中,表示该方法可能会抛出某些类型的异常。它只是一种声明,告诉调用这个方法的代码,这个方法可能会抛出这些异常,调用者需要进行相应的处理(例如使用try - catch结构)。
-
结合代码分析
throw和throws的区别- 在上述
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();来打印异常的栈追踪信息。
- 在
- 在上述
-
总结
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()打印出了详细的异常堆栈跟踪信息,展示了异常在哪个类、哪一行代码被抛出以及调用的层级关系等情况。

浙公网安备 33010602011771号