理解Java的字符串,String、StringBulider、StringBuffer 有什么区别?

典型回答

String 是Java语言非常基础和重要的类,提供了构造和管理字符串的各种基本逻辑。它是典型的Immutable类,被声明成为final class,所有属性也都是final的。由于它的不可变性,类似拼接,裁剪字符串等动作,都会产生新的String对象。由于字符串操作的普遍性,所以相关操作的效率往往对应用性能有明显影响。

StringBuffer 是为解决String操作产生太多中间对象的问题而提供的一个类,本质是一个线程安全的可修改字符序列。

StringBuilder 是Java1.5 中新增的,在能力上和 StringBuffer没有本质区别,但是它去掉了线程安全的部分,有效减小了开销,是绝大部分情况下进行字符串拼接的首选。

考点分析

基本的线程安全设计与实现,JVM对象缓存机制的理解,JVM优化Java代码的一些技巧,String池?

知识扩展

  1. 字符串设计与实现考量
  • Immutable类,原生的保证了基础线程安全,因为你无法对它内部数据进行任何修改,并且对象在拷贝时不需要额外复制数据。
  • StringBuffer的实现细节,通过把各种修改数据的方法都加上 synchronized 关键字实现,非常直白。“过早优化是万恶之源”,考虑可靠性、正确性和代码可读性才是大多是应用开发最重要的因素。
  1. /**
  2. * Constructs a string builder initialized to the contents of the
  3. * specified string. The initial capacity of the string builder is
  4. * {@code 16} plus the length of the string argument.
  5. *
  6. * @param str the initial contents of the buffer.
  7. */
  8. public StringBuilder(String str) {
  9. super(str.length() + 16);
  10. append(str);
  11. }
  • 内部char数组适当避免多次扩容的开销。扩容会产生多重开销,因为要抛弃原有数组,创建新的数组,还要进行arraycopy
  1. 字符串缓存

堆转储(Dump Heap),25%字符串,一半重复。

Java6以后,intern()方法,实现相应字符串缓存,PermGen,永久代,空间有限,容易OOM;
在后续版本中,缓存被放置在堆中,永久代在JDK8中被MetaSpace 元数据区替代了

  1. SymbolTable statistics:
  2. Number of buckets : 20011 = 160088 bytes, avg 8.000
  3. Number of entries : 14448 = 346752 bytes, avg 24.000
  4. Number of literals : 14448 = 619280 bytes, avg 42.863
  5. Total footprint : = 1126120 bytes
  6. Average bucket size : 0.722
  7. Variance of bucket size : 0.715
  8. Std. dev. of bucket size: 0.845
  9. Maximum bucket size : 6
  10. StringTable statistics:
  11. Number of buckets : 60013 = 480104 bytes, avg 8.000
  12. Number of entries : 2554 = 61296 bytes, avg 24.000
  13. Number of literals : 2554 = 215512 bytes, avg 84.382
  14. Total footprint : = 756912 bytes
  15. Average bucket size : 0.043
  16. Variance of bucket size : 0.043
  17. Std. dev. of bucket size: 0.206
  18. Maximum bucket size : 3

Intern是一种显式地排重机制,需要开发者写代码时明确调用,麻烦,污染代码,很难保证效率。
Oracle JDK 8u20 之后,新特性,G1 GC下的字符串排重,JVM底层的改变。
Intrinsic 机制

  1. String自身的演化

评论学习

String的创建机理
由于String在Java世界中使用过于频繁,Java为了避免在一个系统中产生大量的String对象,引入了字符串常量池。其运行机制是:创建一个字符串时,首先检查池中是否有值相同的字符串对象,如果有则不需要创建直接从池中刚查找到的对象引用;如果没有则新建字符串对象,返回对象引用,并且将新创建的对象放入池中。但是,通过new方法创建的String对象是不检查字符串池的,而是直接在堆区或栈区创建一个新的对象,也不会把对象放入池中。上述原则只适用于通过直接量给String对象引用赋值的情况。

举例:

  1. String str1 = "123"; //通过直接量赋值方式,放入字符串常量池
  2. String str2 = new String(“123”);//通过new方式赋值方式,不放入字符串常量池

:::info Java9 以后,对 字符串底层实现的优化 :::