使用 Java 构建 CLI

用 Java 标准能力构建可测试命令行程序:参数解析、退出码与可测核心逻辑。

#type / howto #status / growing #tech / dev #resource / java

[!info] 关联笔记

使用 Java 构建 CLI

目标

完成后应能:

  1. main 解析最少参数
  2. 把业务逻辑放在可单测函数
  3. 用退出码表达成功/失败
  4. 打包后可 java -jar 或模块方式运行(概念上)

这个场景为什么出现

运维脚本、数据修复、批处理入口常用 CLI。若逻辑全塞 main,无法测试;若无退出码,CI 无法判断成败。

[!abstract] 一句话理解 CLI = 参数解析 + 可测核心 + 明确退出码与错误输出;main 只做装配。

最小可运行示例

场景:库存工具计算补货数量

public class ReplenishCli {
    // core:纯函数,便于测试
    static int suggest(int onHand, int target) {
        if (onHand < 0 || target < 0) throw new IllegalArgumentException("negative");
        return Math.max(target - onHand, 0);
    }

    public static void main(String[] args) {
        if (args.length != 2) {
            System.err.println("usage: ReplenishCli <onHand> <target>");
            System.exit(2);
        }
        try {
            int onHand = Integer.parseInt(args[0]);
            int target = Integer.parseInt(args[1]);
            int need = suggest(onHand, target);
            System.out.println(need);
        } catch (Exception ex) {
            System.err.println("error: " + ex.getMessage());
            System.exit(1);
        }
    }
}
javac ReplenishCli.java
java ReplenishCli 3 10

期望输出:

7

结合场景再看三个关注点

  1. usage 错误用退出码 2 一类约定。
  2. 业务异常与解析异常进 stderr。
  3. 核心逻辑无 System.exit 以便测试。

核心步骤与准确模型

  1. 定义命令契约(参数、输出、退出码)
  2. 抽出纯逻辑
  3. main 解析/装配
  4. 测试逻辑
  5. 构建分发(Maven shade / jlink 等后置)

工程实践

  1. 复杂参数用 picocli 等库。
  2. 密码从 env/stdin 读,不进 argv 历史。
  3. 日志与人类输出分离。
  4. 幂等批处理要可重入。

本节总结

  • 可测核心 + 瘦 main
  • 退出码是接口
  • 为服务化批处理打基础

自测题

  1. 为什么不要在核心函数里 System.exit?
  2. stdout/stderr 如何分工?
参考答案
  1. 测试与复用会被强行终止进程。
  2. 正常结果 stdout;诊断/错误 stderr。

延伸阅读与资料来源

资料类型支撑内容
java launcher工具运行
picocli库文档复杂 CLI
创建于 2026/7/15 更新于 2026/7/15