下面说的常量池都是指字符串常量池,字符串常量池存在运行时常量池之中(在JDK7之前存在运行时常量池之中,在JDK7已经将其转移到堆中),运行时常量池从永久代移到了元空间中。
String a = new String("ab");
此时创建了两个对象,常量池中创建了ab对象,堆内存中也创建了ab对象,此时引用指向堆内存中的对象。
String a = "ab";
此时先去常量池中寻找有没有ab这个值,如果有就直接将引用指向它,如果没有,则在常量池中创建这个ab对象,引用指向它。
intern()方法调用,如果字符串常量池中已经存在一个等于String对象的字符串,就直接返回这个字符串对象的引用。如果字符串常量池中不存在该对象,则将此对象放到常量池中,返回新建字符串对象的引用。
String c = new String("b") + new String("a");
会创建六个对象
1.new StringBuilder对象用来做拼接
2.堆内存中创建new String("b")
3.常量池中创建b对象
4.堆内存创建new String("a")
5.常量池中创建a对象
6.最终需要调用StringBuilder中的toString()方法,而toString方法底层调用的是new String来实现的因此又创建了一个对象ab,但是注意点是这里调用toString()方法只会在堆内存创建对象,不会在常量池中创建对象。
intern的难点案例;
String s = new String(1);
s.intern();
String s2 = "1";
System.out.println(s==s2);
1.6至1.8都会返回false
此处和下面的对应起来作比较,这里的intern没有将常量池中引用指向堆内存的new是因为在调用intern方法之前,常量池中已经有对象了,下面调用的是toString(),这里直接new的。
分析:第一步在堆内存和常量池中都会创建一个1的对象,此时s指向堆内存1。第二步调用Intern方法,但是此时常量池中已经存在1这个对象,而且并没有让s指向这个intern,所以此时s还是指向的堆内存中的1。第三步,以为常量池中已经存在1这个对象,因此直接将说引用指向这常量池中地对象。第四步判断为false,一个指的堆内存,一个指的常量池中的对象。
String c = new String("b") + new String("a");
c.intern();
String d = "ab";
System.out.println(c==d);
1.6版本返回false,1.7和1.8返回的是true
和刚刚的区别就是第一步用了拼接最后调用的toString(),常量池中没有创建对象,通过第二步的intern方法才在常量池中创建了ab对象。1.6返回false的理论同上,因为一个指向的堆内存,一个指向的常量池。
1.7和1.8相对于1.6区别在于,字符串常量池移到了堆内存中,为了节省常量池的空间,在调用intern()方法时,如果堆内存中此时1通过new创建了ab对象,那么就直接引用指向这个new出来的对象,也就是说此时常量池中保存的是new出来的那个对象的地址,并没有创建一个新的对象。
标签:String,对象,创建对象,池中,Strng,内存,new,常量 From: https://www.cnblogs.com/liu-jin/p/17398127.html