Java — 异常
一、异常简介
就是程序出现了不正常的情况。
异常结构:
- Throwable: 所有错误和异常的超类。
- Error: 严重问题,不需要处理。
- Exception: 异常类,表示程序本身可以处理的问题。
- Checked Exception: 编译期不检查,出现问题后,需要回来修改代码。
- Unchecked Exception: 编译期必须处理,否则程序不能通过编译、不能运行。
二、异常体系
2.1、Throwable 类
Throwable 类位于 java.lang 包下,它是 Java 语言中所有错误(Error)和异常(Exception)的父类。
Throwable 类包含了其线程创建时线程执行堆栈的快照,它提供了 printStackTrace() 等接口用于获取堆栈跟踪数据等信息。
成员方法:
方法名 | 说明 |
---|---|
public Throwable fillInStackTrace() | 填写执行堆栈跟踪 |
public StackTraceElement[] getStackTrace() | 提供对 printStackTrace() 打印的堆栈跟踪信息的编程访问 |
public Throwable getCause() | 如果原因不存在或未知,则返回此 throwable 的原因或 null |
public String toString() | 返回此可抛出的简短描述 |
public String getMessage() | 返回此 throwable 的详细消息字符串 |
public void printStackTrace() | 把 throwable 对象封装的异常错误信息输出在控制台 |
2.2、Error 类
Error 是 Throwable 的一个直接子类,它指示合理的应用程序不应该尝试捕获的严重问题。
这些错误在应用程序的控制和处理能力之外,编译器不会检查 Error。
对于设计合理的应用程序来说,即使发生了错误,本质上也无法通过异常处理来解决其所引起的异常状况。
常见 Error :
- OutOfMemoryError:内存溢出错误;
- UnsupportedClassVersionError:Java 类版本错误;
- ...
2.3、Exception 类
Exception 是 Throwable 的一个直接子类,它指示合理的应用程序可能希望捕获的条件。
Exception 又包括 Checked Exception(检查异常)和 Unchecked Exception(非检查异常)两大类别。
2.3.1、Checked Exception
Checked Exception 是编译器要求必须处理的异常,除了 RuntimeException 以及它的子类,都是 Checked Exception 异常。
我们程序编写时就必须处理此类异常,否则程序无法编译通过。
常见检查异常:
- IOException:IO 异常;
- SQLException:SQL 异常;
- ...
2.3.2、Unchecked Exception
Unchecked Exception 是编译器不要求强制处理的异常,包含 RuntimeException 以及它的相关子类。
我们编写代码时即使不去处理此类异常,程序还是会编译通过。
常见非检查异常:
- NullPointerException:空指针异常;
- ArrayIndexOutOfBoundsException:数组下标越界异常;
- ...
三、JVM 默认处理方案
若程序出现问题,我们没有做任何处理,最终 JVM 会做默认的处理。
-
把异常的名称,异常原因及异常出现的位置等信息输出在控制台。
-
程序停止执行。
示例:数组下标越界。
// 当数组下标越界,程序在编译阶段不会发生错误,但在运行时会抛出异常 public class Test_01 { public static void main(String[] args) { System.out.println("程序开始"); int[] arr = {1, 2, 3}; System.out.println("arr = " + arr[3]); // 程序在这里发生异常,提前结束运行 System.out.println("程序结束"); // 这行代码没有执行 } }
运行:
程序开始 Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3 at exception.ExceptionDemo01.main(ExceptionDemo01.java:17)
异常原因:由于访问了数组中不存在的元素,所以代码停止执行并打印了相关的异常信息,此信息为堆栈跟踪。
异常信息:
Exception in thread "main"
(异常位于主线程 main 中)java.lang.ArrayIndexOutOfBoundsException
(异常类型)3
(异常详细信息)at exception.ExceptionDemo01.main(Test_01.java:17)
(异常发生位置)
四、异常处理
若程序出现问题,我们需要自己来处理,在 Java 语言中,异常处理机制分为两部分。
- 抛出异常(throws)
- 当一个方法发生错误时,会创建一个异常对象,并交给运行时系统处理。
- 当前方法不处理,而是声明抛出,由该方法的调用者来处理。
- 捕获异常(try … catch …)
- 在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器。
- 在当前方法中使用 try-catch 的语句块来处理异常。
Java 通过 5 个关键字来实现异常处理,分别是:throw
、throws
、try
、catch
、finally
,异常总是先抛出后捕获的。
五、捕获异常
格式:
try { // 可能产生异常的代码 } catch (异常类名 变量名) { // 捕获并处理try抛出的异常类型,记录日志|打印异常信息|继续抛出异常 } catch (异常类名N 变量名) { // 捕获并处理try抛出的异常类型,记录日志|打印异常信息|继续抛出异常 } finally { // 无论是否发生异常,都将执行的代码块 }
流程:
① 程序从 try 里面的代码开始执行。
② 出现异常,会自动生成一个异常类对象,该异常对象将被提交给 Java 运行时系统。
③ 当 Java 运行时系统接收到异常对象时,会到 catch 中去找匹配的异常类,找到后进行异常的处理。
④ 处理异常执行完毕之后,程序还可以继续往下执行。
⑤ 无论程序是否发生异常,finally 中代码块都会执行。
示例:
public class Test_02 { public static void main(String[] args) { System.out.println("程序开始"); int[] arr = {1, 2, 3}; try { System.out.println("arr = " + arr[3]); // 可能发生异常的代码 } catch (ArrayIndexOutOfBoundsException e) { e.printStackTrace(); // 处理异常(打印异常信息) } finally { System.out.println("无论是否发生异常,都将执行的代码块"); } System.out.println("程序结束"); } }
运行:
程序开始 无论是否发生异常,都将执行的代码块 程序结束 java.lang.ArrayIndexOutOfBoundsException: 3 at step_05_exception.Test_02.main(Test_02.java:8)
将第 6 行代码 arr[3] 改为 arr[2] 再运行:
程序开始 arr = 3 无论是否发生异常,都将执行的代码块 程序结束
六、抛出异常
6.1、throw
用在方法体内,跟的是异常对象名,表示抛出异常,由方法体内的语句处理。
执行 throw 一定抛出了某种异常。
示例:
public class Test_03 { public static void main(String[] args) { System.out.println("程序开始"); divide(2, 0); // 调用除法 System.out.println("程序结束"); } // 除法方法 public static void divide(int a, int b) { System.out.println("除法开始"); if (b == 0) { throw new ArithmeticException("除数不能为零"); // 抛出异常、但没有处理异常、异常发生时程序中断不再往下执行 } System.out.println(a / b); System.out.println("除法结束"); } }
运行:
程序开始 除法开始 Exception in thread "main" java.lang.ArithmeticException: 除数不能为零 at step_05_exception.Test_03.divide(Test_03.java:15) at step_05_exception.Test_03.main(Test_03.java:7)
6.2、throws
用在方法声明后面,跟的是异常类名,表示抛出异常,由该方法的调用者来处理。
表示出现异常的一种可能性,并不一定会发生这些异常。
格式:
修饰符 返回值类型 方法名 (参数列表) throws 异常类名1, 异常类名2, 异常类名3... { //方法体 }
示例:
public class Test_04 { public static void main(String[] args) { System.out.println("程序开始"); try { divide(2, 0); // 调用除法 } catch (ArithmeticException e) { e.printStackTrace(); // 处理异常(打印异常信息) } System.out.println("程序结束"); } // 抛出异常,不在方法中处理,谁调用该方法谁处理 public static void divide(int a, int b) throws ArithmeticException { System.out.println("除法开始"); System.out.println(a / b); System.out.println("除法结束"); } }
运行:
程序开始 除法开始 程序结束 java.lang.ArithmeticException: / by zero at step_05_exception.Test_04.divide(Test_04.java:19) at step_05_exception.Test_04.main(Test_04.java:8)
七、自定义异常
声明一个异常类,要么继承 Exception,要么继承 RuntimeException,通常情况下,会直接继承自 Exception 类,给出无参和有参构造方法即可。
示例:
public class Test_05 { public static void main(String[] args) { System.out.println("程序开始"); checkAge(200); // 输入0~120范围之外的年龄 System.out.println("程序结束"); } // 检查年龄方法 static void checkAge(int age) { System.out.println("开始检查"); if (age < 0 || age > 120) { throw new ageException("年龄应在0~120之间"); } else { System.out.println("年龄合法:" + age + "岁"); } System.out.println("结束检查"); } // 自定义年龄异常,继承运行时异常 static class ageException extends RuntimeException { public ageException() { } public ageException(String message) { super(message); } } }
运行:
程序开始 开始检查 Exception in thread "main" step_05_exception.Test_05$ageException: 年龄应在0~120之间 at step_05_exception.Test_05.checkAge(Test_05.java:14) at step_05_exception.Test_05.main(Test_05.java:6)
第 6 行代码 checkAge() 方法中输入0~120范围之内的年龄再运行:
程序开始 开始检查 年龄合法:18岁 结束检查 程序结束
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~