skip to content

Search

String

5 min read Updated:
白墙与绿叶

String类的实现机制

不可变

String类的不可变体现在2点

  • String的值private final byte[] value通过private和final修饰
  • String类没有提供修改value的方法,类似修改的方法都是返回一个新的String对象

字符串常量池

一个JVM级别的哈希表,表中指向缓存堆中的唯一字符串对象

常量池中的字符串是唯一的,存储的是完整的String对象

进入常量池的方式

  • 源代码中的字面量"Hello"
  • 可计算的字符串表达式"Hel" + "lo",编译器会在编译期将其计算为"Hello",并放入常量池中
  • 显示调用intern()方法

new String()、StringBuilder.toString()、从文件/网络读取的字符串等所有运行时动态创建的对象不会自动进池

创建String

String s = "abc";
  • 若池中无"abc"
  1. 常量池中创建"abc"对象
  2. 返回对池中"abc"对象的引用
  • 若池中有"abc"

直接返回对池中"abc"对象的引用

String s = new String("abc");
  • 若池中无"abc"
  1. 常量池中创建"abc"对象
  2. 依据方法public String(String original)堆中新建一个abc对象
  3. 返回对堆中新建abc对象的引用
  • 若池中有"abc"
  1. 依据方法public String(String original)堆中新建一个abc对象
  2. 返回对堆中新建abc对象的引用
String s = new String(new char[]{'a','b','c'});

不在字符串常量池中创建对象,直接在堆中新建对象,返回引用

intern()

intern()返回字符串在常量池中的引用

在JDK7之后,如果常量池中没有该字符串,会直接把当前字符串对象的引用放入常量池中,而不是创建一个新的对象

如果池中已有内容相同的字符串,则直接返回池中已有引用;没有时才将当前对象放入

编译器常量折叠

仅由字面量和编译期常量变量组成的表达式,在编译时就会被计算成单一字面量,写入字节码

由此产生的字符串直接指向常量池中的同一个对象,不会在运行时生成 StringBuilder 拼接

常量变量定义:final 修饰,且使用常量表达式初始化

常量变量:

final String a = "hello";          // 字面量
final String b = a + " world";     // 常量表达式
final String c = "hello" + 1;      // 也是常量表达式

非常量变量:

final String d = new String("hello");    // new 不是常量表达式
final String e = someMethod();           // 方法调用不是常量表达式
final String f; f = "hello";             // 空 final

StringBuilder StringBuffer

StringBuilder和StringBuffer都是可变的字符序列,区别在于StringBuffer是线程安全的,StringBuilder是非线程安全的

执行String类拼接的时候,例如str1 + str2,编译器会将其转换为StringBuilder的拼接方式,调用StringBuilder.append()方法,最后调用StringBuilder.toString()方法返回一个新的String对象