String类
1.String、StringBuilder、StringBuffer
描述 | ||
---|---|---|
String | 不可变性 | string 是一个不可变类,每次 new 都会创建一个新的对象,存储在字符串常量池中 |
StringBuilder | 可变性 线程不安全 | 扩容 int newCapacity = (value.length << 1) + 2; |
StringBuffer | 可变性 线程安全 | 底层使用的 synchronized 实现线程安全 扩容 int newCapacity = (value.length << 1) + 2; |
String jdk 9 之前 底层是用 char value[],jdk 9 之后采用的是 byte value [] (减小空间的浪费);
2. 对象创建过程
由于字符串类比较特殊, 从JVM 的角度理解 String a = "张三" String a = new String("张三"),其本质上是虚拟机的规则。
局部变量存储在方法栈的局部表量表中,最终指向的是同一个引用地址,存储在字符串常量池中的地址
字符串常量池 : 不会存储相同内容的字符串 (底层实现HashTable ,默认长度60013,长度越小则,hash冲突越大会降低查询时间,降低效率)
3 string 操作
string a = “a”; string b = “a”; string a1 = new string("a");
代码 | 注释 |
---|---|
string a1 = new string("a"); | 堆空间中创建一个对象 |
string c = "a"+"b"; | 常量与常量的拼接直接在常量池中进行(编译器就已经完成) final 修饰的变量,虽然也是引用,但也是视为常量 |
string c1 = a + a1; | 变量参与的拼接是在堆中进行 (等价于在堆空间中新创建了一个对象) string c1 = new StringBuilder().append(a).append(a1).tostring(); |
string.intern() | 若常量池中存在则返回地址,否在常量池中创建一个并返回地址 |
从指令源码理解对象常见过程
String a = "ab"; | string a = new string("ab"); | string a = new string("a") + new string("b") | String a = new String("a") + new String("b"); | |
字节码指令 | 0 ldc #2 <ab> | 0 new #3 <java/lang/String> | 0 new #5 <java/lang/StringBuilder> | 0 new #5 <java/lang/StringBuilder> 3 dup 4 invokespecial #6 <java/lang/StringBuilder.<init>> 7 new #3 <java/lang/String> 10 dup 11 ldc #7 <a> 13 invokespecial #4 <java/lang/String.<init>> 16 invokevirtual #8 <java/lang/StringBuilder.append> 19 new #3 <java/lang/String> 22 dup 23 ldc #9 <b> 25 invokespecial #4 <java/lang/String.<init>> 28 invokevirtual #8 <java/lang/StringBuilder.append> 31 invokevirtual #10 <java/lang/StringBuilder.toString> 34 astore_1 35 aload_1 36 invokevirtual #11 <java/lang/String.intern> 39 pop 40 ldc #2 <ab> 42 astore_2 43 return |
解释 | 0 ldc #2 <ab> 直接从字符串常量池中获取 "ab" | 0 new #3 <java/lang/String> 首先创建对象 4 ldc #2 <ab> 直接从字符串常量池中获取 "ab" | 0 new #5 <java/lang/StringBuilder> 先创建的StringBuilder对象 11~28 依次创建 a 、b,中间采用append方法 31 invokevirtual #10 <java/lang/StringBuilder.toString> | a == b true |
重点
- invokevirtual #10 <java/lang/StringBuilder.toString> 这个步骤会先判断 new String ("ab"), 但是不会在字符串常量池中创建 “ab” .
- 只有手动调用 intern() 常量池才能在常量池创建,并返回该字符串的地址 (jdk6 | jdk7 字符串常量池存放的是堆空间字符串的地址,而不是创建字符串)
扩展
String a = new string("a") + new string("b");
String b = "ab";
String c = a.intern();
# a==b false
# b==c true