Java抛出异常

当某个方法抛出了异常时,如果当前方法没有捕获异常,异常就会被抛到上层调用方法,直到遇到某个try ... catch被捕获为止

  • 调用printStackTrace()可以打印异常的传播栈,对于调试非常有用;
  • 捕获异常并再次抛出新的异常时,应该持有原始异常信息;
  • 通常不要在finally中抛出异常。如果在finally中抛出异常,应该原始异常加入到原有异常中。调用方可通过Throwable.getSuppressed()获取所有添加的Suppressed Exception
public class Main {public static void main(String[] args) {try {process1();} catch (Exception e) {e.printStackTrace();}}static void process1() {process2();}static void process2() {Integer.parseInt(null); // 会抛出NumberFormatException}
}

通过printStackTrace()可以打印出方法的调用栈,类似:

java.lang.NumberFormatException: null
    at java.base/java.lang.Integer.parseInt(Integer.java:614)
    at java.base/java.lang.Integer.parseInt(Integer.java:770)
    at Main.process2(Main.java:16)
    at Main.process1(Main.java:12)
    at Main.main(Main.java:5)

printStackTrace()对于调试错误非常有用,上述信息表示:NumberFormatException是在java.lang.Integer.parseInt方法中被抛出的,从下往上看,调用层次依次是:

  1. main()调用process1()
  2. process1()调用process2()
  3. process2()调用Integer.parseInt(String)
  4. Integer.parseInt(String)调用Integer.parseInt(String, int)

查看Integer.java源码可知,抛出异常的方法代码如下:

public static int parseInt(String s, int radix) throws NumberFormatException {if (s == null) {throw new NumberFormatException("null");}...
}

并且,每层调用均给出了源代码的行号,可直接定位。

抛出异常

当发生错误时,例如,用户输入了非法的字符,我们就可以抛出异常。

如何抛出异常?参考Integer.parseInt()方法,抛出异常分两步:

  1. 创建某个Exception的实例;
  2. throw语句抛出。

下面是一个例子:

void process2(String s) {if (s==null) {NullPointerException e = new NullPointerException();throw e;}
}

实际上,绝大部分抛出异常的代码都会合并写成一行:

void process2(String s) {if (s==null) {throw new NullPointerException();}
}

如果一个方法捕获了某个异常后,又在catch子句中抛出新的异常,就相当于把抛出的异常类型“转换”了

void process1(String s) {try {process2();} catch (NullPointerException e) {throw new IllegalArgumentException();}
}void process2(String s) {if (s==null) {throw new NullPointerException();}
}

process2()抛出NullPointerException后,被process1()捕获,然后抛出IllegalArgumentException()

如果在main()中捕获IllegalArgumentException,打印的异常栈

public class Main {public static void main(String[] args) {try {process1();} catch (Exception e) {e.printStackTrace();}}static void process1() {try {process2();} catch (NullPointerException e) {throw new IllegalArgumentException();}}static void process2() {throw new NullPointerException();}
}

打印出的异常栈类似:

这说明新的异常丢失了原始异常信息,我们已经看不到原始异常NullPointerException的信息了。为了能追踪到完整的异常栈,在构造异常的时候,把原始的Exception实例传进去,新的Exception就可以持有原始Exception信息。对上述代码改进如下:

public class Main {public static void main(String[] args) {try {process1();} catch (Exception e) {e.printStackTrace();}}static void process1() {try {process2();} catch (NullPointerException e) {throw new IllegalArgumentException(e);}}static void process2() {throw new NullPointerException();}
}

 运行上述代码,打印出的异常栈类似:

注意到Caused by: Xxx,说明捕获的IllegalArgumentException并不是造成问题的根源,根源在于NullPointerException,是在Main.process2()方法抛出的。

在代码中获取原始异常可以使用Throwable.getCause()方法如果返回null,说明已经是“根异常”了。有了完整的异常栈的信息,我们才能快速定位并修复代码的问题。

如果我们在try或者catch语句块中抛出异常,finally语句是否会执行?

例如:

public class Main {public static void main(String[] args) {try {Integer.parseInt("abc");} catch (Exception e) {System.out.println("catched");throw new RuntimeException(e);} finally {System.out.println("finally");}}
}

上述代码执行结果如下:

第一行打印了catched,说明进入了catch语句块。第二行打印了finally,说明执行了finally语句块。因此,catch中抛出异常,不会影响finally的执行。JVM会先执行finally,然后抛出异常。

异常屏蔽

如果在执行finally语句时抛出异常,那么,catch语句的异常还能否继续抛出?

例如:

public class Main {public static void main(String[] args) {try {Integer.parseInt("abc");} catch (Exception e) {System.out.println("catched");throw new RuntimeException(e);} finally {System.out.println("finally");throw new IllegalArgumentException();}}
}

