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,一经查实,立即删除!

相关文章

【架构】负载均衡SLB浅谈

SLB负载均衡架构培训文档 1. 引言 作为一名架构师&#xff0c;理解并掌握SLB&#xff08;Server Load Balancer&#xff09;负载均衡架构是非常重要的。本培训文档旨在为您提供关于SLB负载均衡架构的详细知识和指导&#xff0c;帮助您更好地设计和优化企业级应用。 2. SLB负…

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…

Java面试题:描述Java中的链式调用是如何实现的?

链式调用&#xff08;Chain of Responsibility&#xff09;是一种行为型设计模式&#xff0c;它允许你通过创建一个对象的序列来处理请求&#xff0c;每个对象处理请求后&#xff0c;将请求传递给下一个对象&#xff0c;直到请求被完整处理。这种模式通常用于简化对象之间的连接…

Python学习之旅高级篇一:Web开发之旅

在Python学习之旅高级篇中&#xff0c;我们将探索如何使用Python进行Web开发。Flask和Django是两个非常流行的Python Web框架&#xff0c;它们可以帮助你快速构建Web应用程序。此外&#xff0c;了解如何设计和实现RESTful API也是Web开发中的一个重要技能。接下来的文章里&…

【java、微服务】MQ

同步通讯 优点 时效性较强&#xff0c;可以立即得到结果 问题 微服务间基于Feign的调用就属于同步方式&#xff0c;存在一些问题。 耦合度高。每次加入新的需求&#xff0c;都要修改原来的代码资源浪费。调用链中的每个服务在等待响应过程中&#xff0c;不能释放请求占用的…

【华科网安复试】网络安全——技术与实践 刘建伟版 重点和笔记

网络安全——技术与实践 刘建伟版 重点和笔记 华中科技大学网络安全学院考研复试笔试部分&#xff1a;《网络安全——技术与实践》重点和笔记&#xff0c;全是图和表格总结的重点&#xff0c;方便复习 文章目录 网络安全——技术与实践 刘建伟版 重点和笔记一、第一章&#xff…

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

目录 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 tar解压缩命令

列出压缩文件内容 tar -tzvf test.tar.gz 解压文件 tar -xzvf test.tar.gz 压缩文件夹或文件&#xff1a; #压缩categraf目录下的所有文件&#xff0c;对应解压后的目录结构跟目录还是categraf/ tar -czvf categraf.tar categraf/ tar -czvf categraffile.tar categraffile.co…

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;…

添加修改ubuntu中环境变量(PATH)

1.打开.bashrc文件进行设置&#xff0c;终端执行以下命令&#xff1a; sudo gedit ~/.bashrc2.在末尾行添加&#xff1a; export PATH$PATH:/xxx/xxx 其中&#xff0c;$PATH代表现存的环境变量&#xff0c;不能省去&#xff0c;等号两边一定不能有空格&#xff0c;/xxx/xxx要…

vLLM:由伯克利大学LMSYS组织开源的大语言模型高速推理框架-显著提高了大型语言模型(LLM)的服务效率

vLLM是一个由伯克利大学LMSYS组织开源的大语言模型高速推理框架,旨在提升实时场景下语言模型服务的吞吐与内存使用效率134。它是一个快速且易于使用的库,能够与HuggingFace无缝集成134。vLLM的核心是PagedAttention算法,这是一种新颖的注意力算法,通过引入操作系统的虚拟内…

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;我们简…

【Ansible】03

【Ansible】02 ansible 变量 facts 变量 facts 翻译为 事实 facts 变量是 ansible 自带的预定以变量 , 用于描述被控端软硬件信息 facts 变量通过 setup 模块获得 ansible webservers -m setup facts 变量是一个大的由 { } 构成的 键值对字典 在 { }中存在多层级的嵌套 …

C 练习实例25

C 练习实例25 题目&#xff1a; 求12!3!...20!的和。 程序分析&#xff1a; 此程序只是把累加变成了累乘。 实例 #include <stdio.h>int main() {int i;long double sum,mix;sum0,mix1;for(i1;i<20;i){mixmix*i;sumsummix;} printf("%Lf\n",sum); }以…

PTA L2-052 吉利矩阵

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