方法一:String str = “abc”
String st1 = "abc";
String st2 = "abc";
System.out.println(st1 == st2); //返回true
如上图可知,使用String str = “abc”是在字符串常量池里面开辟空间,
- 在Java中,当你创建一个字符串字面量时,JVM会首先检查字符串常量池中是否已经存在该值。如果存在,它将直接引用这个对象;如果不存在,JVM会在池中创建一个新的字符串对象。
- 这意味着如果你对相同的字符串字面量进行多次赋值,它们实际上指向的是同一个对象。
- 性能优势:由于字符串常量池减少了相同字符串对象的创建,节省了内存空间并提高了字符串比较的速度,尤其是在需要频繁使用相同字符串的情况下。
方法二:String str1 = new String("abc");
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1 == s2); // 返回false
- 使用
new
关键字显式创建一个新的String
对象。这一行代码会在堆内存中创建一个新的字符串对象,即使池中已经存在"abc"
这个值。 - 这样做无论常量池中是否存在
"abc"
字符串,都会导致新对象的创建。
字符串类型变量拼接细节
String se1 = "abc"; // se1 指向字符串常量池中的 "abc"
String se2 = "ab"; // se2 指向字符串常量池中的 "ab"
String se3 = se2 + "c"; // se3 引用堆中的新对象,内容同样是 "abc"
// 比较引用
System.out.println(se1 == se2); // false,因为它们是不同的对象
System.out.println(se1 == se3); // false,因为 se1 和 se3 是不同的对象
System.out.println(se1.equals(se3)); // true,因为它们的内容相同
在这段代码中,String se1 = "abc";
和 String se2 = "ab";
使用了字符串字面量,分别将 "abc"
和 "ab"
中的字符串对象存储在字符串常量池中。
String se3 = se2 + "c";这样导致:
se2 + "c"
进行字符串连接时,Java 在编译期间并不会把这个运算优化到常量池中,因此它会创建一个新的 String
对象。该新对象会在堆内存中生成,而不是在常量池中。
总结
se1
(存储在字符串常量池中)引用的是"abc"
字符串对象。se2
(也在常量池中)引用的是"ab"
字符串对象。se3
(在堆内存中,因连接操作创建的新对象)引用的是新创建的字符串对象。- 使用
+
进行字符串连接时,会生成一个新的对象,通常在堆中,而不会再常量池中。 - 因此,虽然
se1
和se3
的内容是相同的,但由于它们的引用不同,使用==
比较会返回false
。如果想要比较内容是否相同,应该使用equals()
方法。
字符串拼接细节
String str1 = "abc";
String str2 = "a" + "b" + "c";
System.out.println(str1 == str2); //返回true
字符串常量拼接赋值相当于字符串直接赋值
==和equals的区别
==
==
用来比较两个引用变量是否指向同一个对象(即内存地址是否相同)。- 当用于基本数据类型时,
==
比较的是它们的值。
equals
equals()
方法用于比较两个对象的“内容”是否相等。
注意null
- 使用
==
比较一个对象和null
是有效的。 - 使用
equals()
方法时,如果调用者对象为null
,则会抛出NullPointerException
,因此在调用equals()
之前,通常需要先检查调用者是否为null。
public class Test {
public static void main(String[] args) {
String a = "hello";
String b = new String("hello");
// 使用 == 比较引用
System.out.println(a == b); // false
// 使用 equals 比较内容
System.out.println(a.equals(b)); // true
}
}