一.异常基础概念
异常是程序执行过程中出现的打断正常流程的事件。
Java通过面向对象的方式处理异常
每个异常都是Throwable(可抛出的)类或其子类的对象
二.异常类层次结构
java.lang.Object
└── java.lang.Throwable
├── java.lang.Error (错误)
└── java.lang.Exception (异常)
├── RuntimeException (运行时异常/非受检异常)
└── 其他Exception (受检异常)
Java所有异常都继承自Throwable类
1.Error(错误)
表示严重问题,代表系统级别的错误,通常由JVM抛出
系统一旦出现问题,sun公司会把这些错误封装成Error对象。Error是sun公司自己用的,而不是给程序员用的。因此开发人员不用管它。
2.Exception(异常)
代表程序可能出现的问题
通常用Exception类及其子类来封装程序出现的问题
1.RuntimeException(运行时异常)
包括RuntimeException类及其子类
编译阶段不会触发,在运行时才会触发的异常(如数组索引越界异常、算术异常、空指针异常)
2.其他Exception(编译时异常)
直接继承于Exception类
编译阶段(通过javac命令将.java文件转换成.class文件的过程)就会出现的异常提醒,编译器强制要求处理(如日期解析异常)
编译阶段只会检查语法错误、性能优化
编译时异常是为了提醒程序员检查本地信息(如本地事件,本地的文件)
而运行时异常就是代码出错导致的异常
三.异常的处理方式
1.JVM默认的处理方式
把异常的名称、异常出现的原因、异常出现的位置等信息输出在控制台
异常后面的代码不会再被执行,程序终止
2.自己处理
1.try-catch-finally异常处理机制
java
try {
可能出现异常的代码;
} catch (异常类名 变量名) {
异常处理的代码
} finally {
try中无论是否发生异常都会处理的代码
}
java
package exceptiondemo;
public class demo1 {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
try {
System.out.println(arr[5]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界异常");
}
System.out.println("程序继续执行");
}
}
输出:
java
数组越界异常
程序继续执行
捕获多个异常
java
package exceptiondemo;
public class demo1 {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
try {
//System.out.println(arr[5]);
System.out.println(10/0);
String s = null;
System.out.println(s.equals("null"));
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界异常");
} catch (ArithmeticException e) {
System.out.println("算术异常");
} catch (NullPointerException e) {
System.out.println("空指针异常");
}
System.out.println("程序继续执行");
}
}
输出:
java
算术异常
程序继续执行
父类异常必须写在子类异常后面:
java
package exceptiondemo;
public class demo1 {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
try {
//System.out.println(arr[5]);
System.out.println(10/0);
String s = null;
System.out.println(s.equals("null"));
} catch (Exception e) {
System.out.println("异常");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界异常");
} catch (ArithmeticException e) {
System.out.println("算术异常");
} catch (NullPointerException e) {
System.out.println("空指针异常");
}
System.out.println("程序继续执行");
}
}
报错:java: 已捕获到异常错误java.lang.ArrayIndexOutOfBoundsException
且在IDEA中就有错误提示:xception 'java.lang.ArrayIndexOutOfBoundsException' has already been caught
java
package exceptiondemo;
public class demo1 {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
try {
//System.out.println(arr[5]);
System.out.println(10/0);
String s = null;
System.out.println(s.equals("null"));
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界异常");
} catch (ArithmeticException e) {
System.out.println("算术异常");
} catch (NullPointerException e) {
System.out.println("空指针异常");
} catch (Exception e) {
System.out.println("其他异常");
}
System.out.println("程序继续执行");
}
}
输出:
算术异常
程序继续执行
如果try中的异常没有被catch所捕获到,则JVM会接手来进行默认处理:
java
package exceptiondemo;
public class demo1 {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
try {
//System.out.println(arr[5]);
System.out.println(10/0);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界异常");
}
System.out.println("程序继续执行");
}
}
输出:
java
Exception in thread "main" java.lang.ArithmeticException: / by zero
at exceptiondemo.demo1.main(demo1.java:8)
try代码块中遇到了异常会直接跳转到catch代码块去匹配,不会执行try中异常后的代码:
java
package exceptiondemo;
public class demo1 {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
try {
System.out.println(arr[5]);
System.out.println("用来测试try中的代码发生异常,异常处后面的代码是否还会执行");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界异常");
}
System.out.println("程序继续执行");
}
}
输出:
数组越界异常
程序继续执行
java
package exceptiondemo;
public class demo1 {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
try {
//System.out.println(arr[5]);
System.out.println(10/0);
System.out.println("用来测试try中的代码发生异常,异常处后面的代码是否还会执行");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界异常");
}
System.out.println("程序继续执行");
}
}
输出:
java
Exception in thread "main" java.lang.ArithmeticException: / by zero
at exceptiondemo.demo1.main(demo1.java:8)
2.异常的几个重要方法
|-------------------------------|-----------------------------------------------------|
| public String getMessage() | 返回此Throwable的简短字符串 |
| public String toString() | 返回此Throwable的详细信息字符串表示 |
| public void printStackTrace() | 把异常的错误信息输出在控制台,在IDEA中以红色字体进行打印,与JVM默认处理方式不同,它不会终止程序 |
java
package exceptiondemo;
public class demo1 {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
try {
System.out.println(arr[5]);
} catch (ArrayIndexOutOfBoundsException e) {
String message = e.getMessage();
System.out.println(message);
}
System.out.println("程序继续执行");
}
}
输出:
java
Index 5 out of bounds for length 5
程序继续执行
java
package exceptiondemo;
public class demo1 {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
try {
System.out.println(arr[5]);
} catch (ArrayIndexOutOfBoundsException e) {
String str = e.toString(); // 写e.toString() 然后快捷键ctrl alt v 可以自动补齐变量初始化语句
System.out.println(str);
}
System.out.println("程序继续执行");
}
}
输出:
java
java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
程序继续执行
java
package exceptiondemo;
public class demo1 {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
try {
System.out.println(arr[5]);
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
}
System.out.println("程序继续执行");
}
}
输出:
java
java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
at exceptiondemo.demo1.main(demo1.java:7)
程序继续执行
java
System.err.println("报错,报错,报错");
输出:以红色字体输出三个报错
3.抛出异常
throws和throw关键字
1.throw关键字:抛出一个具体的异常对象
作用:在方法内部主动抛出一个异常(即" 制造 "一个异常)
语法:
java
throw new 异常类("错误信息");
位置:写在方法体内
java
public void checkAge(int age) {
if (age < 0) {
// 主动抛出一个运行时异常
throw new IllegalArgumentException("年龄不能为负数!"); // 如果运行到这一行,那么下面的代码就不会被执行了
}
System.out.println("年龄合法:" + age);
}
2.throws关键字:声明方法可能抛出的异常类型
作用:在方法签名中声明该方法可能会抛出哪些异常,让调用者知道并处理
语法:
java
public void methodName(...) throws 异常类1, 异常类2 { ... }
位置:写在方法参数列表之后,方法体之前
注:若是编译时异常,则必须用throws声明;若是运行时异常,可以不声明throws
java
import java.io.*;
public void readFile(String filename) throws IOException {
FileReader file = new FileReader(filename); // 可能抛出 IOException
// ... 读取文件
}
3.对比
| 特性 | throw |
throws |
|---|---|---|
| 作用 | 抛出一个具体的异常对象 | 声明方法可能抛出的异常类型 |
| 位置 | 方法体内 | 方法签名(参数后) |
| 后面跟什么 | 异常对象(如 new NullPointerException()) |
异常类(如 IOException, Exception) |
| 数量 | 一次只能抛出一个异常 | 可以声明多个异常(用逗号分隔) |
| 是否必须处理 | 抛出后必须被捕获或继续向上抛 | 声明后由调用者决定如何处理 |
四.自定义异常
在 Java 中,自定义异常(Custom Exception) 是指开发者根据业务需求创建的异常类。Java 提供的内置异常(如 NullPointerException、IOException 等)虽然覆盖了很多场景,但在实际开发中,常常需要更具体、语义更清晰的异常来表达特定错误。
1.为什么要自定义异常
- 提高代码可读性 :比如
InvalidOrderStatusException比IllegalArgumentException更明确。 - 便于分类处理 :不同业务异常可以被不同的
catch块处理。 - 封装业务逻辑错误:将业务规则校验失败以异常形式抛出。
2.如何自定义异常
自定义异常类必须继承自 Throwable 的子类,通常选择:
- 继承
Exception→ 检查异常(Checked Exception) - 继承
RuntimeException→ 非检查异常(Unchecked Exception)
✅ 推荐 :大多数业务异常继承
RuntimeException,避免强制调用者处理,保持代码简洁。
示例1:自定义非检查异常(推荐)
java
// 自定义运行时异常(非检查异常)
public class InsufficientBalanceException extends RuntimeException {
public InsufficientBalanceException(String message) {
super(message);
}
// 可选:带 cause 的构造器(用于异常链)
public InsufficientBalanceException(String message, Throwable cause) {
super(message, cause);
}
}
用法:
java
public class BankAccount {
private double balance;
public BankAccount(double balance) {
this.balance = balance;
}
public void withdraw(double amount) {
if (amount > balance) {
throw new InsufficientBalanceException("余额不足!当前余额: " + balance);
}
balance -= amount;
System.out.println("取款成功,剩余余额: " + balance);
}
}
// 测试
public class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount(100);
try {
account.withdraw(150);
} catch (InsufficientBalanceException e) {
System.err.println("业务错误: " + e.getMessage());
}
}
}
| 场景 | 推荐类型 |
|---|---|
| 业务规则校验失败(如参数非法、状态不对) | RuntimeException(非检查) |
| 外部资源问题(如网络、文件、数据库)且可恢复 | Exception(检查异常) |
| 内容 | 说明 |
|---|---|
| 继承哪个类? | RuntimeException(推荐)或 Exception |
| 关键构造器 | 至少提供 String message 构造器 |
| 何时用? | 表达特定业务错误,提升代码清晰度 |
| 命名 | 以 Exception 结尾,语义明确 |