Skip to content

15.4 throws 和 throw

throws 关键字

throws 的概念

throws 关键字用于在方法声明中指定该方法可能抛出的异常类型。它的作用是将异常的处理责任转移给调用该方法的代码。

throws 的语法

java
修饰符 返回类型 方法名(参数列表) throws 异常类型1, 异常类型2, ... {
    // 方法体
}

throws 的使用场景

  1. 方法可能抛出检查型异常:当方法内部可能抛出检查型异常,而方法本身不处理这些异常时,需要使用 throws 声明这些异常

  2. 异常传递:将异常的处理责任传递给调用者

  3. 方法重写:子类重写父类方法时,不能抛出比父类方法更多的异常

示例

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

子类重写父类方法时,抛出的异常不能比父类方法更广泛:

  1. 子类方法可以不抛出任何异常
  2. 子类方法可以抛出与父类方法相同的异常
  3. 子类方法可以抛出父类方法异常的子类
  4. 子类方法不能抛出父类方法没有声明的检查型异常

示例

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. 手动触发异常:当满足特定条件时,手动抛出异常

  2. 自定义异常:抛出自定义的异常对象

  3. 异常转换:将一种异常转换为另一种异常

示例

示例 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 的区别

特性throwsthrow
位置方法声明处方法体内部
作用声明方法可能抛出的异常类型手动抛出一个具体的异常对象
后跟内容异常类型列表异常对象
使用次数每个方法只能使用一次一个方法中可以使用多次
异常处理将异常处理责任转移给调用者触发异常,中断当前方法执行

示例: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());
    }
}

最佳实践

  1. 合理使用 throws:只声明方法确实可能抛出的异常,不要声明过多的异常

  2. 优先处理异常:尽量在方法内部处理异常,而不是将所有异常都传递给调用者

  3. 明确异常信息:使用 throw 抛出异常时,提供清晰、具体的异常信息

  4. 异常转换:在适当的情况下,将底层异常转换为更有意义的业务异常

  5. 遵循方法重写规则:子类重写父类方法时,注意异常声明的规则

  6. 使用自定义异常:对于业务逻辑错误,使用自定义异常可以使代码更清晰

总结

throwsthrow 是 Java 中处理异常的重要关键字:

  1. throws

    • 用于方法声明,指定方法可能抛出的异常类型
    • 将异常处理责任转移给调用者
    • 一个方法可以声明抛出多个异常
  2. throw

    • 用于方法体内部,手动抛出一个具体的异常对象
    • 触发异常,中断当前方法的执行
    • 可以抛出系统异常或自定义异常
  3. 区别

    • throws 声明异常类型,throw 抛出具体异常对象
    • throws 位于方法声明处,throw 位于方法体内部
    • throws 每个方法只能使用一次,throw 一个方法中可以使用多次

通过合理使用 throwsthrow,可以使代码的异常处理更加清晰和有效,提高程序的健壮性和可维护性。

© 2026 编程马·菜鸟教程 版权所有