补充小点
String str="kvill" 和 String str=new String("kvill")的区别
- "kvill"都是字符串常量,它们在编译期就被确定了, 会在常量池中创建一个"kvill"字符串对象
- 用new String("kvill") 首先在堆空间创建内存空间,并将引用付给str 然后去字符串常量池查看是否有hello字符串对象,没有的话分配一个空间存放hello,并且将其空间地址存入堆中new出来的空间中。
- String b = new String("hello"); 因为在new String对象的时候,如果它需要的字符串在常量池中有了,那么这个String对象的成员变量
value[]
的值,就指向常量池中的对象,如果没有,就在常量池中新建一个字符串对象,然后再指向它,而String对象本身是在堆里的 -
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[]; -
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
public class Demo { public static void main(String[] args) { String s1 = "hello"; String s2 = "world"; String s3 = "helloworld"; System.out.println(s3 == s1 + s2);// false System.out.println(s3.equals((s1 + s2)));// true System.out.println(s3 == "hello" + "world");//true System.out.println(s3.equals("hello" + "world"));// true } } **运行结果:** false true true true
1 字符串相加时如果含有字符串变量,则先在堆中开空间,再拼接,本质是new了StringBuilder对象进行append操作,拼接后调用toString()返回String对象。 所以s3==s1+s2是false
2 字符串相加时不含字符串变量,是先加,然后在常量池找,如果有就直接返回,否则,就在常量池中创建一个字符串常量,不会用到StringBuilder。 所以s3 =="hello"+"world"是true
intern方法
- 字符串常量存储在方法区的PermGen Space。在jdk1.7之后,字符串常量重新被移到了堆中
- 常量池指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。
- Java会确保一个常量池中相同的字符串常量有且仅有一个
public static void main(String[] args) { String s1 = new String("1"); s1.intern(); String s2 = "1"; System.out.println(s == s2); String s3 = new String("1") + new String("1"); s3.intern(); String s4 = "11"; System.out.println(s3 == s4); }
jdk1.6 intern()方法会检查字符串池里是否存在xxxx这么一个字符串,如果存在,就返回池里的字符串;如果不存在,该方法会把xxxx添加到字符串池中,然后再返回它的引用
在jdk1.6中返回false,false,jdk1.7是false,true。
jdk6中的解释:
因为 jdk6中的常量池是放在 Perm 区中的,Perm区和正常的 JAVA Heap 区域是完全分开的。上面说过如果是使用引号声明的字符串都是会直接在字符串常量池中生成,而 new 出来的 String 对象是放在 JAVA Heap 区域。
s3.intern 会将11写入常量池 所以s4返回的是常量池的11 而s3则指向堆中的对象。
2.jdk7中的解释
在 Jdk6 以及以前的版本中,字符串的常量池是放在堆的Perm区的,Perm区是一个类静态的区域,主要存储一些加载类的信息,常量池,方法片段等内容,默认大小只有4m,一旦常量池中大量使用 intern 是会直接产生java.lang.OutOfMemoryError:PermGen space
错误的。在 jdk7 的版本中,字符串常量池已经从Perm区移到正常的Java Heap区域了。为什么要移动,Perm 区域太小是一个主要原因,当然据消息称jdk8已经直接取消了Perm区域,而新建立了一个元区域。应该是jdk开发者认为Perm区域已经不适合现在 JAVA 的发展了。
s3.intern 因为常量池没有11 则会将这个对象的引用加入常量池,不会像jdk1.6 直接复制11到常量池。
所以s3==s4
总结 jdk6 intern方法:
当常量池中不存在"abc"这个字符串,在堆内存中new一个String对象,复制这个对象加入常量池,返回常量池中的对象;
当常量池中存在"abc"这个字符串对象,str指向这个对象的引用。
jdk1.7以上 intern方法:
当常量池中不存在"abc"这个字符串的引用,在堆内存中new一个新的String对象,将这个对象的引用加入常量池。(跟1.6的区别是常量池不再存放对象,只存放引用。)
当常量池中存在"abc"这个字符串,str指向这个引用;
常量池可以存放字符串 也可以存放字符串的引用(第一次遇见该字符串)
intern方法是复用字符串。