String详解
String源码
private final char value[];
String的底层实现是靠char[]数组来实现的,因为加上了final修饰,也是就说value被赋值后是不能够指向别的空间的。所以说String对象是无法被修改的。
字符串常量池
字符串常量池就是jvm对字符串的缓存。对于jdk8来说,常量池空间是在堆区的。由c++实现,本质就是一个HashTable,是有固定的长度的,所以大量字符串放入到pool里,势必会引起table里的链表变长,引起查找效率的低下。
代码分析
String a = "a"; //双引号的写法 只会在常量池里创建字符串对象
String a1 = new String("a"); //会在堆中创建一个对象,如果pool里没有还会在pool中创建
System.out.println(a == a1); //false a指向pool里对象。a1指向堆里的对象
String a1 = new String("a"); //指向堆里的对象
String a2 = new String("a"); //指向堆里的对象
System.out.println(a1 == a2); //false 两个对象在堆中占的空间不同
String c = "a" + "b"; //编译期被优化 直接就是“ab” 字节码如下图
String d = "ab";
System.out.println(c == d); //true
String a = "a";
String b = "b";
String c = "a" + b; //见字节码,编译期无法被优化,实际上是通过StringBuilder拼接出来
//String c = a + b. 同理 也会为false
String d = "ab";
System.out.println(c == d); //false c指向堆 d指向常量池
public static void main(String[] args) {
final String b = "b"; //final修饰即为常量 编译期间能被优化
String c = "a" + b;
String d = "ab";
System.out.println(c == d); //true 都指向常量池空间
}
public static void main(String[] args) {
String a = "a";
String b = new String("b");
String c = a + b; //字节码通过StringBuilder拼接 在通过stringbuilder.toString() new出来的对象
String d = "ab";
System.out.println(c == d); //false. c指向堆。d指向常量池
}
public static void main(String[] args) {
String a = new String("a"); //两个对象 堆里一个 常量池里一个
String b = "a";
System.out.println(b == a.intern());//true
//intern()方法:常量池里已经有“a”了,所以返回常量池里的引用
//所以相等,因为b也指向的是常量池里的引用
}
public static void main(String[] args) {
String a = new String("a") + new String("b");
String b = "ab";
System.out.println(a == b); //false. a指向堆里对象 b指向常量池
}
//但是
public static void main(String[] args) {
String a = new String("a") + new String("b");
a.intern();
String b = "ab";
System.out.println(a == b); //true
//a的赋值实际通过stringbuilder拼接,最后调用sb.toString(),会在堆里生成string对象,但是由于没有字面量“ab”所以不会在常量池里创建对象
//a.inter()由于常量池里没有“ab”,对于jdk8,该方法会将“ab”在堆里的引用写进常量池,此时常量池里存的是堆对象“ab”的引用,所以后面给b赋值时,也是将常量池里保存的堆里ab的引用赋值给了b,所以a == b成立。
}
intern()总结
- 如果常量池里有,则返回常量池里字符串的引用
- 如果常量池里没有,将堆里对象的引用写进常量池