核心思想(一句话总结)
throws:声明一个方法可能会抛出某种异常,它把这个“抛出异常”的责任交给了方法的调用者。throw:执行一个动作,真正地创建并抛出一个异常对象,它是在代码中主动制造一个异常。
throws 关键字
throws 用在方法签名中,用来声明该方法可能会抛出的一种或多种异常类型,它是一个“预告”,告诉调用者:“我在执行过程中可能会遇到这些麻烦,你需要准备好处理它们。”

语法
修饰符 返回值类型 方法名(参数列表) throws 异常类型1, 异常类型2, ... {
// 方法体
}
特点
- 声明责任:
throws的作用是声明,而不是处理,它告诉编译器这个方法可能会抛出哪些异常。 - 转移责任:它将处理异常的责任从当前方法转移到了调用该方法的方法上。
- 用于方法签名:它只能出现在方法签名后面。
- 受检异常:
throws主要用于处理受检异常,如果你不处理(try-catch)也不声明(throws),编译器会报错。 - 链式传递:如果一个方法 A 调用了另一个方法 B,而方法 B 声明了
throws Exception,那么方法 A 必须要么try-catch这个异常,要么自己也声明throws Exception。
示例
假设我们有一个方法,它从一个文件中读取内容,文件操作可能会抛出 FileNotFoundException(文件没找到)和 IOException(读写错误),这两个都是受检异常。
import java.io.FileInputStream;
import java.io.IOException;
public class FileReaderExample {
// 这个方法声明它可能会抛出 IOException
// 它自己不处理,而是让调用者来处理
public void readFile(String filePath) throws IOException {
System.out.println("准备读取文件: " + filePath);
FileInputStream fis = new FileInputStream(filePath);
// ... 其他文件操作代码 ...
System.out.println("文件读取成功!");
}
}
我们来看调用它的代码:
public class Main {
public static void main(String[] args) {
FileReaderExample example = new FileReaderExample();
// 因为 readFile() 方法声明了 throws IOException,
// main 方法必须处理这个异常
try {
example.readFile("my_file.txt");
} catch (IOException e) {
// 捕获并处理异常
System.err.println("读取文件时出错: " + e.getMessage());
e.printStackTrace();
}
System.out.println("程序继续执行...");
}
}
在这个例子中:
readFile方法使用throws IOException来声明它可能会发生 IO 错误。main方法作为调用者,通过try-catch块来捕获并处理这个潜在的错误。
throw 关键字
throw 用于在方法体内手动地抛出一个异常对象,当你明确知道某个错误条件已经发生,并且想让程序以异常的方式处理时,就可以使用 throw。

语法
throw new 异常类名(参数);
new 异常类名(参数):创建一个异常对象,参数通常是错误信息字符串。throw:关键字,用于抛出这个对象。
特点
- 执行动作:
throw是一条可执行的语句,它会立即中断当前方法的执行流程,寻找匹配的catch块或者向上传递异常。 - 创建异常:它负责创建一个异常实例并抛出。
- 用于方法体内:它只能用在方法体内部。
- 任何异常类型:无论是受检异常还是非受检异常(
RuntimeException及其子类),都可以被throw抛出。 - 主动触发:通常用于根据程序的业务逻辑主动抛出异常,检查参数是否合法,如果不合法就抛出
IllegalArgumentException。
示例
假设我们有一个方法,用于计算一个数的倒数,我们不能计算 0 的倒数,因为这是数学上无意义的操作,当参数为 0 时,我们应该抛出一个异常。
public class Divider {
/**
* 计算一个数的倒数
* @param number 输入的数字
* @return 倒数
* @throws IllegalArgumentException 如果输入数字为 0
*/
public double calculateReciprocal(int number) {
// 业务逻辑检查:如果数字为 0,则抛出异常
if (number == 0) {
// 使用 throw 主动创建并抛出一个异常
throw new IllegalArgumentException("参数不能为 0,因为 0 没有倒数。");
}
return 1.0 / number;
}
}
调用这个方法的代码:
public class Main {
public static void main(String[] args) {
Divider divider = new Divider();
try {
System.out.println("10 的倒数是: " + divider.calculateReciprocal(10));
System.out.println("0 的倒数是: " + divider.calculateReciprocal(0)); // 这行代码会抛出异常
} catch (IllegalArgumentException e) {
// 捕获由 throw 抛出的异常
System.err.println("捕获到非法参数异常: " + e.getMessage());
}
System.out.println("程序继续执行...");
}
}
输出结果:
10 的倒数是: 0.1
捕获到非法参数异常: 参数不能为 0,因为 0 没有倒数。
程序继续执行...
在这个例子中:

calculateReciprocal方法内部使用throw new IllegalArgumentException(...)来主动抛出一个异常。- 这个异常被
main方法中的try-catch块捕获并处理。
总结与对比
| 特性 | throws |
throw |
|---|---|---|
| 含义 | 声明、预告 | 执行、抛出 |
| 语法位置 | 方法签名后 | 方法体内部 |
| 作用 | 告知调用者此方法可能抛出的异常类型,将处理责任转移 | 手动创建并抛出一个异常对象,中断当前方法执行 |
| 与异常的关系 | 与异常类型关联 | 与异常对象关联 |
| 使用场景 | 处理可能由 JVM 或 API 抛出的受检异常 | 根据业务逻辑主动抛出异常,可以是受检的也可以是非受检的 |
| 例子 | public void method() throws IOException {} |
if (age < 0) throw new IllegalArgumentException("年龄不能为负"); |
一个综合示例
让我们把 throw 和 throws 放在同一个例子中,看看它们如何协同工作。
public class UserService {
/**
* 根据用户ID查找用户
* @param userId 用户ID
* @return 用户对象
* @throws UserNotFoundException 如果用户不存在
*/
public User findUserById(int userId) throws UserNotFoundException {
// 模拟从数据库查询用户
if (userId <= 0) {
// 主动抛出一个异常,这个异常是自定义的,通常是受检异常
throw new UserNotFoundException("无效的用户ID: " + userId);
}
// ... 查询逻辑 ...
User user = new User(userId, "John Doe");
if (user == null) {
throw new UserNotFoundException("未找到ID为 " + userId + " 的用户。");
}
return user;
}
}
// 自定义异常类
class UserNotFoundException extends Exception {
public UserNotFoundException(String message) {
super(message);
}
}
// 调用方
public class Main {
public static void main(String[] args) {
UserService userService = new UserService();
try {
// 1. 调用 findUserById,它声明了 throws UserNotFoundException
User user = userService.findUserById(-1); // 传入一个无效ID
System.out.println("找到用户: " + user.getName());
} catch (UserNotFoundException e) {
// 2. 捕获由 findUserById 方法内部 throw 抛出的异常
System.err.println("用户查询失败: " + e.getMessage());
}
System.out.println("程序结束。");
}
}
流程分析:
main方法调用userService.findUserById(-1)。findUserById方法执行,发现userId为 -1,不合法。- 方法执行
throw new UserNotFoundException(...),创建了一个异常对象并抛出。 - 由于
findUserById方法在签名上声明了throws UserNotFoundException,它将这个异常传递给了调用者main方法。 main方法的catch块捕获了这个UserNotFoundException,并打印出错误信息。- 程序继续执行,没有崩溃。
希望这个详细的解释和例子能帮助你彻底理解 throw 和 throws 的区别!
