1、String底层
底层是一个不可变字符串,使用连接符的时候,实际上是经过了StringBuilder的优化处理的,并不是在原来的String对象中做追加,
最后再调用toString()方法,是把当前StringBuilder的对象变成了String
1.1 String在JDK1.8中的底层实现,如下:
private final char value[];
1.2 String在JDK1.9中的底层实现,如下:
@Stable //表示下方属性 最多被赋值1次! private final byte[] value;
1.3 char数组换为byte数组的原因
开发者发现人们在使用字符串的时候,多数使用的是拉丁文,而拉丁文所占字节数为1。
JDK1.8底层实现是使用的char数组,一个char类型所占字节数为2,所以在char数组存储拉丁文的时候浪费了一个字节的内存空间。
所以开发者在JDK1.9将char数组改为了byte数组!
2、创建与内存场景分析
String s1 = "abc";
分析:对于常量赋值,变量s1始终指向了字符串常量池的字符串(只有一份)
String s1 = new String("abc");
分析:生成了两个对象,分别是“abc” 和 new String("abc"),“abc”在类加载的时就已经创建
“abc” 是存在于常量池中 ,而s1指向堆里的对象。
String s1 = "a" + "b";
分析:当一个字符串由多个字符串常量连接而成时,该字符串是在编译期就能确定。先是在池里生成“a”和“b”,再通过拼接的方式生成"ab"
String s1 = new String("a") + new String("b");
分析: 这句话一共生成了5个对象,首先会先在池子生成“a”和“b”,然后会在堆里生成对象new String("a") 和 new String("b),
这两个对象分别指向常量池的所对应的字符串,接着由于“+”的作用下,创建了新的对象,这个对象通过利用之前的对象所指向的字符进行拼接生成“ab”,
注意:+操作是在堆里实现的,不会将生成的"ab"放在常量池里,此时之前的两个对象已经没有作用了,需要等待垃圾回收。
String intern()方法
String s1 = new String("a");
String s2 = s1.intern();
Systrm.out.println(s1 == s2); // false
先在池里创建“a”,返回一个对象引用赋给堆里的对象new String("a"),也就s1所指向的地方。
通过调用 intern() 方法,在池里找到s1对象所对应的字符串,并且进行返回给s2对象,所以s2所指向的地方,在常量池里。
3、汉字是如何存储的
String类将字符存储在char数组中,每个字符使用两个字节(16位)。
疑问:为什么有时候getBytes() 获得的byte数组是一个汉字长度为3
Strings="你"; byte[]a=s.getBytes(); System.out.println(a.length);
解答:java采用unicode,2个字节来表示一个字符。"你"这个中文字符的unicode就是2个字节。
String.getBytes(encoding)方法是获取指定编码的byte数组表示,通常gbk/gb2312是2个字节,utf-8是3个字节。
注意:Windows 系统:getBytes() 用的是 Windows 的默认编码 GB18030,那1个汉字会被拆成2个字节
Linux 或者 macOS 系统:getBytes() 默认编码是UTF-8,那1个汉字会被拆成3个字节
如果想在各平台上拆成相同的字节,那需要使用 getBytes( "GBK" )
浙公网安备 33010602011771号