Java异常
本文包含异常概念、体系分类、处理方式、自定义异常四大部分。
一、什么是异常
异常就是Java程序在编译或运行过程中出现的错误,会导致程序非正常终止。
开发中最常见的异常场景:
- 数组索引越界:访问了数组不存在的下标
- 算术异常:除数为0
- 空指针异常:调用
null对象的方法 - 文件不存在:读取本地不存在的文件
// 常见运行时异常示例
public class ExceptionTest {
public static void main(String[] args) {
int[] arr = {10, 20, 30};
System.out.println(arr[3]); // 数组索引越界异常
System.out.println(10 / 0); // 算术异常
String str = null;
System.out.println(str.length()); // 空指针异常
}
}异常的核心作用
- 定位BUG:异常信息会精准打印错误位置和原因,是调试程序的关键
- 流程通知:作为方法的特殊返回值,告诉调用者方法执行失败的原因
二、Java异常完整体系
Java中所有异常和错误的根类是 java.lang.Throwable,整体分为两大分支:
1. Error(系统级错误)
属于JVM底层的严重问题(如内存溢出、系统崩溃),也无法通过代码处理。
2. Exception(程序异常,开发核心处理对象)
程序运行/编译中可预见的错误,我们所有异常处理都围绕这个类展开,又细分为运行时异常和编译时异常。
三、异常的两大分类
1. 运行时异常(RuntimeException)
- 定义:
RuntimeException及其所有子类 - 特点:编译阶段不报错,运行时才触发
- 常见示例:数组越界、空指针、算术异常、类型转换异常
2. 编译时异常(受检异常)
- 定义:非
RuntimeException的Exception子类 - 特点:编译阶段强制报错,必须处理才能运行
- 常见示例:日期解析异常、文件读取异常、IO流异常
// 编译时异常示例(必须处理,否则编译失败)
import java.text.SimpleDateFormat;
import java.util.Date;
public class CompileException {
public static void main(String[] args) throws Exception {
String time = "2024-07-09";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
Date date = sdf.parse(time); // 日期解析异常(编译时异常)
}
}四、异常的两种核心处理方式
Java提供了抛出异常和捕获异常两种处理方案,开发中可根据场景灵活使用。
1. 抛出异常(throws)
- 作用:方法内部不处理异常,抛给调用者处理
- 语法:在方法声明后添加
throws 异常类名 - 简化写法:直接抛出
Exception接收所有异常
// 抛出异常示例
public class ThrowException {
public static void main(String[] args) throws Exception {
// 调用者接收异常,也可继续抛出
testException();
}
// 方法抛出异常,交给上层处理
public static void testException() throws Exception {
int num = 10 / 0;
}
}2. 捕获异常(try...catch)
- 作用:直接拦截并处理异常,保证程序不终止
语法:
try { // 可能出现异常的代码 } catch (异常类型 变量名) { // 异常处理逻辑 }- 简化写法:
catch (Exception e)捕获所有异常
// 捕获异常示例
public class TryCatchException {
public static void main(String[] args) {
try {
int num = 10 / 0;
} catch (Exception e) {
e.printStackTrace(); // 打印异常堆栈信息
System.out.println("程序出现异常,已处理!");
}
// 异常处理后,程序继续执行
System.out.println("程序正常结束");
}
}五、自定义异常
Java内置的异常类无法覆盖所有业务场景(如年龄非法、手机号格式错误),此时需要自定义异常来管理业务错误。
自定义异常的两种类型
1. 自定义编译时异常
- 继承:
Exception - 特点:编译强制处理,提醒严格
// 自定义编译时异常
public class AgeIllegalException extends Exception {
// 无参构造
public AgeIllegalException() {}
// 带异常信息的构造
public AgeIllegalException(String message) {
super(message);
}
}2. 自定义运行时异常
- 继承:
RuntimeException - 特点:编译不报错,运行触发,开发更常用
// 自定义运行时异常
public class AgeIllegalRuntimeException extends RuntimeException {
public AgeIllegalRuntimeException() {}
public AgeIllegalRuntimeException(String message) {
super(message);
}
}自定义异常使用示例
public class CustomExceptionTest {
public static void main(String[] args) {
try {
saveAge(300); // 传入非法年龄
} catch (AgeIllegalException e) {
e.printStackTrace();
System.out.println("年龄校验失败!");
}
}
// 业务方法:校验年龄并抛出自定义异常
public static void saveAge(int age) throws AgeIllegalException {
if(age < 1 || age > 200){
throw new AgeIllegalException("年龄非法,范围必须是1-200");
}
System.out.println("年龄保存成功:" + age);
}
}六、企业级异常处理实践
- 分层抛出,统一捕获
底层方法不处理异常,全部向上抛出,最外层统一捕获,打印日志并返回友好提示 - 异常重试修复
针对用户输入错误等场景,捕获异常后引导用户重新输入,保证程序不中断
// 异常重试修复示例
import java.util.Scanner;
public class ExceptionRetry {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (true) {
try {
System.out.print("请输入商品价格:");
double price = sc.nextDouble();
System.out.println("价格设置成功:" + price);
break;
} catch (Exception e) {
System.out.println("输入格式错误,请重新输入数字!");
sc.next(); // 清空错误输入
}
}
}
}七、总结
- 异常根类
Throwable,分Error(无需处理)和Exception(开发处理) - 异常分类:运行时异常(编译不报错)、编译时异常(编译强制处理)
- 处理方式:
throws抛出(上层处理)、try...catch捕获(自行处理) - 自定义异常:继承
Exception(编译时)/RuntimeException(运行时) - 开发规范:底层抛异常,上层统一捕获,避免程序崩溃