【JAVASE | 第十一篇】Java异常处理全攻略
目录
继承自 RuntimeException 的异常默认是非受查异常
前言
今天我们来学习异常的相关内容
引入:
Java异常分两种情况——编译异常与运行异常。
运行异常(Runtime Exception)
乐锅一个朋友没有带钱去KFC,他可以坐在那里吹空调,但不能点餐。因为他没有带钱,点餐会扣他的钱包里面的钱,而他没得扣,如果强行点餐,会报错误。
编译异常(Checked Exception)
乐锅一个朋友前些天在下载某些软件时候。系统会弹出红色警告。而他选择无视风险,继续安装。这就是运行异常。
一 、异常
1.异常是什么?(what)
异常是代码在编译或者执行的过程中可能出现的错误。
2.异常的代表是谁?分为几类?
Exception,分为两类:编译时异常、运行时异常。
编译时异常:没有继承RuntimeExcpetion的异常,编译阶段就会出错。
运行时异常:继承自RuntimeException的异常或其子类,编译阶段不报错,运行时出现的。
3.异常的作用是啥?
用来查找bug;可以作为方法内部的特殊返回值,通知上层调用者底层的执行情况。
二 、运行异常
1.数组越界异常
当访问数组中不存在的索引时触发该异常。
代码示例:
public class Example {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
// 尝试访问索引为3的元素(数组最大索引为2)
int num = arr[3];
System.out.println(num); // 此处会抛出异常,程序终止
}
}
报错(异常):

2.空指针异常
当调用一个为null的对象的方法或属性时触发该异常。
代码示例:
public class Example2 {
public static void main(String[] args) {
String str = null;
// 尝试调用null对象的length()方法
int len = str.length();
System.out.println(len); // 抛出NullPointerException
}
}
报错(异常):

3.算术异常
场景描述:数学运算错误,例如除以零。
代码示例:
public class ArithmeticExceptionExample {
public static void main(String[] args) {
int a = 10;
int b = 0;
// 尝试除以零
int result = a / b;
System.out.println(result); // 抛出ArithmeticException
}
}
报错(异常):

4.类型转换异常
场景描述:尝试将对象强制转换为不兼容的类型时触发。
代码示例:
public class ClassCastExceptionExample {
public static void main(String[] args) {
Object obj = "Hello"; // 字符串对象
// 强制转换为Integer类型
Integer num = (Integer) obj; // 抛出ClassCastException
}
}
报错(异常):
三 、编译异常
写完代码后会红色报错,且运行不了
四 、异常处理
这个时候我们鼠标放在红色代码上按alt键,就会出现两种异常处理
分别是try catch 和添加异常到方法的throws(抛出)

