1.+号
1. 1 + 号连接符的实现原理
StringBuilder(或者StringBuffer)的apend方法拼接,然后toString方法返回新的字符串
1.2 +号的特殊情况
1.2.1 当"+"两端均为编译期确定的字符串常量时,编译器会进行相应的优化,直接将两个字符串常量拼接好
System.out.println("Hello" + "World");
反编译后
System.out.println("HelloWorld");
1.2.2 对于final修饰的变量,它在编译时被解析为常量值的一个本地拷贝存储到自己的常量池中或嵌入到它的字节码流中 (如果是final修饰的方法,那么只能运行期间确定,则跟此处的result不一致)
String s0 = "ab";
final String s1 = "b";
String s2 = "a" + s1;
System.out.println((s0 == s2)); //result = true
2.String.intern方法
intern 方法是一个native方法,intern方法会从字符串常量池中查询当前字符串是否存在,如果存在,就直接返回当前字符串;如果不存在就会将当前字符串放入常量池中,之后再返回常量池中当前字符串的对象引用
3.总结
public static void main(String[] args) {
String s1 = "AB";
String s2 = new String("AB");
String s3 = "A";
String s4 = "B";
String s5 = "A" + "B";
String s6 = s3 + s4;
System.out.println(s1 == s2);
System.out.println(s1 == s5);
System.out.println(s1 == s6);
System.out.println(s1 == s6.intern());
System.out.println(s2 == s2.intern());
}
------
输出:
false
true
false
true
false
解析:真正理解此题目需要清楚以下几点
1)直接使用双引号声明出来的String对象会直接存储在常量池中;
2)String对象的intern方法会得到字符串对象在常量池中对应的引用,如果常量池中没有对应的字符串,则该字符串将被添加到常量池中,然后返回常量池中字符串的引用;
3) 字符串的+操作其本质是创建了StringBuilder对象进行append操作,然后将拼接后的StringBuilder对象用toString方法处理成String对象,这一点可以用javap -c命令获得class文件对应的JVM字节码指令就可以看出来。
还有一个有意思的点,String类中的hashCode并不是final修饰,如果并发情况下,多个线程同时对同一个字符串进行hashCode计算的话,是会重复进行计算的。
为啥不对字符串的hash值加final或者是进行volatile呢?
其实hashCode本身方法进行了简单缓存,这样还比较高效。
volatile本身的read是有开销的,并发条件下如果对同一个String进行hashCode计算的话,不同线程计算出来的结果都是一样的,重复计算不会造成其他安全问题,没必要为了这些小概率时间给hash值加上同步volatile语义。