String str1 = "abc";
String str2 = "abc";
String str3 = "a" + "bc";
System.out.println(str1 == str2);
System.out.println(str1 == "a" + "bc");
System.out.println(str1 == new String("abc"));
System.out.println(new String("ab")+"c" == new String("ab")+"c");
输出
true
true
false
false
一般用来比较字符串是否相同的equals()是比较字符串的内容,即在堆中的字符序列。而 == 则是比较两个字符串的内存地址是否相同(堆中的引用)。
第一个true很容易理解。str1和str2都是在内存的字符串常量池中。
字符串常量池:
- 为了减少在jvm中创建的字符串的数量,字符串类维护了一个字符串常量池,字符串常量池(String pool)是Java堆内存中一个特殊的存储区域;
- 当创建String对象时,jvm会先检查字符串常量池,如果这个字符串的常量值已经存在在池中了,就直接返回池中对象的引用,如果不在池中,就会实例化一个字符串并放入池中;
- 常量池:用于保存java在编译期就已经确定的,已经编译的class文件中的一份数据。包括了类、方法、接口中的常量,也包括字符串常量,如String s = "a"这种声明方式;
三个False:
第一个false很显然,使用了new就代表这个对象会存储在堆上。引用自然不会和在常量池中的对象一样。
第二个false则是因为在运行期"ab"+"c"操作实际上是编译器创建了StringBuilder对象进行了append操作后通过toString()返回了一个字符串对象存在heap上。
更多例子:
String s0 = "111"; //pool
String s1 = new String("111"); //heap
final String s2 = "111"; //pool
String s3 = "sss111"; //pool
String s4 = "sss" + "111"; //pool
String s5 = "sss" + s0; //heap
String s6 = "sss" + s1; //heap
String s7 = "sss" + s2; //pool
String s8 = "sss" + s0; //heap
System.out.println(s3 == s4); //true
System.out.println(s3 == s5); //false
System.out.println(s3 == s6); //false
System.out.println(s3 == s7); //true
System.out.println(s5 == s6); //false
System.out.println(s5 == s8); //false
对于final String s2 = "111",是一个用final修饰的变量,在编译期就已知了,在包含变量的字符串连接符"a"+s2时直接用常量"111"来代替s2,等效于"a"+"111",在编译期就已经生产了字符串对象"a111"对象在常量池中。