throw是方法体内抛出异常的执行语句,throws是方法声明处声明可能抛出异常的契约;前者必须跟异常实例,后者只能跟异常类型,且仅对受检异常有强制约束。

throw 和 throws 看似只差一个字母,但语法位置和语义角色完全不同:前者是“动手扔”,后者是“提前打招呼”。理解错位置,轻则编译失败,重则让异常责任模糊、调用链断裂。
语法位置:一个在方法里,一个在方法头上
throw 必须写在方法体内部,是一条可执行语句,像 return 一样能直接中断流程。它后面跟的是一个已经 new 出来的异常对象实例:
-
正确:
if (id < 0) throw new IllegalArgumentException("ID不能为负"); -
错误:
public void save() throw new Exception() { ... }(throw 不能出现在方法签名处)
throws 则严格固定在方法声明末尾、参数列表之后、方法体之前,属于方法签名的一部分,不执行也不创建异常:
-
正确:
public void readFile(String path) throws IOException, FileNotFoundException { ... } -
错误:
void process() { throws SQLException { ... } }(throws 不能放在方法体内)
语义本质:动作 vs 承诺
throw 是一个运行时动作,代表“此刻出错了,我立刻把异常交出去”。一旦执行,当前方法立即停止,堆栈开始向上回溯。
throws 不是动作,而是一种契约式声明,意思是“我这个方法有可能抛出这些异常,请你——调用者——心里有数、做好准备”。它不保证一定发生,只是划清责任边界。
- 比如
FileInputStream构造方法声明throws FileNotFoundException,但实际是否抛出,取决于传入路径对应文件是否存在 - 而你在方法里写
throw new IllegalStateException(),只要执行到这行,就100%抛出
异常类型与处理约束的联动关系
throw 抛什么,直接决定 throws 是否必须跟上:
- 抛的是受检异常(如
IOException),所在方法必须用 throws 声明,或用 try-catch 捕获;否则编译报错 - 抛的是非受检异常(如
IllegalArgumentException、NullPointerException),throws 可写可不写——写了不强制调用者处理,但写出来更利于协作和文档化 - throws 声明了多个异常,不代表每个分支都 throw;它只是汇总所有可能被抛出的受检异常类型
常见误用与设计提醒
混用或滥用会削弱异常机制的价值:
- 在方法体里写
throws IOException—— 语法非法,throws 只能出现在方法头 - 用
throw new Exception()泛化抛出 —— 掩盖问题本质,应优先选语义明确的子类,如IllegalStateException或自定义业务异常 - 一个方法 throws 超过 3–4 种异常 —— 往往说明职责过重,建议拆分逻辑,或统一包装成一个业务异常类

















