1. String的基本特性
String
:字符串,使用一对""
进行表示
String s1 = "atguigu";//字面量定义方式
String s2 = new String("");
String
是不可被继承的,设置为final
, 其底层是char[]
,而在JDK1.9
之后,使用了byte[]
数组
为什么要修改成
byte[]
数组?这是为了更好的节省空间,当使用char[]
数组的时候,一个char
就是16
个bit,而言对于大部分的拉丁文字来说,他们只需要1个字节就能存储,因此为了因地制宜地使用这个字符编码
,采用byte[]
来存储原始数据,当需要解析数据的时候才使用具体的字符解码规则。
如何来看这个表?ldc #2
,他说的是要到常量池中查找#2
的这个信息,记载好信息之后,将这个信息加载到s3
变量中去,它类似于一个槽,可以将一个变量存到这个槽中。
这就是运行时常量池的样子,可与会看到,此时a,b,ab都是常量池中的符号
当执行代码ldc #2
的时候,就会将这个符号变为一个对象,StringTable[]
此时会去检查StringTable[]
是否有这个对象,如果没有这个对象,那么就会将a
加入这个table
,也就是StringTable["a"]
因此,可以看出不是一开始就将这个字符串放入这个
StringTable
的,而是在执行到相应的代码之后,才会将这个字符串加载到StringTable
中去,有一种懒加载的思想,称为字符串的的延迟加载
String s1 = "a";
String s2 = "b";
String s3 = "ab";
String s4 = s1+s2;//new StringBuilder().append("a").append("b").toString(),它就相当于new String("ab"),这个对象并不会加入到常量池中,相当于是动态拼接的
String s5 = "a" + "b";
关于s5
的结果,要从编译优化的结果来看,s5
所产生的结果是"a"+"b"
,编译器在读取到这个式子的时候,发现都是常量的加减,因此就会在编译期间将这个结果修改为"ab"
,那么你就会发现,由于之前执行ldc #3
的时候,就已经将ab
加入了常量池,那么在s5
的时候,它就不会再去创建新对象,而是将之前加入了Table
中对象拿过来使用
关于StringTable
常量池中的字符串仅仅只是符号,在第一次使用的时候才变为对象
利用串池的机制,来避免重复地创建字符串对象
字符串变量拼接的原理是StringBuilder
字符串常量拼接的原理的是编译器优化
可以将intern()
方法,主动将串池中还没有的字符串放入串池
String s = new String("a")+new String("b");
//堆 new String("a"), new String("b") new String("ab")
String s2 = s.intern();//将这个字符串对象尝试放入到串池,如果有的话那么就不会放入,没有的话就会放入串池,会把串池中的对象返回
StringTable的位置
1.6
中的StringTable
是放在永久代中的,而1.8
中的StringTable
是放在堆中的
2. 如何理的解String的不可变?
当对字符串进行重新赋值的时候,需要重新堆内存中的字符串常量池,不能够使用原来的value
进行赋值
当对现有的字符串进行连接操作的时候,也是需要重新申请内存,然后创建新的字符串的,不能使用原有的value
进行赋值
当使用String
的replace()
方法修改指定字符或者字符串的时候,也需要重新指定内存区域赋值,不能够使用原有的value
进行赋值
通过字面量的方式给一个字符串赋值,此时字符串的声明在字符串的常量池中