java虽无原生契约语法,但可通过注释规范、assert断言和工具类(如guava preconditions)在方法层面表达输入输出约束:注释需明确参数业务含义、成功副作用与失败行为;assert用于开发阶段自检内部状态;生产校验应使用preconditions等显式工具。

Java 本身不提供原生契约语法,但可以通过注释规范 + 断言(assert)+ 工具辅助,在方法层面清晰表达输入输出约束。关键不是堆砌检查,而是让约束可读、可验、可维护。
用注释写明契约语义,而非仅描述功能
注释要承担“轻量契约文档”角色,明确回答三个问题:参数代表什么业务含义?成功时保证什么副作用?失败时如何表现?
- 避免模糊描述如“校验用户参数”,改写为:“@param userId 必须为正整数,对应数据库中已激活的用户ID;若为0或负数,抛IllegalArgumentException”
- 注明成功路径的可观测结果:“@return 返回非空字符串,格式为 'U-{id}-{timestamp}',且该ID已在用户表中标记为‘已登录’状态”
- 失败行为需确定:“@throws IllegalStateException 当账户处于冻结状态时抛出,此时余额字段值保持不变”
断言只用于开发/测试阶段的内部契约验证
assert 不是生产环境的校验主力,而是开发者自检工具——它应在编码初期就写入,用于暴露调用方误用,而非替代参数校验。
- 启用方式:运行时加
-ea(-enableassertions),未启用时断言完全不执行,无性能开销 - 适合场景:检查私有方法的隐含假设、对象内部状态一致性、算法中间结果
- 示例:
assert balance >= 0 : "withdraw后余额违反不变式,当前值=" + balance;(放在方法末尾,验证对象状态)
用工具类把前置条件显式化,兼顾可读性与生产可用性
真正需要在生产环境生效的输入校验,推荐使用 Guava 的 Preconditions 或 Spring 的 @Valid,它们语义清晰、异常明确、无需手动启用。
Preconditions.checkArgument(amount > 0, "取款金额必须大于0,传入值:%s", amount);Preconditions.checkNotNull(user, "用户对象不可为空,调用方需确保登录态有效");- 这类调用既是运行时检查,也是代码即文档——方法签名+校验语句共同构成契约
后置条件与不变式要绑定到具体状态点
后置条件不是泛泛而谈“返回值正确”,而是针对方法执行后**立即可见的状态变化**做断言。
- 例如转账方法执行后:
assert fromAccount.getBalance() == originalFrom - amount : "转出账户余额未扣减"; - 不变式适用于整个对象生命周期:
private void checkInvariant() { assert id > 0 && name != null; },并在所有 public 方法前后调用 - 注意:不变式检查应尽量轻量,避免在高频方法中引入可观测延迟
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











