社交媒体

自定义异常

自定义异常

钩子:Java 不懂你的业务

Java 内置异常一大堆,但没有一个叫"年龄不合法",也没有一个叫"余额不足"。

于是新手常写:

if (age < 0) {
    throw new IllegalArgumentException("年龄不能为负数");
}

能用,但有两个代价:调用方 catch (IllegalArgumentException) 时,"参数传错"和"年龄规则错"混在一起,想单独处理"年龄问题"做不到;报错信息全靠手拼字符串,东一处西一处,格式还不统一。

自定义异常,就是用 Java 的异常机制说自己的业务黑话。

核心:3 行造一个异常

异常本质也是个类,继承一下就有了:

// 运行时异常款(不强制 try,适合"编程错误"类)
class AgeOutOfBoundsException extends RuntimeException {
    public AgeOutOfBoundsException(String message) {
        super(message);   // 把信息交给父类,getMessage() 才能取到
    }
}

用起来:

static void setAge(int age) {
    if (age < 0 || age > 150) {
        throw new AgeOutOfBoundsException("年龄不能为负数或超 150,收到 " + age);
    }
}

调用方就可以精准捕获这一种业务问题:

try {
    setAge(-5);
} catch (AgeOutOfBoundsException e) {
    System.out.println(e.getMessage());   // 年龄不能为负数或超 150,收到 -5
}

31-自定义异常抛接流程图-bjOm.png

进阶:异常对象还能带数据

这是"会说话"的完全体——异常不只是字符串,它是个正经对象,想带什么字段都行:

class BalanceNotEnoughException extends Exception {   // 受检款
    private final double shortage;                    // 缺多少钱

    public BalanceNotEnoughException(double shortage) {
        super("余额不足,还差 " + shortage + " 元");
        this.shortage = shortage;
    }
    public double getShortage() { return shortage; }
}

// 调用方拿到差价,直接弹充值提示:
catch (BalanceNotEnoughException e) {
    showRechargeDialog(e.getShortage());
}

报错和业务数据一起送达,调用方拿到手就能用——这才是异常机制的正确打开方式。

决策:继承谁?什么时候用?

回顾第 28 篇的判断标准,自定义异常完全通用:

  • 继承 RuntimeException:编程错误类(参数传错、状态不对)——编译器不强制,别到处 try,改代码根治;
  • 继承 Exception(受检):调用方必须面对的业务分支(余额不足、文件不存在)——编译器逼着处理,处理方式可能就是"提示用户充值"。

顺手厘清一对万年混淆:

  • throw 是动作,在方法体内真正抛出一个异常对象;
  • throws 是声明,写在方法签名上,提前打招呼"我这方法可能炸"。

速查表

要素 写法 记忆点
定义 class XxxException extends RuntimeException 3 行:继承 + 构造 + super(message)
抛出 throw new XxxException("人话") throw 后面跟对象
声明 void pay(...) throws BalanceNotEnoughException throws 后面跟类型
带数据 自定义字段 + getter 异常对象 = 会说话的数据包
选型 编程错误→RuntimeException;业务分支→Exception 同第 28 篇标准

本文示例代码均已本地实测通过,可直接运行。
对应文件:CustomExceptionDemo.java


橙码酱,分享更有用的技术知识,助你成为更优秀的开发者。