StringBuilder.substring()StringBuilder 类中用于提取子字符串的方法。它允许你从可变字符序列中截取一部分内容,生成一个不可变的 String 对象。

一、方法定义

StringBuilder 提供了两个重载的 substring() 方法:

方法 1:substring(int start)

public String substring(int start)
  • 功能:从索引 start 开始,截取到字符串末尾。
  • 参数start - 起始索引(包含),从 0 开始。
  • 返回值:一个新的 String 对象,包含从 start 到末尾的字符。
  • 异常StringIndexOutOfBoundsException - 如果 start < 0start > length()

方法 2:substring(int start, int end)

public String substring(int start, int end)
  • 功能:截取从索引 startend-1 的字符(左闭右开区间)。
  • 参数
    • start - 起始索引(包含)。
    • end - 结束索引(不包含)。
  • 返回值:一个新的 String 对象,包含 [start, end) 范围内的字符。
  • 异常
    • StringIndexOutOfBoundsException - 如果 start < 0end < 0start > endend > length()

重要区别StringBuilder.substring() 返回 String,而 StringBuilder 的其他方法(如 appendreplace)返回 StringBuilder 本身。


二、功能说明

  • 提取子串:从 StringBuilder 的当前内容中提取指定范围的字符。
  • 不可变结果:返回的是 String 对象,内容不可再修改。
  • 不修改原对象:调用 substring() 不会改变 StringBuilder 本身的值。
  • 左闭右开区间end 索引不包含在结果中,即 [start, end)
  • 支持边界情况
    • start == end → 返回空字符串 ""
    • start == length() → 返回空字符串 ""(仅 substring(start))。

三、示例代码

示例 1:substring(int start) 基本用法

StringBuilder sb = new StringBuilder("Hello World");
String sub1 = sb.substring(6); // 从索引 6 开始
System.out.println(sub1); // 输出:World
// 索引:H(0) e(1) l(2) l(3) o(4)  (5) W(6) o(7) r(8) l(9) d(10)

示例 2:substring(int start, int end) 基本用法

StringBuilder sb = new StringBuilder("Hello World");
String sub2 = sb.substring(0, 5); // [0,5) → 索引 0,1,2,3,4
System.out.println(sub2); // 输出:Hello

示例 3:提取中间部分

StringBuilder sb = new StringBuilder("User: Alice, Age: 25");
String name = sb.substring(6, 11); // [6,11) → "Alice"
String ageStr = sb.substring(18, 20); // [18,20) → "25"
System.out.println("Name: " + name + ", Age: " + ageStr);
// 输出:Name: Alice, Age: 25

示例 4:边界情况

StringBuilder sb = new StringBuilder("Test");

// start == length()
System.out.println(sb.substring(4)); // 输出:""(空字符串)

// start == end
System.out.println(sb.substring(2, 2)); // 输出:""(空字符串)

// end == length()
System.out.println(sb.substring(1, 4)); // 输出:"est"

示例 5:substring() 不修改原对象

StringBuilder sb = new StringBuilder("Original");
String sub = sb.substring(0, 3);
System.out.println("Substring: " + sub); // 输出:Ori
System.out.println("StringBuilder: " + sb.toString()); // 输出:Original(未变)

示例 6:链式调用(注意返回类型)

StringBuilder sb = new StringBuilder("Hello");
// ❌ 错误:substring() 返回 String,没有 append() 方法
// sb.substring(0, 3).append("XXX"); // 编译错误!

// ✅ 正确:先操作 StringBuilder,再取子串
String result = sb.append(" World").substring(6, 11); // "World"
System.out.println(result);

四、使用技巧

技巧 1:快速提取前缀或后缀

StringBuilder sb = new StringBuilder("prefix_data_suffix");

// 提取前缀
String prefix = sb.substring(0, 7); // "prefix_"

// 提取后缀(结合 length)
int len = sb.length();
String suffix = sb.substring(len - 7); // "suffix"(最后 7 个字符)

技巧 2:与 indexOf() 配合提取关键字

