一: String s = new String("ABC") VS String s = "abc"
String s = "abc" // 字符串字面量
String s = new String("abc"); 这个会无条件的在堆中创建一个新对象。
下面看字符串字面量。
首先,记住重要的一点是字符串对象是不可变的。这就意味着一旦创建,一个字符串对象就不能被改变(还是可以通过反射来改变, 反射可以改变使用方法的可见性)。
“字符串常量池”:事实上他是一用来保存字符串对象引用的容器;即使字符串是不可变的,它仍然和Java中的其他对象一样。对象都是创建在堆中,字符串也不例外。
所以字符串常量池仍然依靠堆,他们存储的只是堆中字符串的引用。因为字符串对象是不可变的,所以复制多个引用来“共享”这个字符串是安全的。
package stirng; public class StringTest { public static void main(String[] args) { String one = "abc"; String two = "abc"; System.out.println(one == two); // 比较是否是同一个对象。二者在字符串常量池是同一个字符串,所以指向的是堆中相同的引用。 System.out.println(one.equals(two)); // 这个比较的是内容,一定相等 System.out.println(one == calFun() ); // one和calFun函数中的three是在两个函数中,但是指向都是字符串常量池中的相同的引用。都是堆中字符串的引用。 } private static String calFun() { String three = "abc"; return three; } }
输出:
true true true
下面看一下字符串常量池是如何工作的:
当一个.java文件被编译成.class文件时,和所有其他常量一样,每个字符串字面量都通过一种特殊的方式被记录下来。
当一个.class文件被加载时(注意加载发生在初始化之前),JVM在.class文件中寻找字符串字面量。
当找到一个时,JVM会检查是否有相等的字符串在常量池中存放了堆中引用。
如果找不到,就会在堆中创建一个对象,然后将它的引用存放在池中的一个常量表中。
一旦一个字符串对象的引用在常量池中被创建,这个字符串在程序中的所有字面量引用都会被常量池中已经存在的那个引用代替。
内存示意图如下:
二:JDK 1.7后,字符串常量池的变化:
如果在常量池找不到对应的字符串,则不会再将字符串拷贝到常量池,而只是在常量池中生成一个对原字符串的引用。简单的说,就是往常量池放的东西变了:原来在常量池中找不到时,复制一个副本放到常量池,1.7后则是将在堆上的地址引用复制到常量池。
三: intern方法的作用:
它遵循对于任何两个字符串 s 和 t,当且仅当 s.equals(t) 为 true 时,s.intern() == t.intern() 才为 true。
返回:
一个字符串,内容与此字符串相同,但它保证来自字符串池中。
------------------------------------------------------------------------------------------------
我们知道,一个Java程序运行后,String类会在内存的方法区中维护一个字符串池。对一个字符串调用intern()方法后,会先检查池内是否有该字符串,若有则返回;若没有没有则先创建再返回,确保返回的字符串已经以字面量的形式存在于池中。
测试代码如下:
String a = new String("a"); String b = new String("a"); System.out.println(a == b); System.out.println(a.intern() == b.intern());
输出:
false true