throw用于方法体内主动抛出异常对象,需用new创建实例,执行后中断流程;抛出RuntimeException子类无需处理,抛出Exception等受检异常则必须try-catch或throws声明。
throw 是“真扔”,throws 是“预告要扔”——一个执行动作,一个契约声明。
throw 怎么用?在方法里主动抛出一个异常对象
它必须出现在方法体内部,后面跟的是 new 出来的异常实例,一执行就中断当前流程,后续代码不运行。
- 只能抛一个异常对象,比如
throw new IllegalArgumentException("用户名不能为空") - 抛
RuntimeException及其子类(如IllegalArgumentException、NullPointerException)时,编译器不强制你处理;但抛Exception或IOException这类受检异常(checked exception),就必须用try-catch包住,或在方法上加throws声明 - 常见误写:
throw IllegalArgumentException(错!缺new和括号),正确是throw new IllegalArgumentException("xxx") - 典型场景:参数校验失败、业务规则被破坏(如年龄为负数、订单状态非法)
public void setAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("年龄不能为负数"); // ✅ 正确:抛对象
}
this.age = age;
}
throws 怎么用?在方法签名末尾声明可能抛出的异常类型
它不抛异常,只是告诉调用者:“我这个方法内部可能会冒出这些异常,你得自己兜着。”
- 写在方法返回类型之后、方法体之前,后面跟的是异常类名,不是对象,比如
throws IOException, SQLException - 可声明多个异常,用逗号分隔;只对受检异常有编译强制约束,运行时异常加不加都行(但加了更利于调用方预判)
- 子类重写父类方法时,
throws声明的异常类型不能比父类更宽(比如父类声明throws IOException,子类不能改成throws Exception) - 本质是责任上移:你不想在本层处理,就用
throws把异常“甩”给上层调用者
public void readFile(String path) throws FileNotFoundException, SecurityException {
File file = new File(path);
if (!file.exists()) {
throw new FileNotFoundException("文件不存在: " + path); // ✅ 内部 throw,外部 throws 呼应
}
// ...
}
为什么它们经常一起出现?配合使用才符合 Java 异常处理逻辑
单独用 throw 抛受检异常却不声明 throws,编译直接报错;只写 throws 却没在方法里真正 throw,虽然不报错,但属于“虚假预警”,会误导调用方。