1.throws抛出
在方法增加throws Exception
方法一:throws 抛出
public static void main(String[] args) throws InterruptedException {
// Thread.sleep 抛出的是 InterruptedException(受检异常)
Thread.sleep(1000);
System.out.println("Done");
}
}
//我们可以把抛出Exception前面的类型删掉,这样可以让我们面对多个异常的时候只用抛出一次
public static void main(String[] args) throws Exception {
// Thread.sleep 抛出的是 InterruptedException(受检异常)
Thread.sleep(1000);
System.out.println("Done");
}
}
2.try -catch
try {
// 可能抛出异常的代码
} catch (Exception e) {
e.printStackTrace();// 打印异常信息
}
public static void main(String[] args) {
// Thread.sleep 抛出的是 InterruptedException(受检异常)
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("Done");
}
}
所以我们可以改成这样:
把Exception前面内容删掉,然后调用e.printStackTrace方法,如果不通过则打印异常信息
public static void main(String[] args) {
// Thread.sleep 抛出的是 InterruptedException(受检异常)
try {
Thread.sleep(1000);
System.out.println("Done");
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.throw关键字抛出
public static void main(String[] args) {
saveAge(300);
System.out.println("程序结束。。。。");
}
// 需求:我们公司的系统只要收到了年龄小于1岁或者大于200岁就是一个年龄非法异常。
public static void saveAge(int age) {
if(age < 1 || age > 200){
// 年龄非法;抛出去一个异常。
//在这里自己手动抛出异常,而不是jvm的异常
throw new myselfException2("年龄非法 age 不能低于1岁不能高于200岁");
}else {
System.out.println("保存年龄:" + age);
}
}
}
4.try-catch-fianlly
finally:无论如何都会最后执行
不过我们常用于io流是释放资源,比如关闭文件、释放数据库连接、关闭网络连接等。确保这些资源在使用后能够被正确释放,以避免资源泄漏。
同时我们也会遇到没跑完就关闭的可能性,这里我们后面会讲到
public static void main(String[] args) {
System.out.println("Start");
try {
System.out.println("In try");
int r = 10 / 0; // 会抛出 ArithmeticException(运行时异常)
System.out.println("After error"); // 不会执行
} catch (Exception e) {
System.out.println("In catch: " + e.getClass().getSimpleName() + " - " + e.getMessage());
} finally {
System.out.println("In finally: cleanup here");
}
System.out.println("End");
}

四、自定义异常
自定义异常通常会继承自 Exception 或者 RuntimeException
继承自 Exception 的异常默认是受查异常
- 编译器强制要求:调用者必须通过
try-catch捕获或在方法签名中使用throws声明,否则编译失败- 设计目的:表示可预见、可恢复的外部问题,调用者有能力或责任采取补救措施
继承自 RuntimeException 的异常默认是非受查异常
- 编译器不强制要求:调用者可选择处理或忽略,编译器不会报错
- 设计目的:表示程序逻辑错误或不可恢复的问题,调用者无法合理处理
继承Exception:
//继承Exception
public class myselfException extends Exception{
public myselfException(String message) {
System.out.println(message);super(message);
}
public myselfException(){
}
}
public static void main(String[] args) {
try {
show(200);
} catch (myselfException e) {
e.printStackTrace();
}
}
private static void show(int age) throws myselfException {
if(age<1 || age>120){
throw new myselfException("您输入的年龄有误");
}else{
System.out.println("年龄合法,保存");
}
}
继承RuntimeEception:
public class myselfException2 extends RuntimeException{
public myselfException2(String message) {
System.out.println(message);super(message);
}
public myselfException2(){
}}
public static void main(String[] args) {
saveAge(300);
System.out.println("程序结束。。。。");
}
// 需求:我们公司的系统只要收到了年龄小于1岁或者大于200岁就是一个年龄非法异常。
public static void saveAge(int age) {
if(age < 1 || age > 200){
// 年龄非法;抛出去一个异常。
throw new myselfException2("年龄非法 age 不能低于1岁不能高于200岁");
}else {
System.out.println("保存年龄:" + age);
}
}
}
异常处理关键字横向对比表
| 对比维度 | try-catch | try-catch-finally | throws | throw |
|---|---|---|---|---|
| 核心用途 | 捕获并处理异常 | 捕获异常 + 确保资源清理 | 声明方法可能抛出的异常 | 主动抛出异常对象 |
| 语法位置 | 方法内部代码块 | 方法内部代码块 | 方法签名(参数列表后) | 方法内部任意位置 |
| 强制性 | 编译异常必须处理 | 非强制,但推荐用于资源管理 | 编译异常必须声明或处理 | 非强制,按需使用 |
| 执行时机 | 运行时 | 运行时 | 编译时检查 | 运行时 |
| 典型场景 | 处理可恢复的业务异常 (如:文件不存在时使用默认配置) |
资源操作必须清理 (如:文件IO、数据库连接、网络请求) |
将异常处理责任转移给调用者 (如:底层DAO方法抛出SQLException) |
主动触发异常流程 (如:参数校验失败、业务规则违反) |
| 返回值处理 | 可在catch中返回默认值或重试结果 | finally中的return会覆盖try/catch的return | 不影响方法返回值 | 抛出后方法立即终止,无返回值 |
| 资源管理 | 需手动在catch中清理资源 | 自动确保 finally 块执行,适合资源清理 | 不涉及资源管理 | 需配合try-finally确保资源释放 |
| 异常传播 | 异常被捕获,不向上传播 | 异常被捕获,不向上传播(除非finally中抛出新异常) | 异常向上传播给调用者 | 异常向上传播给调用者 |
| 代码 | try{}catch{} | try{}catch{}finally{} | throws Exceptoion | throw |
| 优势 | 精准处理特定异常 提供恢复机制 |
100%确保资源释放 代码健壮性强 |
清晰声明方法契约 调用者明确知道风险 |
主动控制异常流程 提前暴露问题 |
| 劣势 | 可能过度处理异常 代码嵌套过深 |
语法相对繁琐 finally中return会覆盖结果 |
调用者必须处理 可能污染调用链 |
滥用会导致流程混乱 性能开销较大 |
| 设计哲学 | 防御式编程 "我能处理这个问题" |
负责任的清理 "无论如何都要收拾干净" |
契约式设计 "这可能会出问题,你得知道" |
主动防御 "这里有问题,立刻停止" |
| 前言场景映射 | "可以但有警告"的应对方案 (点击继续后处理磁盘不足) |
"可以但有警告"的安全保障 (即使失败也释放内存) |
"不可但无警告"的责任转移 (灰色按钮的责任说明) |
"不可但无警告"的主动拒绝 (直接阻止非法操作) |
总结
今天我们学习了异常的相关知识。如何处理异常是Java的一个重点知识。
本次分享到此结束,感谢大家的观看与支持!如果本内容对您有帮助,欢迎点赞、收藏,您的支持将是我持续创作的最大动力,谢谢!
更多推荐



所有评论(0)