Java 异常处理详解

Java异常是Java编程语言中用于表示程序运行时错误的一种机制。Java异常体系通过异常类和异常处理来实现,允许程序在遇到预期或意外情况时,优雅地处理问题,而不是立即终止程序运行。

异常类层次结构

Java异常类都继承自java.lang.Throwable类,它是所有异常和错误的根类。Throwable类有两个直接子类:ErrorException

异常类层次结构

  • Error:这是程序无法捕获或恢复的严重错误,如系统崩溃、内存溢出等。这类错误通常不需要程序处理,因为它们通常是不可控的系统级错误。
  • Exception:这是程序在运行过程中可能出现的可以捕获和处理的异常情况。Exception又分为两类:
    • Checked Exception(编译时异常):编译器要求必须显式处理的异常,如果不处理,代码无法编译通过。例如:IOExceptionSQLException等。
    • Unchecked Exception(运行时异常):也被称为RuntimeException,编译器不要求必须捕获这类异常,但是如果出现则会导致程序立即停止运行,除非它们在代码中被捕获。例如:NullPointerExceptionIllegalArgumentExceptionArrayIndexOutOfBoundsException等。

编译时异常

编译时异常,也称为受检查异常,是指在编译阶段就需要处理的异常。这类异常通常由程序外部环境或非程序自身逻辑错误引起,比如I/O错误网络通信失败数据库连接失败等。编译时异常强调的是异常的预见性和可控性,要求程序员在编写代码时就必须考虑如何处理这些异常

特点

  • 当方法可能抛出编译时异常时,必须在方法签名中通过throws关键字声明该异常,或者在方法体内部使用try-catch块捕获并处理异常。
  • 如果调用含有声明编译时异常的方法的地方没有处理这个异常,编译器会提示错误,直到异常被适当地捕获或声明抛出为止。
  • 常见的编译时异常包括:IOExceptionSQLExceptionClassNotFoundException等。

运行时异常

运行时异常,也称为未检查异常,是指在编译时不强制处理的异常,它们通常由程序内部逻辑错误导致,如空指针异常数组越界算术异常等。运行时异常强调的是程序运行时的正确性和完整性,它们通常反映出代码逻辑的缺陷,程序员也应该尽量避免这些异常的发生,但编译器并不会强制处理。

特点:

  • 编译器不会强迫程序员在方法签名中声明运行时异常,也不会因为在方法体内没有处理运行时异常而导致编译失败。
  • 程序员可以选择捕获并处理运行时异常,但这不是必需的。若没有捕获,当运行时异常发生时,程序将终止,栈轨迹(StackTrace)会显示异常的发生位置和相关信息。
  • JVM默认会抛出运行时异常,除非在调用栈中的某一层有适当的处理代码。
  • 常见的运行时异常包括:NullPointerExceptionArrayIndexOutOfBoundsExceptionClassCastExceptionIllegalArgumentExceptionArithmeticException等。

JVM默认处理异常的方式

Java程序在运行时抛出一个异常,并且没有在当前线程的调用栈中找到合适的catch块来捕获这个异常时,JVM会按照以下步骤来处理这个未捕获的异常:

  1. 寻找最近的未处理异常处理器(UncaughtExceptionHandler)
    • 每个线程都可以设置一个未处理异常处理器,如果线程抛出了未捕获的异常,JVM会首先查找该线程是否设置了自定义的Thread.UncaughtExceptionHandler,如果有,则调用该处理器的uncaughtException(Thread t, Throwable e)方法来处理异常。
  2. 默认的未处理异常处理器
    • 如果线程没有设置自定义的UncaughtExceptionHandler,JVM将使用默认的异常处理器。默认的处理器通常会打印异常的堆栈跟踪信息到标准错误输出(System.err)。
  3. 终止线程
    • 无论是自定义的还是默认的未处理异常处理器,在处理完异常后,JVM通常会选择终止抛出异常的线程。对于主线程(main thread)而言,整个Java应用也会随之退出,因为它承载了Java程序的入口点。

说明:

