Java substring需确保起始索引≥0、结束索引≤字符串长度,单参数截取至末尾,双参数左闭右开;处理emoji等需用codePointCount避免代理对截断;Java 7u6后无内存泄漏。

substring 两个参数怎么选才不越界
Java 的 substring 不会自动帮你校验索引,越界直接抛 StringIndexOutOfBoundsException。常见错误是把“截取前3个字符”写成 substring(0, 3) —— 这其实是对的;但换成“从第3个字符开始截到末尾”,有人写 substring(3, str.length()),万一字符串只有2个字符就崩了。
安全做法是先判断长度:
- 用
str.length()拿总长,再和起始/结束位置比大小 - 起始索引必须 ≥ 0,结束索引必须 ≤ 字符串长度
- 如果不确定长度,用
Math.min(3, str.length())替代硬写数字
substring(1) 和 substring(1, 3) 的行为差异
单参数版本 substring(int beginIndex) 是“从指定位置到结尾”,双参数版本 substring(int beginIndex, int endIndex) 是“左闭右开”——endIndex 本身不包含在结果里。这是最容易混淆的点。
比如 "abcde".substring(1, 3) 返回 "bc",不是 "bcd";而 "abcde".substring(1) 返回 "bcde"。
立即学习“Java免费学习笔记(深入)”;
- 记住口诀:“起始含,结束不含”
- 如果想取第 n 个字符(从1数),用
substring(n-1, n) - 空字符串调用
substring(0)没问题,但substring(1)就会报错
中文、emoji 字符下 substring 会不会乱码
不会乱码,但可能切错“字”。Java 的 String 基于 UTF-16 编码,一个 emoji(如 ?)或某些生僻汉字占两个 char(即一个代理对)。substring 按 char 索引切,不是按“用户感知的字符”切。
例如 "a?b".length() 是 4('a'=1, ?=2, 'b'=1),substring(1, 3) 会返回代理对的前半部分,打印出来可能是 或其他异常符号。
- 真要按“人眼字符”截取,得用
String.codePointCount()+offsetByCodePoints() - 日常处理 ASCII 文本或已知无 emoji 场景,直接用
substring完全够用 - 日志里看到 ,先检查是不是
substring切进了代理对中间
性能上,substring 在 Java 7u6 之后还有没有内存泄漏风险
没有。老版本(Java 6 及之前)的 substring 会共享原字符串的 char[],导致小子串持大数组不释放;但从 Java 7u6 开始,每次调用都生成新数组,内存干净,但代价是多一次数组复制。
- 频繁截取超长字符串的某一段?考虑用
CharBuffer.wrap(str).subSequence(...).toString()避免复制(不过多数场景没必要) - 别再为“避免 substring 导致内存泄漏”去手写循环拼接,那是过时经验
- 用 JFR 或 VisualVM 看堆里字符串数组大小,比凭经验猜更靠谱
最常被忽略的是:你以为在切字符串,其实对象本身可能已被 intern 或缓存,真正影响内存的是引用链,不是 substring 这一行代码。