StringBuilder sb = new StringBuilder("Error: NULL_POINTER, Code: E001");
int start = sb.indexOf("Code: ") + 6; // 跳过 "Code: "
int end = sb.indexOf(",", start); // 查找下一个逗号
if (end == -1) end = sb.length(); // 如果没有逗号,则到末尾

String errorCode = sb.substring(start, end);
System.out.println("Error Code: " + errorCode); // 输出:E001

技巧 3:安全提取(避免异常)

public static String safeSubstring(StringBuilder sb, int start, int end) {
    int len = sb.length();
    // 边界修正
    start = Math.max(0, Math.min(start, len));
    end = Math.max(start, Math.min(end, len));
    return sb.substring(start, end);
}

// 使用
StringBuilder sb = new StringBuilder("Hi");
System.out.println(safeSubstring(sb, 0, 10)); // 输出:"Hi"(自动修正 end=2)

技巧 4:提取固定长度字段

// 假设格式:ID(4位)Name(10位)Age(3位)
StringBuilder sb = new StringBuilder("0001Alice     25");
String id = sb.substring(0, 4);     // "0001"
String name = sb.substring(4, 14);  // "Alice     "
String age = sb.substring(14, 17);  // "25"

五、常见错误

错误 1:索引越界

StringBuilder sb = new StringBuilder("Hi");
String sub = sb.substring(5); 
// 抛出 StringIndexOutOfBoundsException
// 原因:start=5 > length()=2

错误 2:end < start

String sub = sb.substring(3, 1); 
// 抛出 StringIndexOutOfBoundsException

错误 3:混淆“包含”与“不包含”

StringBuilder sb = new StringBuilder("Hello");
// 想提取 "llo"(索引 2,3,4),错误写法:
String sub = sb.substring(2, 4); // 只取索引 2,3 → "ll"
// 正确写法:
String correct = sb.substring(2, 5); // 取索引 2,3,4 → "llo"

错误 4:误以为修改了原对象

StringBuilder sb = new StringBuilder("Original");
sb.substring(0, 3); // 返回 "Ori",但 sb 仍是 "Original"
// 如果想修改 sb,需用 replace/delete 等方法

六、注意事项

  1. 返回 Stringsubstring() 返回的是新的 String 对象,不是 StringBuilder
  2. 左闭右开end 索引不包含在结果中,务必注意。
  3. 不修改原对象StringBuilder 本身内容不变。
  4. 性能:创建新 String 对象,涉及内存分配和字符复制。
  5. 共享字符数组?:与 Stringsubstring() 不同,StringBuilder.substring() 不会共享内部字符数组,而是创建新数组复制数据(JDK 7+ 行为)。

七、最佳实践

1. 验证索引有效性

int len = sb.length();
if (start >= 0 && start <= len && end >= start && end <= len) {
    String result = sb.substring(start, end);
} else {
    // 处理边界情况或抛出自定义异常
}

2. 避免不必要的 substring()

  • 如果只是想查看部分内容,考虑直接操作 StringBuilder
  • 频繁提取可能影响性能,评估是否需要缓存结果。

3. 使用 safeSubstring 工具方法

  • 在不确定索引时,使用封装的边界安全方法。

4. 理解与 String.substring() 的区别

  • String.substring()(JDK 6)曾共享数组导致内存泄漏,JDK 7+ 已修复。
  • StringBuilder.substring() 始终复制数据,无共享风险。

八、总结

要点 说明
核心作用 StringBuilder 中提取子字符串
方法重载 substring(start)substring(start, end)
返回类型 String(新对象,不可变)
关键规则 左闭右开区间 [start, end)
不修改原对象 StringBuilder 内容保持不变
常见错误 索引越界、混淆包含性、误以为修改原对象
最佳实践 验证索引、安全提取、理解性能开销

最终建议

StringBuilder.substring() 是一个安全、直观的子串提取工具。掌握其“左闭右开”的索引规则是避免错误的关键。在实际开发中,常用于解析固定格式字符串、提取关键字、截取前后缀等场景。记住:它只读取内容,不修改 StringBuilder。结合 indexOf() 和边界检查,即可高效、安全地处理各种文本提取需求。