throw是方法体内抛出异常的动作,throws是方法签名中声明可能抛出异常的承诺;前者触发异常并中断执行,后者仅作编译期契约声明,不改变运行行为。

throw 是动作,throws 是承诺。前者在方法内部实实在在地“扔出”一个异常对象,程序流立刻中断;后者只是在方法签名上写一行声明,告诉别人“我可能会出问题,你调用我时得自己兜着”。两者不在同一层面上——一个执行,一个预告。
throw:方法体内的异常触发器
它出现在代码块里,必须搭配一个已创建的异常实例,比如 throw new IllegalArgumentException("id 不能为空")。只要执行到这一行,JVM 就会立即构造异常对象、填充栈轨迹、终止当前方法,并把控制权交给最近的 catch 块或向上抛出。
- 每次只能抛一个异常对象
- 可以抛受检异常(如 IOException)或非受检异常(如 NullPointerException)
- 如果抛的是受检异常,且所在方法没用 try-catch 捕获,就必须用 throws 声明,否则编译不通过
- 后面不能跟正常语句(因为永远执行不到)
throws:方法签名上的责任交接声明
它写在方法参数列表之后、方法体之前,例如 public void save(File f) throws IOException, SQLException。这行字本身不做任何事,也不产生异常,只是划一条线:这些异常类型可能从我这儿冒出来,你调用我,就得负责处理它们——要么用 try-catch 捕获,要么继续用 throws 往上推。
- 声明的是异常类型,不是实例;可列多个,用逗号分隔
- 主要面向受检异常;对 RuntimeException 及其子类,写不写 throws 都合法,但写了能提升接口可读性
- 不保证异常一定发生,只表示“有可能”,取决于运行时条件(比如文件是否存在、数据库是否连通)
- 属于方法契约的一部分,影响 API 使用者的设计决策
二者配合使用的典型逻辑链
当你在方法里检测到非法状态,先 new 一个异常,再用 throw 把它甩出去;而为了让编译通过、也让调用方知情,你得在方法头上用 throws 把这个异常类型亮出来。
- throw new FileNotFoundException() → 必须对应 throws FileNotFoundException(若为受检异常)
- throw new IllegalArgumentException() → throws 可省略,但加上更清晰
- 一个方法里有多个 throw,最终 throws 声明需覆盖所有可能抛出的受检异常类型
容易混淆的关键点
有人以为 “throws 了就一定会抛”,其实不是——它只是备案;也有人以为 “throw 了就必须 throws”,其实只对受检异常才强制。非受检异常(RuntimeException 类及其子类)哪怕不声明,也能被 throw 出去,JVM 照样处理。
- throws 不改变程序行为,只影响编译检查和协作约定
- throw 改变程序行为,是真正的异常起点
- 二者不是替代关系,而是上下游配合:throws 是说明书,throw 是按下启动键

















