CharSequence 是只读字符序列接口,定义 length()、charAt()、subSequence() 三个方法;String 是不可变实现,StringBuilder 是可变实现,二者语义不同但均可传入期望 CharSequence 的 API,提升灵活性。

CharSequence 是 Java 中对“字符序列”这一概念的统一抽象,它不关心具体实现是不可变还是可变、是否线程安全、底层用什么结构存储——只定义了最基础的读取能力。String 和 StringBuilder 都实现了这个接口,但语义和行为截然不同:一个代表“不可变的文本值”,一个代表“可编辑的文本容器”。这种抽象让很多 API 可以同时接受两者,提升灵活性,也暴露了设计上关键的分层逻辑。
CharSequence 接口只承诺“能读”,不承诺“能改”
它仅规定三个核心方法:
- length():返回字符个数
- charAt(int index):按索引取单个字符
- subSequence(int start, int end):截取子序列(返回新的 CharSequence)
这些方法全是只读的。String、StringBuilder、StringBuffer 都必须提供这些能力,但各自返回的对象类型可能不同——String 的 subSequence 返回 String,StringBuilder 的则返回自身类型的 CharSequence(实际是 StringBuilder 实例)。
String 是 CharSequence 的“不可变实现”
String 类被声明为 final,内部 char[](JDK 9+ 后是 byte[] + coder)也是 private final。这意味着:
- 任何看似“修改”的操作(如 substring、toUpperCase、replace)都返回新 String 对象
- 传入 CharSequence 参数的方法里,你无法通过该引用改变原始 String 内容
- 适合做 key、缓存、配置项等需要稳定性的场景
StringBuilder 是 CharSequence 的“可变实现”
它继承自 AbstractStringBuilder,内部 char[] 是普通数组(非 final),所有 append、insert、delete 等操作直接修改 value 数组内容:
- 同一个 StringBuilder 实例多次调用 append(),对象地址不变,内容就地更新
- 虽然它实现了 CharSequence,但它的 subSequence 返回的是一个新的 CharSequence 视图(仍指向原数组,但带 offset/length 限定)
- 不能直接用作 Map key 或长期缓存,除非明确调用 toString() 固化成 String
为什么 API 常用 CharSequence 而不是 String?
这是为了放宽参数约束,避免无谓的类型转换:
- 比如 String.valueOf(CharSequence cs) 可接收 String、StringBuilder、甚至自定义的 CharSequence 实现
- CharSequence cs = new StringBuilder("abc") 合法,后续可传给任意期望 CharSequence 的方法
- 若强制要求 String,调用方就得写 sb.toString(),哪怕只是临时读取 length 或 charAt —— 白费一次对象创建

















