throw和throws的区别是什么
一、总览
1.一句话区分:
✅ throw是"扔出"一个异常对象
✅ throws是"声明"这个方法可能会扔出异常
2.具体区别详解(结合例子)
关键词 | 含义 | 作用场景 | 是否放在方法签名中 | 是否后面跟异常对象 |
throw | 实际扔出一个异常实例 | 在方法体内部真正抛出异常 | ❌ 否 | ✅ 是(对象) |
throws | 声明这个方法可能抛异常 | 在方法声明处告诉别人我可能出错 | ✅ 是 | ❌ 否(类名) |
3.看两个完整的例子(贴近实际项目)
🚀 场景一:系统找不到客户数据 → 抛出业务异常
public void checkCustomer(String custId) { if (custId == null) { throw new BusinessException("客户ID不能为空"); } }
⛳ 解释:
- throw new BusinessException(...):这里是真正扔出异常,让程序往上抛。
- 这是运行时异常(RuntimeException),不需要throws,调用这个方法的人也不需要强制处理。
🚀 场景二:方法可能连接数据库,查客户 → 声明可能抛异常(checked异常)
public Customer getCustomer(String custId) throws SQLException { Connection conn = dataSource.getConnection(); // 可能抛 SQLException ... }
⛳ 解释:
- throws SQLException是告诉别人:这个方法可能会抛数据库异常
- 调用这个方法的地方必须 try-catch,或者继续throws抛出去
4.结合使用的例子(throw和throws都出现)
🚀 场景三:读取文件内容的方法
public String readFile(String path) throws IOException { FileReader reader = new FileReader(path); // 可能抛FileNotFoundException ... return content; }
调用者必须这样写:
try { String content = readFile("data.txt"); } catch (IOException e) { System.out.println("读取文件失败:" + e.getMessage()); }
⛳ 总结:
- throws IOException:声明可能发生异常(不处理会编译错误)
- throw new IOException(...):在方法内部真正抛出错误对象
5.实际后端开发中的使用规律总结
异常类型 | 是否需要throws声明 | 常见场景 |
运行时异常(RuntimeException) | ❌ 不强制 | 业务异常、空指针、非法状态等 |
检查型异常(IOException、SQLException等) | ✅ 必须声明或 try-catch | 文件操作、数据库连接、远程调用等 |
6.再帮你总结一下核心记忆点:
关键词 | 中文记忆法 | 关键点 |
throw | “抛出” | 在代码块内部用,抛出异常对象 |
throws | “声明” | 在方法签名上,告诉别人我可能抛异常 |
你可以记住一句话:
“throw 抛对象,throws 抛声明”
7.结合你目前学的内容再举一例(你的项目代码)
if (sysOffice == null){ throw new BusinessException("jiesuan公司code不存在"); }
- 这里你只写了throw,没有写throws,也不需要写,因为BusinessException是RuntimeException的子类。
- 如果你抛的是IOException或SQLException,那你就必须throws它。
二、常见延伸思考:
throws只出现在方法定义的时候?
throws后必须要跟Exception的类型吗?
如果是的话,这个类型可以自定义吗?
这个throws其实在功能上一点用都没有,它只有一个用,告诉调用它的程序员,要在调用它的位置,写上try-catch,就这一个提示调用而已,对吗?
✅ throws是写在方法定义上,比如:
public void readFile() throws IOException { ... }
✅ throws后面必须跟一个或多个异常类的类型,比如:
throws IOException throws IOException, SQLException
❓"throws后必须要跟一个exception类型吗?可以自定义吗?"
✅ 必须是Exception或它的子类!
- 可以是系统的:IOException、SQLException
- 也可以是你自定义的:比如 BusinessException、MyCustomException
public void saveData() throws BusinessException { ... }
✅ 你说的第二点:
throws 的功能是"告诉调用者要 try-catch",其实没别的用,对吗?
✅ 你说得几乎完全正确!
它的核心作用的确就是:👉 "告诉调用者,我可能会抛出这些异常,你要么 try-catch,要么继续 throws 接着往上扔"。
这是Java的设计理念 —— "受检查异常(checked exception)必须显式处理”。
❗但注意:
只有非运行时异常(checked exception)必须throws和try-catch。
比如:
public void test() throws IOException { // 编译器会强制你 throws 或 try-catch }
但像这种就不需要声明:
public void test() { throw new NullPointerException("空指针"); // 编译器不会管 }
✅ 总结你的理解版本(你可以这样记)
我理解的 | 是否正确 | 补充优化说明 |
throws只能写在方法定义上 | 基本正确 | 是写在"方法或构造器定义上" |
throws后必须是Exception类型 | ✅ | 可以是系统的或自定义的 |
throws的功能就是告诉别人"你得 try-catch" | ✅✅✅ | 本质就是编译器检查提示 |
throws本质上没别的作用 | ✅(语义上) | 它不控制流程、也不抛异常,只是"声明" |
🧠 一句话金句送你:
throw是真的"抛",throws是提前"告知"。
三、追问:
所以:
非检查异常基本都使用throw,而不需要try-catch,
检查异常基本都必须使用throws与try-catch结合的方式,对吗?
然后第二个问题是,在实际开发中,我该根据什么,或者说怎么判断一个区域的代码属于非检查异常还是检查异常,以及我该使用throw还是throws与try-catch的结合?
✅ 第一部分:你说的对吗?
❓你说:
非检查异常基本都使用throw,而不需要try-catch;
检查异常基本都必须使用throws与try-catch的方式。
✅ 非常正确!总结如下:
异常类型 | 是否必须try-catch | 是否必须throws | 常见写法 |
✅ 非检查异常(RuntimeException) | ❌ 不需要 | ❌ 不需要 | 直接 throw new XxException() |
✅ 检查异常(Exception 子类) | ✅必须try或throws | ✅ 必须 | throws IOException + try-catch |
这也是 Java 区分异常的核心设计哲学:
非检查异常表示"程序员你写错了",检查异常表示"你必须处理这个潜在的异常"。
✅ 第二部分:实战中怎么判断应该用哪种方式?
🎯[问题1]我怎么判断这段代码是否可能抛"检查异常"?
🛠 答案:靠工具提示 + 查看方法签名
比如你写下面这段代码时:
FileReader reader = new FileReader("hello.txt");
编译器会提示你:
Unhandled exception: java.io.FileNotFoundException
这就说明:
- FileReader的构造方法throws FileNotFoundException;
- 这是 检查异常(checked exception),你必须处理:
try { FileReader reader = new FileReader("hello.txt"); } catch (FileNotFoundException e) { ... }
🎯【问题2】那我怎么判断某个异常是"检查"还是"非检查"?
✅ 答案:记住一个实用规则:
☆☆☆☆☆凡是继承自RuntimeException的,是非检查异常;其他都是检查异常☆☆☆☆☆ —— (太牛了这句话!)
实战中你不需要死记硬背,用IDE(比如 IntelliJ IDEA)可以直接看到类的继承结构:
举个例子:
java.lang.NullPointerException ↳ extends RuntimeException → ✅ 非检查异常
java.io.IOException ↳ extends Exception → ❌ 不是 RuntimeException → ✅ 检查异常
✅ 第三部分:开发中你该怎么选throw / throws?
这部分送你一张实战速查表:
情况类型 | 推荐使用 | 是否try-catch必须 | 举例 |
你主动抛出业务异常 | throw + 自定义异常(继承RuntimeException) | ❌ 不需要 | throw new BusinessException("参数非法") |
使用Java API抛出检查异常的方法 | throws + try-catch | ✅ 必须 | throws IOException, try FileReader |
你写的工具类方法想抛出异常让别人处理 | throws 声明 | ✅ 看情况 | public void read() throws SQLException |
系统程序错误,比如数组越界、空指针 | 直接throw | ❌ 不需要 | throw new NullPointerException() |
💡 实战建议(经验分享):
✅ 常用套路1:业务异常都用RuntimeException
不声明throws,调用处也不用处理,统一交给全局异常处理器处理,比如:
if (user == null) { throw new BusinessException("用户不存在"); }
全局处理器:
@ExceptionHandler(BusinessException.class) public Response handleBiz(BusinessException e) { return fail(e.getMessage()); }
✅ 常用套路2:调用第三方/系统API,就要try-catch或throws
比如文件、数据库、网络:
public void readFile(String path) throws IOException { FileReader reader = new FileReader(path); }
✅ 常用套路3:如果你的方法是"中间层",你也可以用throws把异常继续往上传
public void processOrder(String id) throws SQLException { orderDao.save(id); // save() 方法 throws SQLException }
让 Controller 再去处理,或者让上层统一处理。
✅ 总结你可以直接用的口诀:
🔹 throw抛对象,throws抛声明
🔹 RuntimeException不用管,Checked Exception必须处理
🔹 自己的业务用throw,自定义异常更清晰
🔹 调系统 API就用 throws或try-catch