理解Java的字符串,String、StringBulider、StringBuffer 有什么区别?
典型回答
String
是Java语言非常基础和重要的类,提供了构造和管理字符串的各种基本逻辑。它是典型的Immutable类,被声明成为final class,所有属性也都是final的。由于它的不可变性,类似拼接,裁剪字符串等动作,都会产生新的String对象。由于字符串操作的普遍性,所以相关操作的效率往往对应用性能有明显影响。
StringBuffer
是为解决String操作产生太多中间对象的问题而提供的一个类,本质是一个线程安全的可修改字符序列。
StringBuilder
是Java1.5 中新增的,在能力上和 StringBuffer没有本质区别,但是它去掉了线程安全的部分,有效减小了开销,是绝大部分情况下进行字符串拼接的首选。
考点分析
基本的线程安全设计与实现,JVM对象缓存机制的理解,JVM优化Java代码的一些技巧,String池?
知识扩展
- 字符串设计与实现考量
- Immutable类,原生的保证了基础线程安全,因为你无法对它内部数据进行任何修改,并且对象在拷贝时不需要额外复制数据。
- StringBuffer的实现细节,通过把各种修改数据的方法都加上
synchronized
关键字实现,非常直白。“过早优化是万恶之源”,考虑可靠性、正确性和代码可读性才是大多是应用开发最重要的因素。
/**
* Constructs a string builder initialized to the contents of the
* specified string. The initial capacity of the string builder is
* {@code 16} plus the length of the string argument.
*
* @param str the initial contents of the buffer.
*/
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
- 内部char数组适当避免多次扩容的开销。扩容会产生多重开销,因为要抛弃原有数组,创建新的数组,还要进行arraycopy
- 字符串缓存
堆转储(Dump Heap),25%字符串,一半重复。
Java6以后,intern()
方法,实现相应字符串缓存,PermGen,永久代,空间有限,容易OOM;
在后续版本中,缓存被放置在堆中,永久代在JDK8中被MetaSpace
元数据区替代了
SymbolTable statistics:
Number of buckets : 20011 = 160088 bytes, avg 8.000
Number of entries : 14448 = 346752 bytes, avg 24.000
Number of literals : 14448 = 619280 bytes, avg 42.863
Total footprint : = 1126120 bytes
Average bucket size : 0.722
Variance of bucket size : 0.715
Std. dev. of bucket size: 0.845
Maximum bucket size : 6
StringTable statistics:
Number of buckets : 60013 = 480104 bytes, avg 8.000
Number of entries : 2554 = 61296 bytes, avg 24.000
Number of literals : 2554 = 215512 bytes, avg 84.382
Total footprint : = 756912 bytes
Average bucket size : 0.043
Variance of bucket size : 0.043
Std. dev. of bucket size: 0.206
Maximum bucket size : 3
Intern是一种显式地排重机制,需要开发者写代码时明确调用,麻烦,污染代码,很难保证效率。
Oracle JDK 8u20 之后,新特性,G1 GC下的字符串排重,JVM底层的改变。Intrinsic
机制
- String自身的演化
评论学习
String的创建机理
由于String在Java世界中使用过于频繁,Java为了避免在一个系统中产生大量的String对象,引入了字符串常量池。其运行机制是:创建一个字符串时,首先检查池中是否有值相同的字符串对象,如果有则不需要创建直接从池中刚查找到的对象引用;如果没有则新建字符串对象,返回对象引用,并且将新创建的对象放入池中。但是,通过new方法创建的String对象是不检查字符串池的,而是直接在堆区或栈区创建一个新的对象,也不会把对象放入池中。上述原则只适用于通过直接量给String对象引用赋值的情况。
举例:
String str1 = "123"; //通过直接量赋值方式,放入字符串常量池
String str2 = new String(“123”);//通过new方式赋值方式,不放入字符串常量池
:::info Java9 以后,对 字符串底层实现的优化 :::