Java String不可变是为了字符串常量池、线程安全和哈希缓存优化,每次操作如substring()都新建对象;频繁拼接导致GC压力大,比较必须用equals();可变场景应选StringBuilder或StringBuffer。

String 为什么不可变,以及它如何影响日常操作
Java 的 String 不可变不是设计癖好,而是为了字符串常量池、线程安全和哈希缓存优化。你每次调用 substring()、toLowerCase() 或 replace(),其实都新建了一个对象——原字符串毫发无损。
这直接导致两个常见问题:
- 频繁拼接(如循环中
str += "a")会生成大量中间对象,GC 压力陡增 -
==比较字符串内容几乎总是错的,必须用.equals()
如果真需要可变字符串,用 StringBuilder(单线程)或 StringBuffer(线程安全但慢),别硬扛 String。
Apache Commons Lang 的 StringUtils 解决哪些原生痛点
原生 String API 缺少空安全、批量处理和语义明确的工具方法。StringUtils(来自 commons-lang3)补上了这些缺口,比如:
立即学习“Java免费学习笔记(深入)”;
-
StringUtils.isBlank(str)同时判断null、空串、纯空白(比str == null || str.trim().isEmpty()简洁且不抛 NPE) -
StringUtils.defaultString(str, "default")替代三元表达式str != null ? str : "default" -
StringUtils.split("a,b,c", ",")返回String[],天然跳过空项,而"a,,c".split(",")会返回长度为 3 的数组(含空字符串)
注意:它不处理 Unicode 边界(如代理对),做国际化文本处理仍需 java.text.BreakIterator。
Java 11+ 新增的 String 实用方法怎么用才不踩坑
Java 11 引入了几个高频需求方法,但部分行为容易误解:
-
str.strip()是按 Unicode 空白字符裁剪(等价于trim()的升级版),而str.stripLeading()和str.stripTrailing()才分别对应左右裁剪;trim()只识别 ASCII 空格(U+0020)及少数控制符,遇到\u2000(EN QUAD)会失效 -
str.repeat(3)很方便,但如果传负数会直接抛IllegalArgumentException,不静默返回空串 -
str.lines()返回Stream,按平台无关换行符(\r\n、\n、\r)切分,但注意它**不会自动关闭流**——在 try-with-resources 中使用,否则可能泄漏资源
try (Streamlines = text.lines()) { lines.filter(s -> !s.isBlank()).forEach(System.out::println); }
正则替换与性能敏感场景该选哪个类
做模式匹配或替换时,别只盯着 String.replaceAll()。它每次调用都会编译正则,反复使用同一模式时开销明显:
- 单次操作:用
str.replaceAll("a+", "X")最简 - 高频复用:预编译
Pattern.compile("a+"),再调用pattern.matcher(str).replaceAll("X") - 纯字面量替换(无正则元字符):用
str.replace("a", "X"),比replaceAll()快 3–5 倍,因为它走的是朴素子串搜索,不进正则引擎
String 类本身没提供“替换前 n 次”的方法,得靠 Matcher.replaceFirst() 或手动迭代 Matcher.find() —— 这种细节,StringUtils.replaceOnce() 已帮你封装好。










