杰瑞科技汇

throw与throws在异常处理中如何区分使用?

  • throws:用在 方法签名 上,表示这个方法可能会抛出某种异常,但 自己不处理,而是将处理的责任 抛给 调用它的方法。
  • throw:用在 方法体 内部,是手动 创建并抛出 一个异常实例的行为。

详细解释

throws - 声明异常 (Declare Exception)

throws 关键字用于方法签名中,目的是 声明 该方法可能会抛出的一种或多种受检异常(Checked Exceptions),它像一个“预告”,告诉方法的调用者:“我可能会出问题,但这个问题我不打算在这里解决,你需要准备好处理它。”

throw与throws在异常处理中如何区分使用?-图1
(图片来源网络,侵删)

语法:

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

核心要点:

  • 位置:方法签名末尾。
  • 作用:声明异常,不处理异常。
  • 目标:通知调用者,让他们使用 try-catch 块处理,或者在它们自己的方法签名上继续使用 throws 声明。
  • 受检异常:通常用于处理受检异常(即编译器会检查的异常),对于非受检异常(如 RuntimeException),虽然也可以使用 throws,但通常不推荐,因为调用者无法强制处理它们。
  • 责任转移:它将处理异常的责任从当前方法转移到了调用者。

示例: 假设我们有一个方法,它根据文件名读取文件内容,如果文件不存在,会抛出 FileNotFoundException(这是一个受检异常)。

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileReader {
    // 声明这个方法可能会抛出 FileNotFoundException 和 IOException
    // 它自己不处理,让调用者去处理
    public void readFile(String filePath) throws FileNotFoundException, IOException {
        System.out.println("尝试读取文件: " + filePath);
        FileInputStream fis = new FileInputStream(filePath);
        // ... 读取文件内容
        fis.close();
    }
}

任何调用 readFile 方法的代码都必须处理这些异常:

throw与throws在异常处理中如何区分使用?-图2
(图片来源网络,侵删)
public class Main {
    public static void main(String[] args) {
        FileReader reader = new FileReader();
        // 调用者必须处理异常
        try {
            reader.readFile("test.txt");
        } catch (FileNotFoundException e) {
            System.err.println("错误:文件未找到!");
            e.printStackTrace();
        } catch (IOException e) {
            System.err.println("错误:发生IO异常!");
            e.printStackTrace();
        }
    }
}

throw - 抛出异常 (Throw Exception)

throw 关键字用于方法体内部,是程序员 手动 创建一个异常对象并将其抛出,这通常在某个条件不满足、业务逻辑出错或数据不合法时使用。

语法:

throw new 异常类型("异常信息");

核心要点:

  • 位置:方法体内部。
  • 作用:主动抛出一个异常实例。
  • 目标:中断当前方法的正常执行流程,并将异常信息传递给上层的调用栈。
  • 创建实例:它后面必须跟一个 Throwable 类或其子类的对象,你不能抛出一个基本类型或一个普通的对象。
  • 可以抛出受检或非受检异常
    • 如果抛出的是 受检异常,那么当前方法必须使用 throws 声明,否则编译会报错。
    • 如果抛出的是 非受检异常(如 RuntimeException),则不需要 throws 声明。

示例: 假设我们有一个方法,用于给用户设置年龄,年龄不能为负数。

throw与throws在异常处理中如何区分使用?-图3
(图片来源网络,侵删)
public class User {
    private String name;
    private int age;
    public void setAge(int age) {
        // 检查年龄是否合法
        if (age < 0) {
            // 手动创建并抛出一个 IllegalArgumentException
            // 这是一个非受检异常,所以方法签名不需要 throws
            throw new IllegalArgumentException("年龄不能为负数!");
        }
        this.age = age;
    }
}

如果调用者传入负数,就会触发这个异常:

public class Main {
    public static void main(String[] args) {
        User user = new User();
        user.setName("张三");
        try {
            user.setAge(-5); // 这里会抛出 IllegalArgumentException
            System.out.println("年龄设置成功。");
        } catch (IllegalArgumentException e) {
            System.out.println("捕获到异常: " + e.getMessage());
        }
    }
}
// 输出: 捕获到异常: 年龄不能为负数!

核心区别对比表

特性 throws throw
语法位置 方法签名末尾 方法体内部
关键字性质 关键字 关键字
作用 声明一个方法可能抛出的异常 执行一个抛出异常的动作
操作对象 是一个异常类型列表(如 IOException 是一个异常对象实例(如 new IOException()
编译器检查 用于受检异常,编译器会检查是否处理 抛出受检异常时,要求方法必须用 throws 声明
责任 将异常处理的责任转移给调用者 主动中断当前方法的执行,并向上传递异常
与异常的关系 是一种“预告”或“契约” 是一种“行动”或“事件”

两者结合使用的例子

这是最常见的情况:一个方法内部通过 throw 抛出一个受检异常,然后通过 throws 声明它。

public class BankAccount {
    private double balance;
    // 存款方法
    public void deposit(double amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("存款金额必须大于0");
        }
        this.balance += amount;
    }
    // 取款方法
    // 1. 方法内部可能会抛出受检异常
    // 2. 方法签名使用 throws 声明这个异常
    public void withdraw(double amount) throws InsufficientFundsException {
        if (amount > this.balance) {
            // 手动创建并抛出自定义的受检异常
            throw new InsufficientFundsException("余额不足,无法取款!");
        }
        this.balance -= amount;
    }
}
// 自定义一个受检异常
class InsufficientFundsException extends Exception {
    public InsufficientFundsException(String message) {
        super(message);
    }
}

调用方:

public class Main {
    public static void main(String[] args) {
        BankAccount account = new BankAccount();
        account.deposit(1000);
        try {
            account.withdraw(1500); // 这会触发 withdraw 方法中的 throw
            System.out.println("取款成功。");
        } catch (InsufficientFundsException e) {
            // 因为 withdraw 方法用 throws 声明了,所以这里必须处理
            System.out.println("取款失败: " + e.getMessage());
        }
    }
}
// 输出: 取款失败: 余额不足,无法取款!

在这个例子中:

  1. withdraw 方法内部使用 throw创建并抛出 InsufficientFundsException
  2. withdraw 方法在签名上使用 throws声明它可能会抛出这个异常。
  3. main 方法作为调用者,必须用 try-catch处理这个异常。

记住这个简单的类比:

  • throws 就像一份 “产品说明书”,告诉你这个方法(产品)可能会有什么风险(异常),但说明书本身不解决风险。
  • throw 就像实际 “按下警报按钮” 的动作,当某个条件满足时,你主动触发了警报(异常)。

理解了 throwthrows 的区别,你就掌握了 Java 异常处理的核心机制之一。

分享:
扫描分享到社交APP
上一篇
下一篇