zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Java中的异常处理(二)

JAVA异常 处理
2023-09-27 14:25:56 时间
3. 如何进行异常处理


在 Java 语言中 异常处理机制可以分为两部分


抛出异常 当一个方法发生错误时 会创建一个异常对象 并交给运行时系统处理


捕获异常 在方法抛出异常之后 运行时系统将转为寻找合适的异常处理器。


Java 通过 5 个关键字来实现异常处理 分别是 throw、throws、try、catch、finally。


异常总是先抛出 后捕获的。下面我们将围绕着 5 个关键字来详细讲解如何抛出异常以及如何捕获异常。


4. 抛出异常


4.1 实例


我们先来看一个除零异常的实例代码


package com.caq.exception;

public class Demo01 {

 //打印a/b的结果

 public static void test(int a,int b){

 System.out.println(a/b);

 public static void main(String[] args) {

 //调用test方法

 test(5,0);

}


运行结果


Exception in thread main java.lang.ArithmeticException: / by zero

 at com.caq.exception.Demo01.test(Demo01.java:6)

 at com.caq.exception.Demo01.main(Demo01.java:11)

Process finished with exit code 1


我们知道 0 是不能用作除数的 由于 test() 方法中除数 b 为 0 所以代码将停止执行并显示了相关的异常信息 此信息为堆栈跟踪


上面的运行结果告诉我们 main 线程发生了类型为 ArithmeticException 的异常 显示消息为 by zero 并且提示了可能发生异常的方法和行号。


4.2 throw


上面的实例中 程序在运行时引发了错误 那么如何来显示抛出 创建 异常呢


我们可以使用 throw 关键字来抛出异常 throw 关键字后面跟异常对象 改写上面的实例代码


package com.caq.exception;

public class Demo01 {

 //打印a/b的结果

 public static void test(int a,int b){

 if (b 0){

 //抛出异常

 throw new ArithmeticException( 被除数不能为零 

 System.out.println(a/b);

 public static void main(String[] args) {

 //调用test方法

 test(5,0);

}


运行结果


Exception in thread main java.lang.ArithmeticException: 被除数不能为零

 at com.caq.exception.Demo01.test(Demo01.java:8)

 at com.caq.exception.Demo01.main(Demo01.java:15)


代码在运行时同样引发了错误 但显示消息为 “除数不能为零”。我们看到 test() 方法中加入了条件判断 如果调用者将参数 b 设置为 0 时 会使用 throw 关键字来抛出异常 throw 后面跟了一个使用 new 关键字实例化的算数异常对象 并且将消息字符串作为参数传递给了算数异常的构造函数。


我们可以使用 throw 关键字抛出任何类型的 Throwable 对象 它会中断方法 throw 语句之后的所有内容都不会执行。除非已经处理抛出的异常。异常对象不是从方法中返回的 而是从方法中抛出的。


4.3 throws


可以通过 throws 关键字声明方法要抛出何种类型的异常。如果一个方法可能会出现异常 但是没有能力处理这种异常 可以在方法声明处使用 throws 关键字来声明要抛出的异常。例如 汽车在运行时可能会出现故障 汽车本身没办法处理这个故障 那就让开车的人来处理。


throws 用在方法定义时声明该方法要抛出的异常类型 如下是伪代码


public void demoMethod() throws Exception1, Exception2, ... ExceptionN {

 // 可能产生异常的代码

}


throws 后面跟的异常类型列表可以有一个也可以有多个 多个则以 , 分割。当方法产生异常列表中的异常时 将把异常抛向方法的调用方 由调用方处理。


throws 有如下使用规则


如果方法中全部是非检查异常 即 Error、RuntimeException 以及的子类 那么可以不使用 throws 关键字来声明要抛出的异常 编译器能够通过编译 但在运行时会被系统抛出


如果方法中可能出现检查异常 就必须使用 throws 声明将其抛出或使用 try catch 捕获异常 否则将导致编译错误


当一个方法抛出了异常 那么该方法的调用者必须处理或者重新抛出该异常


当子类重写父类抛出异常的方法时 声明的异常必须是父类所声明异常的同类或子类。


5. 捕获异常


使用 try 和 catch 关键字可以捕获异常。try catch 代码块放在异常可能发生的地方。它的语法如下


Tips 

再IDEA里可以通过//ctrl alt t选择代码自动包裹

try {

 // 可能会发生异常的代码块

} catch (Exception e1) {

 // 捕获并处理try抛出的异常类型Exception

} catch (Exception2 e2) {

 // 捕获并处理try抛出的异常类型Exception2

} finally {

 // 无论是否发生异常 都将执行的代码块

}


我们来看一下上面语法中的 3 种语句块


try 语句块 用于监听异常 当发生异常时 异常就会被抛出


catch 语句块 catch 语句包含要捕获的异常类型的声明 当 try 语句块发生异常时 catch 语句块就会被检查。当 catch 块尝试捕获异常时 是按照 catch 块的声明顺序从上往下寻找的 一旦匹配 就不会再向下执行。因此 如果同一个 try 块下的多个 catch 异常类型有父子关系 应该将子类异常放在前面 父类异常放在后面


finally 语句块 无论是否发生异常 都会执行 finally 语句块。finally 常用于这样的场景 由于 finally 语句块总是会被执行 所以那些在 try 代码块中打开的 并且必须回收的物理资源 如数据库连接、网络连接和文件 一般会放在 finally 语句块中释放资源。


try 语句块后可以接零个或多个 catch 语句块 如果没有 catch 块 则必须跟一个 finally 语句块。简单来说 try 不允许单独使用 必须和 catch 或 finally 组合使用 catch 和 finally 也不能单独使用。


实例如下


package com.caq.exception;

public class Demo01 {

 //打印a/b的结果

 public static void test(int a,int b){

 System.out.println(a/b);

 public static void main(String[] args) {

 //调用test方法

 try {

 test(5,0);

 }catch (ArithmeticException e1){

 System.out.println( 捕获的异常为 e1);

 }finally {

 System.out.println( 无论是否发送异常 我都会执行哦~ 

}


运行结果


捕获的异常为 java.lang.ArithmeticException: / by zero

无论是否发送异常 我都会执行哦~


test() 方法中除数 b 为 0 会发生除零异常 我们在方法调用处使用了 try 语句块对异常进行捕获 如果捕获到了异常 catch 语句块会对 ArithmeticException 类型的异常进行处理 此处打印了一行自定义的提示语句


最后的 finally 语句块 无论发生异常与否 总会执行。


Java 7 以后 catch 多种异常时 也可以像下面这样简化代码


try {

 // 可能会发生异常的代码块

} catch (Exception | Exception2 e) {

 // 捕获并处理try抛出的异常类型

} finally {

 // 无论是否发生异常 都将执行的代码块

}


6. 自定义异常


自定义异常 就是定义一个类 去继承 Throwable 类或者它的子类。


Java 内置了丰富的异常类 通常使用这些内置异常类 就可以描述我们在编码时出现的大部分异常情况。一旦内置异常无法满足我们的业务要求 就可以通过自定义异常描述特定业务产生的异常类型。


实例


public class ExceptionDemo4 {

 static class MyCustomException extends RuntimeException {

 * 无参构造方法

 public MyCustomException() {

 super( 我的自定义异常 

 public static void main(String[] args) {

 // 直接抛出异常

 throw new MyCustomException();

}


运行结果


Exception in thread main ExceptionDemo4$MyCustomException: 我的自定义异常

 at ExceptionDemo4.main(ExceptionDemo4.java:13)


在代码中写了一个自定义异常 MyCustomException 继承自 RuntimeException 它是一个静态内部类 这样在主方法中就可以直接抛出这个异常类了。当然 也可以使用 catch 来捕获此类型异常。


7. 异常链


异常链是以一个异常对象为参数构造新的异常对象 新的异常对象将包含先前异常的信息。简单来说 就是将异常信息从底层传递给上层 逐层抛出 我们来看一个实例


public class ExceptionDemo5 {

 * 第一个自定义的静态内部异常类

 static class FirstCustomException extends Exception {

 // 无参构造方法

 public FirstCustomException() {

 super( 第一个异常 

 * 第二个自定义的静态内部异常类

 static class SecondCustomException extends Exception {

 public SecondCustomException() {

 super( 第二个异常 

 * 第三个自定义的静态内部异常类

 static class ThirdCustomException extends Exception {

 public ThirdCustomException() {

 super( 第三个异常 

 * 测试异常链静态方法1 直接抛出第一个自定义的静态内部异常类

 * throws FirstCustomException

 public static void f1() throws FirstCustomException {

 throw new FirstCustomException();

 * 测试异常链静态方法2 调用f1()方法 并抛出第二个自定义的静态内部异常类

 * throws SecondCustomException

 public static void f2() throws SecondCustomException {

 try {

 f1();

 } catch (FirstCustomException e) {

 throw new SecondCustomException();

 * 测试异常链静态方法3 调用f2()方法 并抛出第三个自定义的静态内部异常类

 * throws ThirdCustomException

 public static void f3() throws ThirdCustomException {

 try {

 f2();

 } catch (SecondCustomException e) {

 throw new ThirdCustomException();

 public static void main(String[] args) throws ThirdCustomException {

 // 调用静态方法f3()

 f3();

}


运行结果


Exception in thread main ExceptionDemo5$ThirdCustomException: 第三个异常

 at ExceptionDemo5.f3(ExceptionDemo5.java:46)

 at ExceptionDemo5.main(ExceptionDemo5.java:51)


通过运行结果 我们只获取到了静态方法 f3() 所抛出的异常堆栈信息 前面代码所抛出的异常并没有被显示。


我们改写上面的代码 让异常信息以链条的方式 “连接” 起来。可以通过改写自定义异常的构造方法 来获取到之前异常的信息。实例如下


public class ExceptionDemo6 {

 * 第一个自定义的静态内部异常类

 static class FirstException extends Exception {

 // 无参构造方法

 public FirstException() {

 super( 第一个异常 

 * 第二个自定义的静态内部异常类

 static class SecondCustomException extends Exception {

 * 通过构造方法获取之前异常的信息

 * param cause 捕获到的异常对象

 public SecondCustomException(Throwable cause) {

 super( 第二个异常 , cause);

 * 第三个自定义的静态内部异常类

 static class ThirdCustomException extends Exception {

 * 通过构造方法获取之前异常的信息

 * param cause 捕获到的异常对象

 public ThirdCustomException(Throwable cause) {

 super( 第三个异常 , cause);

 * 测试异常链静态方法1 直接抛出第一个自定义的静态内部异常类

 * throws FirstException

 public static void f1() throws FirstException {

 throw new FirstException();

 * 测试异常链静态方法2 调用f1()方法 并抛出第二个自定义的静态内部异常类

 * throws SecondCustomException

 public static void f2() throws SecondCustomException {

 try {

 f1();

 } catch (FirstException e) {

 throw new SecondCustomException(e);

 * 测试异常链静态方法3 调用f2()方法 并抛出第三个自定义的静态内部异常类

 * throws ThirdCustomException

 public static void f3() throws ThirdCustomException {

 try {

 f2();

 } catch (SecondCustomException e) {

 throw new ThirdCustomException(e);

 public static void main(String[] args) throws ThirdCustomException {

 // 调用静态方法f3()

 f3();

}


运行结果


Exception in thread main ExceptionDemo6$ThirdCustomException: 第三个异常

 at ExceptionDemo6.f3(ExceptionDemo6.java:74)

 at ExceptionDemo6.main(ExceptionDemo6.java:80)

Caused by: ExceptionDemo6$SecondCustomException: 第二个异常

 at ExceptionDemo6.f2(ExceptionDemo6.java:62)

 at ExceptionDemo6.f3(ExceptionDemo6.java:72)

 ... 1 more

Caused by: ExceptionDemo6$FirstException: 第一个异常

 at ExceptionDemo6.f1(ExceptionDemo6.java:51)

 at ExceptionDemo6.f2(ExceptionDemo6.java:60)

 ... 2 more


通过运行结果 我们看到 异常发生的整个过程都打印到了屏幕上 这就是一个异常链。


8. 小结


通过学习 我们知道了异常就是程序上的错误 良好的异常处理可以提高代码的健壮性。


Java 语言中所有错误 Error 和异常 Exception 的父类都是 Throwable。


Error 和 Exception 是 Throwable 的直接子类 我们通常说的异常处理实际上就是处理 Exception 及其子类 异常又分为检查型异常和非检查型异常。


通过抛出异常和捕获异常来实现异常处理。我们亦可以通过继承 Throwable 类或者它的子类来自定义异常类。通过构造方法获取之前异常的信息可以实现异常链


Java后端开发你应该知道的全局异常处理 在开发Web应用程序时,异常处理是一项非常重要的任务。异常处理可以提高程序的健壮性和稳定性。Java后端开发人员可以设计一个统一的全局异常处理方案来解决异常处理的问题,避免代码冗余,提高开发效率。在本文中,我们将介绍如何设计Java后端的全局异常处理方案。
C++ 和 Java 中的默认虚拟行为有何不同及其异常处理的比较 在 C++ 中,类成员方法默认是非虚拟的。可以使用virtual关键字将它们设为虚拟。例如,Base::show() 在以下程序中是非虚拟的,并且程序打印 “Base::show() called”
Java 异常处理 异常,指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常终止在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象,Java处理异常的方式是中断处理
Java异常处理和最佳实践(含案例分析) 如何处理Java异常?作者查看了一些异常处理的规范,对 Java 异常处理机制有更深入的了解,并将自己的学习内容记录下来,希望对有同样困惑的同学提供一些帮助。