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.hqwc.cn/news/638240.html

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

相关文章

【python项目推荐】键盘监控--统计打字频率

原文&#xff1a;https://greptime.com/blogs/2024-03-19-keyboard-monitoring 代码&#xff1a;https://github.com/GreptimeTeam/demo-scene/tree/main/keyboard-monitor 项目简介 该项目实现了打字频率统计及可视化功能。 主要使用的库 pynput&#xff1a;允许您控制和监…

CAS解析和 synchronized 优化过程

目录 正文&#xff1a; 1.synchronized的优化过程 1.1锁粗化与锁细化 1.2自旋锁 1.3锁消除 1.4 偏向锁 1.5. 轻量级锁 1.6 重量级锁 2.CAS 2.1概述 2.2java中的cas操作 2.3ABA问题 总结&#xff1a; 正文&#xff1a; 1.synchronized的优化过程 synchronized 是 J…

Git | Git基本命令

Git | Git基本操作 文章目录 Git | Git基本操作一、创建Git本地仓库1、创建Git仓库2、配置Git3、理解工作区、暂存区、版本库关系 二、添加、修改与查看添加文件查看历史提交记录 修改文件查看.git文件 三、版本回退版本回退撤销修改尚未add已add但还未commit已add并commit 删除…

STM32F4以太网 (ETH)之精简介质独立接口:RMII

目录 概述 1 以太网简介 1.1 介绍 1.2 特征 2 以太网功能说明&#xff1a; RMII 3 RMII接口 3.1 接口介绍 3.2 精简介质独立接口信号 3.3 RMII 时钟源 3.4 RMII 选择 3.5 RMII内部时钟方案 4 RMII工作时序 4.1 发送序列 ​4.2 发送时序图 4.3 RMII时序参数 5 …

InFusion:通过从扩散先验学习深度完成来修复3D高斯

InFusion: Inpainting 3D Gaussians via Learning Depth Completion from Diffusion Prior InFusion&#xff1a;通过从扩散先验学习深度完成来修复3D高斯 Zhiheng Liu * 刘志恒 *1144Hao Ouyang * 欧阳浩 *2233Qiuyu Wang 王秋雨33Ka Leong Cheng 郑家亮2233Jie Xiao 街小…

美盈森携手飞讯打造SRM项目驱动供应链价值跃升

日前&#xff0c;美盈森集团股份有限公司&#xff08;以下简称&#xff1a;美盈森&#xff09;携手飞讯工业互联共同启动了以“协同创新&#xff0c;驱动供应链价值跃升”为主题的SRM项目。美盈森厂长袁训光、严光友、周振华等领导携同公司各职能部门的核心成员齐聚现场&#x…

Docker构建Golang项目常见问题

Docker构建Golang项目常见问题 1 Dockerfile1.1 dockerfile报错&#xff1a;failed to read expected number of bytes: unexpected EOF1.2 go mod tidy: go.mod file indicates go 1.21, but maximum supported version is 1.171.3 是否指定启动文件问题 2 构建及部署 1 Docke…

CSS的语法规则——基础选择器

元素&#xff1a; 用法&#xff1a; 标签名&#xff1a;{style的内容} 特点&#xff1a; 全局性&#xff0c;使用后&#xff0c;所有的相同标签都是同一种样式。 举例&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UT…

Docker - Compose

原文地址&#xff0c;使用效果更佳&#xff01; Docker - Compose | CoderMast编程桅杆Docker - Compose 在部署应用时&#xff0c;常常使用到不止一个容器&#xff0c;那么在部署容器的时候就需要一个一个进行部署&#xff0c;这样的部署过程也相对来说比较繁琐复杂&#xff…

医学图像分割入门-FCN理论与实践

FCN&#xff08;全卷积神经网络&#xff09; 引言 全卷积网络&#xff08;Fully Convolutional Network&#xff0c;简称FCN&#xff09;是一种深度学习模型&#xff0c;专门设计用于图像分割任务。相比于传统的基于全连接层的神经网络&#xff0c;FCN可以接受任意尺寸的输入…

「健身党」真无线蓝牙耳机怎么选?攻略来啦

在减脂健身的道路上,我虽然还是个新手,但在耳机选择上却已经踩了不少雷。根据自己的经验分享一些挑选真无线蓝牙耳机的小技巧,希望能为大家提供一些帮助,避免像我一样花冤枉钱。 一、耳机类型 传统入耳式耳机和头戴式耳机真的达咩,健身的时候戴这两种耳机不仅容易掉,而且舒适…

Stanford天空图像和光伏发电数据集

需要的同学私信联系&#xff0c;推荐关注上面图片右下角的订阅号平台 自取下载。 太阳能的间歇性对光伏&#xff08;PV&#xff09;与电网的大规模集成提出了挑战。基于天空图像的太阳能预报已被认为是预测短期波动的一种很有前途的方法。在这里&#xff0c;介绍一个数据集名叫…