Appearance
15.4 throws 和 throw
throws 关键字
throws 的概念
throws 关键字用于在方法声明中指定该方法可能抛出的异常类型。它的作用是将异常的处理责任转移给调用该方法的代码。
throws 的语法
java
修饰符 返回类型 方法名(参数列表) throws 异常类型1, 异常类型2, ... {
// 方法体
}throws 的使用场景
方法可能抛出检查型异常:当方法内部可能抛出检查型异常,而方法本身不处理这些异常时,需要使用
throws声明这些异常异常传递:将异常的处理责任传递给调用者
方法重写:子类重写父类方法时,不能抛出比父类方法更多的异常
示例
java
import java.io.IOException;
public class ThrowsExample {
// 声明该方法可能抛出 IOException
public static void readFile() throws IOException {
// 可能抛出 IOException 的代码
throw new IOException("文件读取错误");
}
public static void main(String[] args) {
try {
readFile(); // 调用可能抛出异常的方法
} catch (IOException e) {
System.out.println("捕获到异常: " + e.getMessage());
}
}
}多异常声明
一个方法可以声明抛出多个异常,多个异常类型之间用逗号分隔。
java
import java.io.IOException;
import java.sql.SQLException;
public class MultipleThrowsExample {
// 声明该方法可能抛出 IOException 和 SQLException
public static void process() throws IOException, SQLException {
// 可能抛出 IOException 的代码
// 可能抛出 SQLException 的代码
}
public static void main(String[] args) {
try {
process();
} catch (IOException e) {
System.out.println("IO 异常: " + e.getMessage());
} catch (SQLException e) {
System.out.println("SQL 异常: " + e.getMessage());
}
}
}方法重写中的 throws
子类重写父类方法时,抛出的异常不能比父类方法更广泛:
- 子类方法可以不抛出任何异常
- 子类方法可以抛出与父类方法相同的异常
- 子类方法可以抛出父类方法异常的子类
- 子类方法不能抛出父类方法没有声明的检查型异常
示例:
java
import java.io.IOException;
import java.sql.SQLException;
class Parent {
// 父类方法声明抛出 IOException
public void method() throws IOException {
// 方法体
}
}
class Child extends Parent {
// 子类方法可以不抛出异常
@Override
public void method() {
// 方法体
}
}
class Child2 extends Parent {
// 子类方法可以抛出与父类相同的异常
@Override
public void method() throws IOException {
// 方法体
}
}
class Child3 extends Parent {
// 子类方法可以抛出父类异常的子类
@Override
public void method() throws java.io.FileNotFoundException {
// 方法体
}
}
class Child4 extends Parent {
// 错误:子类方法不能抛出父类方法没有声明的检查型异常
// @Override
// public void method() throws SQLException {
// // 方法体
// }
}throw 关键字
throw 的概念
throw 关键字用于在方法内部手动抛出一个异常对象。它的作用是触发异常,中断当前方法的执行,并将异常传递给调用者。
throw 的语法
java
throw 异常对象;throw 的使用场景
手动触发异常:当满足特定条件时,手动抛出异常
自定义异常:抛出自定义的异常对象
异常转换:将一种异常转换为另一种异常
示例
示例 1:手动抛出异常
java
public class ThrowExample {
public static void checkAge(int age) {
if (age < 0) {
// 手动抛出 IllegalArgumentException
throw new IllegalArgumentException("年龄不能为负数");
}
System.out.println("年龄: " + age);
}
public static void main(String[] args) {
try {
checkAge(-10); // 调用可能抛出异常的方法
} catch (IllegalArgumentException e) {
System.out.println("捕获到异常: " + e.getMessage());
}
}
}示例 2:异常转换
java
import java.io.IOException;
public class ExceptionConversionExample {
public static void process() throws RuntimeException {
try {
// 可能抛出 IOException 的代码
throw new IOException("IO 错误");
} catch (IOException e) {
// 将 IOException 转换为 RuntimeException
throw new RuntimeException("处理失败", e);
}
}
public static void main(String[] args) {
try {
process();
} catch (RuntimeException e) {
System.out.println("捕获到异常: " + e.getMessage());
e.printStackTrace();
}
}
}throws 和 throw 的区别
| 特性 | throws | throw |
|---|---|---|
| 位置 | 方法声明处 | 方法体内部 |
| 作用 | 声明方法可能抛出的异常类型 | 手动抛出一个具体的异常对象 |
| 后跟内容 | 异常类型列表 | 异常对象 |
| 使用次数 | 每个方法只能使用一次 | 一个方法中可以使用多次 |
| 异常处理 | 将异常处理责任转移给调用者 | 触发异常,中断当前方法执行 |
示例:throws 和 throw 的综合应用
示例 1:验证用户输入
java
public class InputValidationExample {
// 声明可能抛出 IllegalArgumentException
public static void validateInput(String input) throws IllegalArgumentException {
if (input == null || input.isEmpty()) {
// 手动抛出异常
throw new IllegalArgumentException("输入不能为空");
}
System.out.println("输入有效: " + input);
}
public static void main(String[] args) {
try {
validateInput(""); // 传递空字符串
} catch (IllegalArgumentException e) {
System.out.println("输入验证失败: " + e.getMessage());
}
try {
validateInput("Hello"); // 传递有效输入
} catch (IllegalArgumentException e) {
System.out.println("输入验证失败: " + e.getMessage());
}
}
}示例 2:文件操作
java
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class FileOperationExample {
// 声明可能抛出 IOException
public static String readFirstLine(String filePath) throws IOException {
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line = reader.readLine();
if (line == null) {
// 手动抛出异常
throw new IOException("文件为空");
}
return line;
}
}
public static void main(String[] args) {
try {
String firstLine = readFirstLine("example.txt");
System.out.println("文件第一行: " + firstLine);
} catch (IOException e) {
System.out.println("文件操作失败: " + e.getMessage());
}
}
}示例 3:自定义异常
java
// 自定义异常类
class InvalidAgeException extends Exception {
public InvalidAgeException(String message) {
super(message);
}
}
public class CustomExceptionExample {
// 声明可能抛出自定义异常
public static void checkAge(int age) throws InvalidAgeException {
if (age < 0 || age > 150) {
// 手动抛出自定义异常
throw new InvalidAgeException("年龄必须在 0-150 之间");
}
System.out.println("年龄有效: " + age);
}
public static void main(String[] args) {
try {
checkAge(200); // 传递无效年龄
} catch (InvalidAgeException e) {
System.out.println("年龄验证失败: " + e.getMessage());
}
try {
checkAge(30); // 传递有效年龄
} catch (InvalidAgeException e) {
System.out.println("年龄验证失败: " + e.getMessage());
}
}
}常见问题
1. throws 声明了异常但不处理
症状:方法声明了 throws 异常,但调用者没有处理这些异常
解决方案:调用者必须使用 try-catch 捕获异常,或者继续使用 throws 声明将异常传递给上层
示例:
java
import java.io.IOException;
// 错误:main 方法没有处理 IOException
// public static void main(String[] args) {
// readFile(); // 编译错误
// }
// 正确:使用 try-catch 捕获异常
public static void main(String[] args) {
try {
readFile();
} catch (IOException e) {
System.out.println("处理异常: " + e.getMessage());
}
}
// 正确:继续传递异常
// public static void main(String[] args) throws IOException {
// readFile();
// }2. throw 后没有 return 语句
症状:throw 语句后没有 return 语句,但方法有返回值
解决方案:throw 语句会中断方法执行,因此 throw 语句后的代码不会执行,不需要 return 语句
示例:
java
public static int divide(int a, int b) {
if (b == 0) {
throw new ArithmeticException("除数不能为零");
// 不需要 return 语句,因为 throw 会中断方法执行
}
return a / b;
}3. 抛出异常后没有捕获
症状:抛出异常后没有被捕获,导致程序终止
解决方案:确保所有可能抛出的异常都被捕获或声明
示例:
java
public static void main(String[] args) {
// 错误:抛出异常后没有捕获
// int result = divide(10, 0);
// 正确:捕获异常
try {
int result = divide(10, 0);
System.out.println("结果: " + result);
} catch (ArithmeticException e) {
System.out.println("捕获到异常: " + e.getMessage());
}
}最佳实践
合理使用 throws:只声明方法确实可能抛出的异常,不要声明过多的异常
优先处理异常:尽量在方法内部处理异常,而不是将所有异常都传递给调用者
明确异常信息:使用 throw 抛出异常时,提供清晰、具体的异常信息
异常转换:在适当的情况下,将底层异常转换为更有意义的业务异常
遵循方法重写规则:子类重写父类方法时,注意异常声明的规则
使用自定义异常:对于业务逻辑错误,使用自定义异常可以使代码更清晰
总结
throws 和 throw 是 Java 中处理异常的重要关键字:
throws:
- 用于方法声明,指定方法可能抛出的异常类型
- 将异常处理责任转移给调用者
- 一个方法可以声明抛出多个异常
throw:
- 用于方法体内部,手动抛出一个具体的异常对象
- 触发异常,中断当前方法的执行
- 可以抛出系统异常或自定义异常
区别:
throws声明异常类型,throw抛出具体异常对象throws位于方法声明处,throw位于方法体内部throws每个方法只能使用一次,throw一个方法中可以使用多次
通过合理使用 throws 和 throw,可以使代码的异常处理更加清晰和有效,提高程序的健壮性和可维护性。
