1. 引言
简单来说,CharSequence
和String
是Java中两个不同的基础概念。本文将快速对比两者的区别,并说明何时使用它们。
2. CharSequence
CharSequence
是一个表示字符序列的接口。它不强制要求实现类是可变还是不可变,因此可变类(如StringBuilder
)和不可变类(如String
)都实现了这个接口。
作为接口,CharSequence
不能直接实例化,需要通过实现类来创建变量:
CharSequence charSequence = "baeldung";
这里charSequence
实际是String
类型。其他实现类的实例化方式:
CharSequence charSequence = new StringBuffer("baeldung");
CharSequence charSequence = new StringBuilder("baeldung");
3. String
String
是Java中表示字符序列的具体类。它是不可变的,也是Java中最常用的类型之一。该类实现了CharSequence
、Serializable
和Comparable<String>
接口。
当通过字面量创建String
时,JVM会执行字符串驻留操作:如果字符串常量池中已存在相同值,则直接复用;否则在池中创建新值。这意味着使用相同字面量创建的两个String
变量实际上是同一个对象:
String string1 = "baeldung";
String string2 = "baeldung";
assertEquals(string1, string2);
assertSame(string1, string2);
但若使用new String(...)
构造器创建,即使值相同,也会在堆上创建独立对象:
String string3 = new String("baeldung");
assertEquals(string1, string3);
assertNotSame(string1, string3);
虽然string3
和string1
值相同,但它们是两个不同的对象。
4. CharSequence
vs. String
对比两者的核心差异:
特性 | CharSequence |
String |
---|---|---|
类型 | 接口 | 具体类 |
可变性 | 不强制(可变/不可变) | 不可变 |
包位置 | java.lang |
java.lang |
比较能力 | 无内置比较策略 | 实现Comparable |
不可变性差异示例:
String
每次拼接都会创建新对象:@Test public void givenString_whenAppended_thenUnmodified() { String test = "a"; int firstAddressOfTest = System.identityHashCode(test); test += "b"; int secondAddressOfTest = System.identityHashCode(test); assertNotEquals(firstAddressOfTest, secondAddressOfTest); }
StringBuilder
直接修改原对象:@Test public void givenStringBuilder_whenAppended_thenModified() { StringBuilder test = new StringBuilder(); test.append("a"); int firstAddressOfTest = System.identityHashCode(test); test.append("b"); int secondAddressOfTest = System.identityHashCode(test); assertEquals(firstAddressOfTest, secondAddressOfTest); }
比较操作差异:
CharSequence
需先转为String
才能比较:
@Test
public void givenIdenticalCharSequences_whenCastToString_thenEqual() {
CharSequence charSeq1 = "baeldung_1";
CharSequence charSeq2 = "baeldung_2";
assertTrue(charSeq1.toString().compareTo(charSeq2.toString()) > 0);
}
5. CharSequence
转String
CharSequence
是接口,由String
、StringBuilder
、StringBuffer
等实现。转换时需注意实现类型,常见三种方式:
5.1. 类型转换
仅当确定CharSequence
是String
时可用:
@Test
public void givenCharSequenceAsString_whenConvertingUsingCasting_thenCorrect() {
String expected = "baeldung";
CharSequence charSequence = "baeldung";
String explicitCastedString = (String) charSequence;
assertEquals(expected, charSequence);
assertEquals(expected, explicitCastedString);
}
⚠️ 踩坑:若实际类型不是String
会抛异常:
@Test(expected = ClassCastException.class)
public void givenCharSequenceAsStringBuiler_whenConvertingUsingCasting_thenThrowException() {
CharSequence charSequence = new StringBuilder("baeldung");
String castedString = (String) charSequence;
}
5.2. toString()
方法
通用方案,但需处理null
:
@Test
public void givenCharSequence_whenConvertingUsingToString_thenCorrect() {
String expected = "baeldung";
CharSequence charSequence1 = "baeldung";
CharSequence charSequence2 = new StringBuilder("baeldung");
assertEquals(expected, charSequence1.toString());
assertEquals(expected, charSequence2.toString());
}
❌ 若CharSequence
为null
会抛NullPointerException
5.3. valueOf()
方法
最安全方案,自动处理null
:
@Test
public void givenCharSequence_whenConvertingUsingValueOf_thenCorrect() {
String expected = "baeldung";
CharSequence charSequence1 = "baeldung";
CharSequence charSequence2 = new StringBuilder("baeldung");
CharSequence charSequence3 = null;
assertEquals(expected, String.valueOf(charSequence1));
assertEquals(expected, String.valueOf(charSequence2));
assertEquals("null", String.valueOf(charSequence3));
}
底层逻辑:非null
时调用toString()
,null
时返回字符串"null"
。
6. 总结
- ✅ **默认用
String
**:不确定类型时优先选择 - ✅ 需要可变操作时:用
StringBuilder
(单线程)或StringBuffer
(多线程) - ✅ 方法参数设计:用
CharSequence
提高灵活性,但内部处理需注意类型转换