 ​​​​

这说明finally抛出异常后,原来在catch中准备抛出的异常就“消失”了因为只能抛出一个异常。没有被抛出的异常称为“被屏蔽”的异常(Suppressed Exception)。

在极少数的情况下,我们需要获知所有的异常。如何保存所有的异常信息?方法是先用origin变量保存原始异常,然后调用Throwable.addSuppressed(),把原始异常添加进来,最后在finally抛出

public class Main {public static void main(String[] args) throws Exception {Exception origin = null;try {System.out.println(Integer.parseInt("abc"));} catch (Exception e) {origin = e;throw e;} finally {Exception e = new IllegalArgumentException();if (origin != null) {e.addSuppressed(origin);}throw e;}}
}

catchfinally都抛出了异常时,虽然catch的异常被屏蔽了,但是,finally抛出的异常仍然包含了它。通过Throwable.getSuppressed()可以获取所有的Suppressed Exception。绝大多数情况下,在finally中不要抛出异常。因此,我们通常不需要关心Suppressed Exception

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/94416.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

接口测试系列 —— POSTMAN的简单使用

postman的基本使用 概述 我相信对于postman的介绍,网上一搜肯定很多很多。下面我就不打算跟大家普及postman了。只看应该怎么用postman进行接口测试。好了,下面咱们直接进入正文吧。 环境 postman之前是作为chrome插件形式存在的。后面变成了独立的应…

从零开始,探索C语言中的字符串

字符串 1. 前言2. 预备知识2.1 字符2.2 字符数组 3. 什么是字符串4. \04.1 \0是什么4.2 \0的作用4.2.1 打印字符串4.2.2 求字符串长度 1. 前言 大家好,我是努力学习游泳的鱼。你已经学会了如何使用变量和常量,也知道了字符的概念。但是你可能还不了解由…

Loki日志系统

1、Loki是什么? Loki是一个开源的日志聚合系统,由Grafana Labs开发和维护。它旨在帮助用户收集、存储和查询大规模的日志数据,帮助用户更好地理解和监控他们的应用程序和系统。 Loki的设计灵感来自于Prometheus,它采用了类似的标…

肖sir__linux详解__003(vim命令)

linux 文本编辑命令 作用:用于编辑一个文件 用法:vim 文件名称 或者vi (1)编辑一个存在的文档 例子:编辑一个file1文件 vim aa (2)编辑一个文件不存在,会先创建文件,再…

lv3 嵌入式开发-3 linux shell命令(权限、输入输出)

1 Shell概述 随着各式Linux系统的图形化程度的不断提高,用户在桌面环境下,通过点击、拖拽等操作就可以完成大部分的工作。 然而,许多Ubuntu Linux功能使用shell命令来实现,要比使用图形界面交互,完成的更快、更直接。…

小兔鲜商02

npm i vueuse/core -fvue插件使用: 许多公用的全局组件,,可以通过插件注册进去,就不用一个一个导入组件,, import XtxSkeleton from /components/library/xtx-skeletonexport default {install (app) {// …

安装配置mariadb

记录下安装配置mariadb的经历。 环境:ubuntu22 一、apt在线安装 apt代理配置 APT是Ubuntu系统中用于安装和升级软件包的工具,如果本地没有可用的软件包,APT将会连接到远程软件包服务器下载软件包。在某些情况下,用户需要将APT的…

已解决‘jupyter‘ 不是内部或外部命令,也不是可运行的程序或批处理文件报错

本文摘要:本文已解决‘jupyter‘ 不是内部或外部命令,也不是可运行的程序或批处理文件的相关报错问题,并系统性地总结提出了几种可用解决方案。同时结合人工智能GPT排除可能得隐患及错误。 😎 作者介绍:我是程序员洲洲…

图:有向无环图(DAG)

1.有向无环图的定义 有向无环图:若一个有向图中不存在环,则称为有向无环图。 简称DAG图(Directed Acyclic Graph) 顶点中不可能出现重复的操作数。 2.有向无环图的应用 1.描述算数表达式 用有向无环图描述算术表达式。 解题步骤: 把各个操作数不重…

Linux查看目录下文件及其大小

ls -lh在Linux下,"ls -lh"是一个用于显示文件和目录详细信息的命令。它会列出当前目录中的文件和目录,并显示它们的文件大小和权限等详细信息。 其中,参数"-l"是用来显示详细信息的选项,"h"表示以…

【Jetpack】Navigation 导航组件 ⑤ ( NavigationUI 类使用 )

文章目录 一、NavigationUI 类简介二、NavigationUI 类使用流程1、创建 Fragment2、创建 NavigationGraph3、Activity 导入 NavHostFragment4、创建菜单5、Activity 界面开发 NavigationUI 的主要逻辑 ( 重点 )a、添加 Fragment 布局b、处理 Navigation 导航逻辑 ( 重点 )c、启…

视频汇聚/视频监控管理平台EasyCVR接入海康SDK协议后无法播放该如何解决?

开源EasyDarwin视频监控/安防监控/视频汇聚EasyCVR能在复杂的网络环境中,将分散的各类视频资源进行统一汇聚、整合、集中管理,在视频监控播放上,视频安防监控汇聚平台可支持1、4、9、16个画面窗口播放,可同时播放多路视频流&#…