在 Java 中创建 字符串对象 的方式有两种:通过字面量赋值或者通过 new 方法创建。
字符串的字面量是常量,字符串池中的字面量常量只有一个拷贝,它可以在程序任何地方被引用,以避免重复创建内容相同的字符串。通过字符串常量创建的字符串对象存放在字符串池中,通过 new 方法创建的字符串对象存放在堆中。
字符串池是 String 类维护了一块特殊的内存空间。
通过字面量赋值
String str1 = "barwe";
String str2 = "barwe";
System.out.println(str1 == str2); // true
创建 str1 时,JVM 在字符串池中找不到 “barwe” 这个常量,然后 JVM 在字符串池中创建了一个 “barwe” 常量,并将将这个常量的内存地址返回存储在变量 str1 中。
创建 str2 时,JVM 在字符串池中发现 “barwe” 常量已经存在了,JVM 直接将这个常量的内存地址返回赋值给变量 str2。
最终,变量 str1 和 str2 中存储的是字符串池中同一个 “barwe“ 常量的内存地址。 因此 str1 == str2 returns true。
通过 new 方法创建
String str3 = String.new("chin");
String str4 = String.new("chin");
System.out.println(str3 == str4); // false
创建 str3 时,字符串池没有找到常量 “chin”,因此 JVM 首先在字符串池中创建常量 “chin”,然后在 堆 中创建一个 String 对象,然后返回这个创建的 String 对象的内存地址给变量 str3。
创建 str4 时,JVM 在字符串池中找到了对应常量,则直接在堆中创建一个 String 对象,并将其地址返回给变量 str4.
变量 str3 和 str4 指向的是堆中两个不同的 String 对象,因此 str3 == str4 returns false.
综上,存储在 字符串池 中的是 字符串常量,通过字符串常量创建的 引用变量 存储的是 字符串常量 的地址。储存在 堆 上的是 字符串对象,通过 new 方法创建的 引用变量 存储的是 字符串对象 的地址。
所有字面量相同的 字符串常量 在 字符串池 中都对应着同一个常量对象,所以具有相同字面量的字符串变量都相等。
这里有个非常有趣的例子:
String str1 = "hello";
String str2 = "world";
String str3 = "helloworld";
System.out.println(str3 == "helloworld"); // true
System.out.println(str3 == "hello" + "world"); // true
System.out.println(str3 == str1 + str2); // false
在第一个判断中,右边的匿名引用变量指向的字符串地址就是创建 str3 时使用的字符串池地址。
在第二个判断中,右边使用 + 连接了多个字面量,该表达式实际上是确定的,与程序运行时无关,因此该操作实际上在编译时就进行计算了,其情况与第一个判断相同。
在第三个判断中,由于引用变量 str1 和 str2 依赖于程序运行,其结果只有当程序运行到该行时才能确定。也就是说,str1 + str2 不是一个字面量,它的结果是保存在 堆 上的,其结果是一个 字符串对象。==
右边是一个匿名引用变量,指向堆上的一个字符串对象,而左边是一个指向字符串常量的变量,二者自然不相等。
评论区