JVM对于未捕获的异常,默认行为是打印堆栈追踪信息到标准错误输出,并结束抛出异常的线程(如果是主线程,则结束整个Java应用程序)。当然,开发人员可以通过设置自定义的UncaughtExceptionHandler来改变这种默认行为,以便进行更细致的异常处理,比如记录日志、通知监控系统或者进行其他必要的清理工作。

异常处理机制

Java提供了以下几种机制来处理异常:

throws关键字

  • 在方法签名中声明该方法可能抛出的异常。
  • throws格式是跟在方法的括号后面的

定义格式:

public void 方法() throws 异常类名1,异常类名2 {
}

代码示例

public class Demo01_Throws {public static void main(String[] args) throws ParseException {System.out.println("开始");// method();method2();System.out.println("结束");}// 运行时异常public static void method() throws ArrayIndexOutOfBoundsException{int[] arr = {1, 2, 3};System.out.println(arr[3]);}// 编译时异常public static void method2() throws ParseException {String s = "2048-08-09";SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");Date d = sdf.parse(s);System.out.println(d);}
}

throw关键字

  • 使用throw关键字可以抛出一个异常对象,后面的代码不再执行。
  • 格式:throw new 异常();

代码示例

public class Demo02_Throw {public static void main(String[] args) {int [] arr = {1,2,3,4,5};// int [] arr = null;printArr(arr);//就会 接收到一个异常.//我们还需要自己处理一下异常.}private static void printArr(int[] arr) {if(arr == null){//调用者知道成功打印了吗?//System.out.println("参数不能为null");throw new NullPointerException(); //当参数为null的时候//手动创建了一个异常对象,抛给了调用者,产生了一个异常}else{for (int i = 0; i < arr.length; i++) {System.out.println(arr[i]);}}}
}

try-catch语句

  • 用于捕获异常并提供恢复措施。

执行流程

  • 程序从 try 里面的代码开始执行
  • 出现异常,就会跳转到对应的 catch 里面去执行
  • 执行完毕之后,程序还可以继续往下执行
try {// 可能抛出异常的代码
} catch (ExceptionType name) {// 异常处理代码
}

try-catch-finally语句

  • 无论是否发生异常,都会执行的代码块。
try {// 可能抛出异常的代码
} catch (ExceptionType name) {// 异常处理代码
} finally {// 总会执行的代码
}

代码示例

public class Demo03_Try {public static void main(String[] args) {System.out.println("开始");method();System.out.println("结束");}public static void method() {try {int[] arr = {1, 2, 3};System.out.println(arr[3]);System.out.println("这里能够访问到吗");} catch (ArrayIndexOutOfBoundsException e) {System.out.println("你访问的数组索引不存在,请回去修改为正确的索引");}finally {System.out.println("一直会执行,一般用来释放资源...");}}
}

try-with-resources语句

try-with-resources语句是一种用于自动管理和关闭资源的异常处理机制,它从Java 7开始引入,旨在简化资源清理工作,确保即使在发生异常的情况下,资源也能被正确地关闭。这种语句适用于那些实现了java.lang.AutoCloseable接口的对象,如文件流、套接字、数据库连接等,这些对象在使用完毕后需要显式关闭以释放系统资源。

基本语法

try (ResourceType resource1 = initializer1;ResourceType resource2 = initializer2;// ... 其他资源声明与初始化
) {// 在此处使用资源进行操作
}
catch (ExceptionType1 ex1) {// 处理与resource1或resource2等相关的异常
}
catch (ExceptionType2 ex2) {// 处理其他特定类型的异常
}
finally {// 可选的finally块,用于执行额外的清理工作(非资源关闭)
}

特点与优势

