String.format() 必须显式指定参数索引(如%1$s)或严格按顺序匹配,否则报错;支持%d、%s等转换符但类型检查更严,null转为字符串"null",不支持%u/%p,推荐预处理null并避免循环中滥用。

String.format() 的基本语法和占位符怎么写
Java 的 String.format() 和 C 的 printf 行为接近,但占位符不是 %d、%s 这种裸用形式,必须带索引或按顺序匹配,否则运行时报 IllegalFormatConversionException 或 MismatchedFormatter。
最安全的写法是显式指定参数索引,比如:String.format("用户 %1$s 年龄 %2$d", name, age)。其中 %1$s 表示“取第 1 个参数(name),按字符串格式化”;%2$d 表示“取第 2 个参数(age),按十进制整数格式化”。
- 不写索引(如
%s)也可以,但要求参数顺序严格对应,一旦错位就出错 -
s(字符串)、d(十进制整数)、f(浮点数)、x(十六进制)这些转换符和 C 一致,但 Java 不支持%u或%p - 宽度、精度等修饰符写法相同,例如
%5s表示最小宽度 5,%.2f表示保留两位小数
为什么直接套用 C 的 printf 风格会报错
常见错误是把 C 代码原样搬过来:比如写成 String.format("value = %d, str = %s", 42, "hello") 看似没问题,但一旦传入 null 字符串,就会抛 java.util.FormatFlagsConversionMismatchException 或静默变成 "null" —— 而 C 的 printf 遇到 NULL 指针通常崩溃或打印 (null)。
更隐蔽的问题是类型不匹配:C 中 %d 打印 long 可能只截低 32 位,而 Java 中 long 必须用 %d(自动适配)或显式用 %ld(其实 %ld 是冗余的,Java 不区分 int/long 的格式符),但若误用 %d 格式化 float,则直接抛异常。
立即学习“Java免费学习笔记(深入)”;
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- Java 对类型检查更严格,
%d只接受byte、short、int、long、BigInteger,不接受float或包装类Integer以外的其他数字包装类(如AtomicInteger会失败) -
null值传给%s会被转成字符串"null",不是空字符串,也不是抛 NPE —— 这和很多人的直觉相反 - 没有
%n以外的换行符支持,\n是平台相关,%n才是跨平台换行
如何安全处理 null 和可选字段
Java 没有 C 那样的宏或可变参数预处理,所以不能靠编译期跳过 null 参数。实际中常需要手动兜底,否则日志或拼接字符串时突然冒出 "null" 很难排查。
推荐在调用 String.format() 前做一次预处理,用三元表达式或 Objects.toString():
String name = null;
String msg = String.format("用户: %s, ID: %d",
Objects.toString(name, "(未命名)"),
userId);
- 不要依赖
String.format()自动判空,它不提供默认值机制 - 如果字段可能为 null 且业务上不允许显示
"null",必须在外层处理,比如用Optional.ofNullable(name).orElse("(缺省)") - 对集合或数组,
%s会调用toString(),结果可能是[Ljava.lang.String;@1b6d3586这种,需先转成可读格式(如Arrays.toString(arr))再传入
性能和替代方案要注意什么
每次调用 String.format() 都会解析格式串、创建内部 Formatter 实例、做类型校验和字符串拼接,比直接用 + 或 StringBuilder 慢 3–5 倍(尤其在循环内)。简单拼接别硬套 format。
- 日志场景优先用 SLF4J 的
logger.info("user {} age {}", name, age),它底层惰性格式化,不触发 GC - Android 开发中,
String.format()在低版本有兼容问题(如 API 16 下不支持%,d千分位),需加@TargetApi或降级处理 - 如果格式串固定、参数常变,可考虑预编译:Apache Commons Lang 的
StrSubstitutor或自己缓存MessageFormat实例(但注意线程安全)
真正要“像 C 那样写”,关键不是语法多像,而是理解 Java 的类型边界和 null 处理逻辑 —— 否则格式串看着对,一跑就崩。

















