使用 Java 构建 CLI
用 Java 标准能力构建可测试命令行程序:参数解析、退出码与可测核心逻辑。
#type / howto
#status / growing
#tech / dev
#resource / java
[!info] 关联笔记
使用 Java 构建 CLI
目标
完成后应能:
- 用
main解析最少参数 - 把业务逻辑放在可单测函数
- 用退出码表达成功/失败
- 打包后可
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
结合场景再看三个关注点
- usage 错误用退出码 2 一类约定。
- 业务异常与解析异常进 stderr。
- 核心逻辑无 System.exit 以便测试。
核心步骤与准确模型
- 定义命令契约(参数、输出、退出码)
- 抽出纯逻辑
- main 解析/装配
- 测试逻辑
- 构建分发(Maven shade / jlink 等后置)
工程实践
- 复杂参数用 picocli 等库。
- 密码从 env/stdin 读,不进 argv 历史。
- 日志与人类输出分离。
- 幂等批处理要可重入。
本节总结
- 可测核心 + 瘦 main
- 退出码是接口
- 为服务化批处理打基础
自测题
- 为什么不要在核心函数里 System.exit?
- stdout/stderr 如何分工?
参考答案
- 测试与复用会被强行终止进程。
- 正常结果 stdout;诊断/错误 stderr。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| java launcher | 工具 | 运行 |
| picocli | 库文档 | 复杂 CLI |