1. 引言

简单来说,CharSequenceString是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中最常用的类型之一。该类实现了CharSequenceSerializableComparable<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);

虽然string3string1值相同,但它们是两个不同的对象。

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. CharSequenceString

CharSequence是接口,由StringStringBuilderStringBuffer等实现。转换时需注意实现类型,常见三种方式:

5.1. 类型转换

仅当确定CharSequenceString时可用:

@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());
}

❌ 若CharSequencenull会抛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提高灵活性,但内部处理需注意类型转换

完整示例代码见GitHub仓库,更多细节参考JavaDoc


原始标题:CharSequence vs. String in Java | Baeldung