话不多说,先上代码:
String str1 = "123";
String str2 = "123";
String str3 = new String("123");
String str4 = new String("123");
System.out.println(str1==str2);
System.out.println(str3==str4);
System.out.println(str3.equals(str4));
运行结果:
true
false
true
为什么会出现这样的结果呢,那我们就要从java的内存模型上来分析String构造对象与引用String常量对象的存储过程。(这里我分析的内存模型是简化的模型)
- 引用String常量对象
结合上述的代码例子:
我们要知道String类型的变量是引用类型,在内存栈中存放的只是String变量的引用,指向实际的内容。
那么在使用赋值语句新建String变量时,系统会在字符串常量池开辟开辟空间,存放字符串内容,栈中的str1变量指向字符串常量池中的“123”空间。
由于字符串常量池空间中的内容不可重复,因此在新建str2变量时,系统会检测“123”内容是否已经存在(在每次新建String变量时都会检测),检测已经存在了该内容,便不会再开辟空间,而是让新建的str2变量指向str1变量已经指向的空间。
在对String变量使用==判等时,判断的是变量指向的空间是否相同,这里str1与str2指向的空间相同,所以输出结果为true - String构造对象
同样结合上述的例子:
和引用String常量对象不同的是,使用构造器构造的String对象会先在堆中开辟一个空间,这个空间再指向字符串常量池中的内容(在开辟堆空间之前会检查字符串常量池中有没有对应内容,没有的话会先在字符串常量池中开辟空间储存内容,再执行上述操作,此时会构造两个对象)
在构造str3和str4这两个对象是都在堆中开辟了一个空间,这个空间指向字符串常量池“123”内容对应的空间
而==判断的是str3和str4直接指向的空间 ,这两个对象在堆中的空间不同,所以比较的输出结果为:false
equals方法比较的是变量的实际内容,所以这个比较的输出结果为:true - 使用字符串拼接符构造的字符串对象
String str1 = "123";
String str2 = "456";
String str3 = new String("123");
String str4 = new String("456");
String str5 = "123"+"456";
String joint1 = str1+str2;
String joint2 = str1+str4;
String joint3 = str3+str4;
System.out.println(joint1==joint2);
System.out.println(joint1==joint3);
System.out.println(joint1==joint4);
输出结果:
false
false
false
这里就要讲到字符串常量池的另一个规定了,字符串常量池空间中的内容是不可以改变的。要改变String变量的值,只能检测常量池改变后的内容,与其他空间能否对应。能对应则指向该空间;不能对应则开辟新空间。
那么joint1就新开辟了一个空间。之所以两个输出结果都为false,是因为使用字符串拼接符来拼接时,只要有一个是String变量,就会按String构造对象的方式来新建对象。只有都为常量时才会直接指向字符串常量池。
特别提醒:
当需要频繁地改变字符串的值时, 建议使用StringBuffer类或StringBuilder类。