  • 自动关闭:当try块结束时,不论是因为正常执行到结束还是因为抛出并捕获了异常,Java都会自动调用资源对象的close()方法来关闭资源。这避免了手动编写finally块来确保资源关闭,提高了代码的简洁性和可靠性。
  • 多资源支持try语句内的资源声明可以有多个,用分号隔开。所有资源按照声明顺序逆序关闭,即后声明的资源先关闭。这样即使在关闭一个资源时抛出异常,后续资源仍有机会被正确关闭。
  • 异常处理:如果有异常在try块内抛出,catch子句可以捕获并处理这些异常。如果在关闭资源过程中也抛出了异常,那么这个关闭异常会被抑制(suppressed)并添加到已存在的异常中(可通过Throwable.getSuppressed()访问)。如果try块内没有异常,但关闭资源时抛出异常,则这个关闭异常会作为try-with-resources语句的结果抛出。

代码示例

使用java.util.Scanner读取用户从控制台输入的一行文本:

try (Scanner scanner = new Scanner(System.in)) {System.out.println("请输入文本: ");String inputLine = scanner.nextLine();System.out.println("你输入的文本: " + inputLine);
} catch (Exception e) {System.err.println("读取录入内容出错:");e.printStackTrace();
}

自定义异常

开发者可以创建自定义异常类,通常是通过继承Exception类或其子类,用来表示程序中特有的、标准异常类无法精确描述的异常情况。

创建自定义异常类的步骤:

  1. 继承异常基类:自定义异常类通常继承自java.lang.Exception或其子类。如果希望自定义的是运行时异常(无需强制捕获),可以继承自java.lang.RuntimeException。如果希望自定义的是编译时异常(需要强制捕获),则直接继承自java.lang.Exception
  2. 添加构造方法:自定义异常类通常至少包含一个构造方法,用于初始化异常对象。构造方法通常接受一个字符串参数,用于存储详细的异常信息(如错误描述)。还可以添加其他构造方法,如接受多个参数或无参构造方法。
  3. 可选地,添加属性和方法:根据需要,自定义异常类可以添加特定的属性(如错误代码、错误详情等)和方法,以便提供更多关于异常的上下文信息。
代码示例

创建一个简单的自定义异常类示例:

public class InvalidInputException extends Exception {public InvalidInputException(String message) {super(message);}public InvalidInputException(String message, Throwable cause) {super(message, cause);}
}

解析

  • InvalidInputException继承自java.lang.Exception,意味着它是一个编译时异常,需要在代码中显式捕获或声明抛出。
  • 定义了两个构造方法:
    • 第一个构造方法接收一个字符串参数message,用来描述异常的具体信息。它调用父类Exception的构造方法,将传入的message传递给父类,以便在异常堆栈信息中显示。
    • 第二个构造方法接收两个参数:messagecausemessage同上,cause是一个Throwable对象,用于表示引发此异常的底层原因。这个构造方法同样调用父类的相应构造方法,将messagecause传递给父类。

使用自定义异常类的示例:

假设有一个方法calculateAverage(),它接收一个整数数组并计算平均值。如果数组为空,我们希望抛出InvalidInputException

public class Demo05_ExcetionTest {public static void main(String[] args) throws InvalidInputException {int[] num = {99,65,78,63,45,15,94,64};double ca = calculateAverage(num);System.out.println(ca);//输出: 65.375}/* 获取数组的平均值*/public static double calculateAverage(int[] numbers) throws InvalidInputException {if (numbers == null || numbers.length == 0) {throw new InvalidInputException("Input array is empty or null.");}int sum = 0;for (int number : numbers) {sum += number;}return (double) sum / numbers.length;}
}

解析

  • 方法calculateAverage()声明抛出InvalidInputException,表明它可能在执行过程中抛出这个自定义异常。
  • 如果传入的数组为空或为null,方法会使用InvalidInputException的构造方法创建一个新的异常对象,并通过throw关键字抛出该异常。

异常处理的最佳实践

