throws:用在 方法签名 上,表示这个方法可能会抛出某种异常,但 自己不处理,而是将处理的责任 抛给 调用它的方法。throw:用在 方法体 内部,是手动 创建并抛出 一个异常实例的行为。
详细解释
throws - 声明异常 (Declare Exception)
throws 关键字用于方法签名中,目的是 声明 该方法可能会抛出的一种或多种受检异常(Checked Exceptions),它像一个“预告”,告诉方法的调用者:“我可能会出问题,但这个问题我不打算在这里解决,你需要准备好处理它。”

语法:
修饰符 返回值类型 方法名(参数列表) throws 异常类型1, 异常类型2 {
// 方法体
}
核心要点:
- 位置:方法签名末尾。
- 作用:声明异常,不处理异常。
- 目标:通知调用者,让他们使用
try-catch块处理,或者在它们自己的方法签名上继续使用throws声明。 - 受检异常:通常用于处理受检异常(即编译器会检查的异常),对于非受检异常(如
RuntimeException),虽然也可以使用throws,但通常不推荐,因为调用者无法强制处理它们。 - 责任转移:它将处理异常的责任从当前方法转移到了调用者。
示例:
假设我们有一个方法,它根据文件名读取文件内容,如果文件不存在,会抛出 FileNotFoundException(这是一个受检异常)。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileReader {
// 声明这个方法可能会抛出 FileNotFoundException 和 IOException
// 它自己不处理,让调用者去处理
public void readFile(String filePath) throws FileNotFoundException, IOException {
System.out.println("尝试读取文件: " + filePath);
FileInputStream fis = new FileInputStream(filePath);
// ... 读取文件内容
fis.close();
}
}
任何调用 readFile 方法的代码都必须处理这些异常:

public class Main {
public static void main(String[] args) {
FileReader reader = new FileReader();
// 调用者必须处理异常
try {
reader.readFile("test.txt");
} catch (FileNotFoundException e) {
System.err.println("错误:文件未找到!");
e.printStackTrace();
} catch (IOException e) {
System.err.println("错误:发生IO异常!");
e.printStackTrace();
}
}
}
throw - 抛出异常 (Throw Exception)
throw 关键字用于方法体内部,是程序员 手动 创建一个异常对象并将其抛出,这通常在某个条件不满足、业务逻辑出错或数据不合法时使用。
语法:
throw new 异常类型("异常信息");
核心要点:
- 位置:方法体内部。
- 作用:主动抛出一个异常实例。
- 目标:中断当前方法的正常执行流程,并将异常信息传递给上层的调用栈。
- 创建实例:它后面必须跟一个
Throwable类或其子类的对象,你不能抛出一个基本类型或一个普通的对象。 - 可以抛出受检或非受检异常:
- 如果抛出的是 受检异常,那么当前方法必须使用
throws声明,否则编译会报错。 - 如果抛出的是 非受检异常(如
RuntimeException),则不需要throws声明。
- 如果抛出的是 受检异常,那么当前方法必须使用
示例: 假设我们有一个方法,用于给用户设置年龄,年龄不能为负数。

public class User {
private String name;
private int age;
public void setAge(int age) {
// 检查年龄是否合法
if (age < 0) {
// 手动创建并抛出一个 IllegalArgumentException
// 这是一个非受检异常,所以方法签名不需要 throws
throw new IllegalArgumentException("年龄不能为负数!");
}
this.age = age;
}
}
如果调用者传入负数,就会触发这个异常:
public class Main {
public static void main(String[] args) {
User user = new User();
user.setName("张三");
try {
user.setAge(-5); // 这里会抛出 IllegalArgumentException
System.out.println("年龄设置成功。");
} catch (IllegalArgumentException e) {
System.out.println("捕获到异常: " + e.getMessage());
}
}
}
// 输出: 捕获到异常: 年龄不能为负数!
核心区别对比表
| 特性 | throws |
throw |
|---|---|---|
| 语法位置 | 方法签名末尾 | 方法体内部 |
| 关键字性质 | 关键字 | 关键字 |
| 作用 | 声明一个方法可能抛出的异常 | 执行一个抛出异常的动作 |
| 操作对象 | 是一个异常类型列表(如 IOException) |
是一个异常对象实例(如 new IOException()) |
| 编译器检查 | 用于受检异常,编译器会检查是否处理 | 抛出受检异常时,要求方法必须用 throws 声明 |
| 责任 | 将异常处理的责任转移给调用者 | 主动中断当前方法的执行,并向上传递异常 |
| 与异常的关系 | 是一种“预告”或“契约” | 是一种“行动”或“事件” |
两者结合使用的例子
这是最常见的情况:一个方法内部通过 throw 抛出一个受检异常,然后通过 throws 声明它。
public class BankAccount {
private double balance;
// 存款方法
public void deposit(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("存款金额必须大于0");
}
this.balance += amount;
}
// 取款方法
// 1. 方法内部可能会抛出受检异常
// 2. 方法签名使用 throws 声明这个异常
public void withdraw(double amount) throws InsufficientFundsException {
if (amount > this.balance) {
// 手动创建并抛出自定义的受检异常
throw new InsufficientFundsException("余额不足,无法取款!");
}
this.balance -= amount;
}
}
// 自定义一个受检异常
class InsufficientFundsException extends Exception {
public InsufficientFundsException(String message) {
super(message);
}
}
调用方:
public class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount();
account.deposit(1000);
try {
account.withdraw(1500); // 这会触发 withdraw 方法中的 throw
System.out.println("取款成功。");
} catch (InsufficientFundsException e) {
// 因为 withdraw 方法用 throws 声明了,所以这里必须处理
System.out.println("取款失败: " + e.getMessage());
}
}
}
// 输出: 取款失败: 余额不足,无法取款!
在这个例子中:
withdraw方法内部使用throw来创建并抛出InsufficientFundsException。withdraw方法在签名上使用throws来声明它可能会抛出这个异常。main方法作为调用者,必须用try-catch来处理这个异常。
记住这个简单的类比:
throws就像一份 “产品说明书”,告诉你这个方法(产品)可能会有什么风险(异常),但说明书本身不解决风险。throw就像实际 “按下警报按钮” 的动作,当某个条件满足时,你主动触发了警报(异常)。
理解了 throw 和 throws 的区别,你就掌握了 Java 异常处理的核心机制之一。
