String-不同情况的字符串对象进行"=="比较的结果(jdk1.8)
public class Main{
public static void main(String args[]){
String str1 = "a";
String str2 = "b";
String str3 = "ab";
String str4 = str1 + str2;
String str5 = "a" + "b";
System.out.println( str3 == str4 );//(1) false
System.out.println( str3 == str5 );//(2) true
System.out.println( str4 == str5 );//(3) 结合(1)(2) false
}
}
- 运行程序时,运行时常量池中将 “a” ,“b”,“ab” 三个字符串对象创建出来,并放入StringTable中;
通过字符串变量 str1 + str2 得到 str4 的内部逻辑是:
str1 + str2 = new StringBuilder()
.append(str1)
.append(str2)
.toString();
StringBuilder中toString()方法的源码如下:
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);//通过new关键字创建了对象,存在堆中
}
由源码可知, StringBuilder.toString()方法在转换的过程中是new了一个String对象的,也就是所str4 = str1 + str2 ;的过程其实是创建了新的字符串对象,通过new关键字创建的对象都存放在JVM的堆内存中[[JVM内存结构]( (1条消息) JVM—内存结构_m0_53759248的博客-CSDN博客 )] , 而str3是存放在StringTable中.
(1) 由以上可知,str3 == str4结果为false;
- 在java文件编译期间,变量的值是无法确定的,而常量的值是确定的,“a”,“b”,"ab"都属于常量; 含有变量的运算在编译期间是无法做的,而只含有常量的运算是可以做的,而且运算之后的结果是固定的.{即:java在编译期间会直接计算只含有常量的运算}
“a”+“b"在编译的过程就可以计算出结果为"ab”; 之前"ab"对象已经存放在StringTable中了,所以给str5赋值的时候直接将StringTable中的"ab"对象的引用给str5即可.
(2)根据上面一点可以得出(2)处为true;(3)处为false
public class Main{
public static void main(String args[]){
String str6 = new String("a");
String str7 = new String("b");
String str8 = new String("ab");
String str9 = str6 + str7;
String str11 = "a" + "b";
System.out.println( str8 == str9 );//(4) false
System.out.println( str8 == str11 );//(5) false
System.out.println( str9 == str11 );//(6) false
System.out.println( str6 == "a" );//(7) false
}
}
-
以上代码在编译期间,“a”,“b”,“ab"同样转换了字符串对象存放在StringTable中,在执行的过程中,给str6,str7,str8赋值时采用了new关键字,所以在堆中又创建了三个新的字符串对象,并将引用给了变量,此时JVM中"a”,“b”,"ab"对象都有两个,变量指向的是队中的对象.
-
str9是通过变量相加得来,内部还是通过StringBuilder.toString()创建了新的字符串对象,存放在堆中,但是str9所指向的"ab"对象在StringTable中没有.
-
显然,代码中(4)(5)(6)(7)处的结果都为false;
使用intern()方法后的比较.
- JDK1.7以后,String中的intern()方法作用:
手动的将某个字符串对象尝试放入StringTable中,如果有则不放入,没有则放入,最后一定会将StringTable中的该字符串对象返回.
- JDK1.6以前,String中的intern()方法作用:
作用基本和jdk1.7是一样的,但是在StringTable中没有该对象的时候,是复制一个对象放入StringTable中,所以在堆中的对象和在StringTable中的对象本质上是两个对象。
public class Main{
public static void main(String args[]){
String s1 = new String("a"); //s1指向堆中的对象"a"
String s2 = s1.intern(); //s2指向StringTable的对象"a"
String s3 = new String("b");//s3此时指向堆中的对象"b"
s3 = s3.intern();//s3此时指向StringTable的对象"b", 此时执行垃圾回收的话,堆中的对象"b"会被GC回收.
System.out.println( s1 == "a" );//(1)false
System.out.println( s2 == "a" );//(2)true
System.out.println( s3 == "b" );//(3)true
String s4 = new String("a") + new String("b");//s4此时指向堆中的对象"ab",且此时StringTable中不存在"ab"对象.
System.out.println( s4 == "ab" );//(4)false
System.out.println( s4.intern() == "ab" );//(5)true 此时s4.intern()将"ab"对象放入了StringTable中,s4.intern()指向StringTable,但s4仍然指向堆中的"ab"对象.
System.out.println( s4 == s4.intern() );//(6)false
}
}
根据intern()方法的定义,结合之前的内容,就很容易得出上方的(1)(2)(3)(4)(5)(6)处的结果