  • 捕获具体的异常:避免捕获ThrowableException类,这会隐藏错误和程序中的问题。
  • 提供有用的异常信息:在自定义异常中提供有用的错误信息。
  • 避免在finally中抛出异常finally块中抛出的异常会覆盖之前捕获的异常,导致调试困难。
  • 使用受检异常表示非程序逻辑错误:如文件不存在、用户名无效等。
  • 使用非受检异常表示程序逻辑错误:如空指针访问、数组越界等。

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

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

相关文章

python爬虫--------requests案列(二十七天)

兄弟姐们&#xff0c;大家好哇&#xff01;我是喔的嘛呀。今天我们一起来学习requests案列。 一、requests____cookie登录古诗文网 1、首先想要模拟登录&#xff0c;就必须要获取登录表单数据 登录完之后点f12&#xff0c;然后点击network&#xff0c;最上面那个就是登录接口…

Spring AI Summary

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl Spring AI is a project that aims to streamline the development of AI applications by providing abstractions and reusable components that can be easily integrate…

【软考---系统架构设计师】软件架构

目录 1 一、软件架构的概念 二、软件架构风格 &#xff08;1&#xff09;数据流风格​​​​​​​ &#xff08;2&#xff09;调用/返回风格 &#xff08;3&#xff09;独立构件风格 &#xff08;4&#xff09;虚拟机风格 &#xff08;5&#xff09;仓库风格 三、架构…

labview中循环停止事件的深入研究

1.错误用法 第一次值事件运行的时候空白按钮给的F值&#xff0c;第二次值事件运行的时候空白按钮给的T值&#xff0c;这时循环才真正结束。 2.正确用法之一 赋值和值改变事件从同时进行变成按顺序执行。 3.正确用法之二 值事件发生以后超时事件将T值赋值给结束条件&#xff…

Linux环境变量深度解析

文章目录 一、引言二、环境变量的基本概念1、环境变量的定义2、环境变量的作用与意义 三、环境变量的导入1、导入所需文件2、登陆时的导入 四、环境变量的设置方法1、查看环境变量的方式2、使用export命令临时设置环境变量3、修改配置文件以永久设置环境变量 五、命令行参数与环…

免费听音乐,下载音乐mp3,mp4,歌词的网站分享(2024-04-22)

亲测&#xff01;&#xff01;&#xff01; 1、音乐客 免费听和免费下载 经典老歌 - 音乐客音乐客,yinyueke.net,免费音乐,免费在线音乐播放器,免费下载音乐,音乐&#xff0c;播放器&#xff0c;下载&#xff0c;播放&#xff0c;DJ&#xff0c;免费,mp3,高音质&#xff0c;…

LinkedList和链表

1.ArrayList的缺陷 ArraryList由于底层是一段连续的空间&#xff0c;所以在ArrayList任意位置插入或者删除元素时&#xff0c;就 需要将后续元素往前或者往后搬移&#xff0c;时间复杂度为O(n)&#xff0c;效率比较低&#xff0c;因此ArrayList不适合做任意位置插入和删除比较…

pytest教程-27-分布式执行用例插件-pytest-xdist

上一小节我们学习了pytest随机执行用例插件-pytest-random-order&#xff0c;本小节我们讲解一下pytest分布式执行用例插件pytest-xdist。 前言 平常我们手工测试用例非常多时&#xff0c;比如有1千条用例&#xff0c;假设每个用例执行需要1分钟。如果一个测试人员执行需要10…

C++的初步知识——命名空间,缺省参数,重载函数

C 首先写一段代码&#xff1a; #include <stdio.h>int main() {printf("Hello world\n");return 0; }这段C语言代码在cpp文件中仍可运行。我们了解C是兼容C语言的&#xff0c;C的关键字中就包含了C语言的关键字和自身的关键字。关于关键字&#xff0c;我们简…

PTA L2-052 吉利矩阵

题目 解析 这题考的是搜索剪枝 可行性剪枝&#xff1a; 即判断当前行&#xff08;列&#xff09;是否已经超过L和剩下的格子都填最大值是否小于L&#xff0c;若是则剪枝。 当前行数大于1时&#xff0c;判断上一个填完的行是否等于L&#xff0c;若否&#xff0c;则剪枝。 当前行…

浏览器数据找回

网站上分享的文章应该都是个人的心血&#xff0c;对于一些操作问题导致心血丢失真的很奔溃&#xff0c;终于找到一个弥补的办法&#xff0c;csdn的文章谷歌浏览器亲测有效&#xff0c;理论上其他浏览器的其他网站应该也可以&#xff0c;适用以下场景 把博客编辑当成了编写新博…

MATLAB中gurobi 运行报错与调试

问题背景如下&#xff1a;刚拿到一份MATLAB的代码&#xff0c;但是电脑第一次安装gurobi&#xff0c;在运行过程中发生了报错&#xff0c;使用断点进行调试和步进调试方法&#xff0c;最终发现&#xff0c;这个问题出在了哪一步&#xff0c;然后向了人工智能和CSDN、百度寻求答…

ELK 日志分析(二)

一、ELK Kibana 部署 1.1 安装Kibana软件包 #上传软件包 kibana-5.5.1-x86_64.rpm 到/opt目录 cd /opt rpm -ivh kibana-5.5.1-x86_64.rpm 1.2 设置 Kibana 的主配置文件 vim /etc/kibana/kibana.yml --2--取消注释&#xff0c;Kiabana 服务的默认监听端口为5601 server.po…

李宏毅2022机器学习/深度学习 个人笔记(2)

本系列用于推导、记录该系列视频中本人不熟悉、或认为有价值的知识点 本篇记录第一讲&#xff08;选修&#xff09;&#xff1a;神奇宝贝分类&#xff08;续&#xff09; 讲解如何用高斯概率分布假设来推导类似于逻辑斯蒂分布的表达式 如图&#xff0c;boundary变为直线&…

JavaSE——常用API进阶二(7/8)-DateTimeFormatter、Period、Duration(常见方法、用法示例)

目录 DateTimeFormatter 主要方法 用法示例 Period 常见方法 用法示例 Duration 常见方法 用法示例 接下来继续要学习的是JDK 8之后新增的代替SimpleDateFormat的一个API——DateTimeFormatter 同样是用来格式化和解析时间的&#xff0c;与SimpleDateFormat相比较来说…

kettle从入门到精通 第五十三课 ETL之kettle MQTT/RabbitMQ consumer实战

1、上一节课我们学习了MQTT producer 生产者步骤&#xff0c;MQTT consumer消费者步骤。该步骤可以从支持MRQTT协议的中间件获取数据&#xff0c;该步骤和kafka consumer 一样可以处理实时数据交互&#xff0c;如下图所示&#xff1a; 2、双击步骤打开MQTT consumer 配置窗口&a…

ROS下机器人系统仿真及部分SLAM建图

文章目录 一、 Launch文件使用二、 参考资料三、 遇到的问题四、 效果演示五、相关代码5.1 一些简介5.2 机器人模型5.2.1 机器人底盘5.2.2 摄像头5.2.3 雷达 5.3 惯性矩阵 六、代码传送门实验结果及分析 温馨提示&#xff1a;如果有幸看到这个文章&#xff0c;不要看里面的内容…

java-单列集合List详解

一、List概述 ​​​​​​​List 接口继承自 Collection 接口。这意味着所有 List 类型的对象都是 Collection 类型的对象&#xff0c;它们共享 Collection 接口中定义的所有方法。 List集合的特点&#xff1a; 1、有序&#xff1a;存和取得元素顺序一致 2、有索引&#xf…

使用FPGA实现比较器

介绍 比较器就是通过比较输入的大小&#xff0c;然后输出给出判断。 在这个比较器中&#xff0c;有两个输入&#xff0c;三个输出。根据输出就可以判断出哪个输入值大了。 设计文件 library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; entity compa…

【大语言模型LLM】-使用大语言模型搭建点餐机器人

关于作者 行业&#xff1a;人工智能训练师/LLM 学者/LLM微调乙方PM发展&#xff1a;大模型微调/增强检索RAG分享国内大模型前沿动态&#xff0c;共同成长&#xff0c;欢迎关注交流… 大语言模型LLM基础-系列文章 【大语言模型LLM】-大语言模型如何编写Prompt?【大语言模型LL…