看了几篇关于intern方法的文章,总结如下:
一、主要知识点:
1、使用引号声明的字符串常量都会直接在字符串常量池中生成
2、intern方法在jdk1.6与jdk1.7中有点区别,主要原因是jdk1.7中将常量池移到了堆中
3、使用intern方法会很大程度的节省内存
二、Java中字符串创建的两种方式:
Java中字符串对象创建有两种形式,
一种为字面量形式,如String str=" hello",
另一种就是使用标准的构造对象的方法,如String str=new String("hello");
这两种经常使用,在内存上面有一些区别。这一切都是JVM为了减少字符串对象重复创建,其维护了一个特殊的内存,这段内存就是字符串常量池
工作原理
当代码中出现字面量形式创建字符串对象时吗,JVM就会首先对这个字面量进行检查,,如果字符串常量池中存在相同内容的字符串对象的引用,则将这个引用返回,否则新的字符串对象就被创建,然后将这个引用放入字符串常量池中,并返回该引用。
String str1="hello"
JVM检测这个字面量,这里我们认为没有内容为"hello"的对象存在。JVM通过字符串常量池查找不到内容为"hello"的字符串对象存在,那么会创建这个字符串对象,然后将刚创建的对象的引用放入到字符串常量池中,并且将引用返回给变量str1。
String str2="hello"
同样JVM还是要检测这个字面量,JVM通过查找字符串常量池,发现内容为”droid”字符串对象存在,于是将已经存在的字符串对象的引用返回给变量str2。注意这里不会重新创建新的字符串对象。
验证是否为str1和str2是否指向同一对象,我们可以通过这段代码
System.out.println(str1 == str2);结果为true。
String str3 = new String("hello");
当我们使用了new来构造字符串对象的时候,不管字符串常量池中有没有相同内容的对象的引用,新的字符串对象都会创建。因此我们使用下面代码测试一下,
String str3 = new String("hello");
System.out.println(str1 == str3);
结果如我们所想,为false,表明这两个变量指向的为不同的对象。
三、JDK1.6与JDK1.7中intern方法的区别:
1、JDK1.6中
在JDK1.6中,常量池是放在Perm区(属于方法区)中的,这是和堆区完全分开的。
在JDK1.6中,intern()方法的作用:
检查字符串池里是否存在"hello"这么一个字符串,如果存在,就返回池里的字符串;如果不存在,该方法会把"hello"添加到字符串池中,然后再返回它的引用。
(1)
public static void main(String[] args) {
//在常量池中添加字符串hello
String s1="hello";
//由于常量池中已经存在"hello",所以返回该字符串
String s2=s1.intern();
System.out.println(s2); //hello
//s1和s2都指向常量池中"hello"
System.out.println(s2==s1); //true
}
public static void main(String[] args) {
//在堆中创建字符串并且在常量池中添加字符串hello 因为使用引号声明的字符串常量都会直接在字符串常量池中生成
String s1=new String("hello");
//由于常量池中已经存在"hello",所以返回该字符串
String s2=s1.intern();
System.out.println(s2); //hello
//由于s1指向的字符串在堆中,而s2指向的字符串在常量池中,所以地址不同(==判断的是地址)
System.out.println(s2==s1); //false
}
(3)
public static void main(String[] args) {
//在堆中创建字符串"hello world" 在常量池中分别创建"hello"与"world"
String s1=new String("hello")+new String("World");
//由于常量池中不存在"hello world",所以添加该字符串,并返回引用
String s2=s1.intern();
System.out.println(s2); //helloWorld
//由于s1指向的字符串在堆中,而s2指向的字符串在常量池中,所以地址不同(==判断的是地址)
System.out.println(s2==s1); //false
}
注意:在jdk1.7中会输出true,下面将介绍。
2、在JDK1.7中
在JDK1.7中,将常量池放到了堆中。
正常情况下,使用new创建的字符串对象的引用不会放入字符串常量池,但是在使用JDK1.7的情况下,如果想将这个对象的引用加入到字符串常量池,可以使用intern方法。调用intern后,首先检查字符串常量池中是否有该对象的引用,如果存在,则将这个引用返回给变量,否则将引用加入并返回给变量。
public static void main(String[] args) {
//在堆中创建字符串"hello world" 在常量池中分别创建"hello"与"world"
String s1=new String("hello")+new String("World");
/*
* 由于常量池中不存在"hello world",但是在堆中有该字符串,
* 所以将引用s1返回,即s2也指向堆中的字符串
*/
String s2=s1.intern();
System.out.println(s2); //helloWorld
//所以地址相同
System.out.println(s2==s1); //true
String s3="helloWorld";
//常量池中已经存在"helloWorld"的引用,所以s3也直接指向堆中的字符串
System.out.println(s3==s1); //true
}
关于intern()方法为什么能够节省内存,参见 Java技术——你真的了解String类的intern()方法吗