字符串对象和常量池的头疼问题,求大神赐教!!!!

流烟默 2018-09-03 06:37:15
public static void main(String[] args){
String str1 = new String("abc") + new String("abc");
str1.intern();
String str2 = "abcabc";
System.out.println(str1 == str2);//true

String str3 = new String("abcd") + new String("abcd");
String str4 = "abcdabcd";
str3.intern();
System.out.println(str3 == str4);//false
}

public static void main(String[] args){
String str2 = new String("hello");
str2.intern();
String str = "hello";
System.out.println(str==str2);// 运行后结果为false

String str3 = new String("world");
String str4 = "world";
str3.intern();
System.out.println(str4==str3);// 运行后结果为false
}


上面两个方法测试结果为什么不一致?另外还有这样几个问题:

① String str = "abc";在类加载的时候已经将abc这个字面量放进了常量池,对吗?在类加载的时候是否创建了一个所谓的"拘留字符串对象"?

② String str = new String("abc");涉及到几个实例?我看有的说法是涉及到两个,一个拘留字符串对象,一个在堆中new String()产生的对象。

③ intern()这个方法究竟做了什么?如str.intern();我看源码是这样翻译的:如果String池中有与str通过equals方法比较相同的String对象,就返回该String;否则就将str这个对象放进String池中,并返回该对象的引用。返回String和返回该对象的引用什么区别?按照我的理解 str肯定是存的对象引用啊,只是指的位置不同。

④ 有说法为String常量池现在已经不在方法区的运行时常量池了,在堆中,有一个独有的String常量池,该中说法是否正确?如果正确,那么是否意味着,在类加载的时候,类的常量池中关于String的那些东西会放在堆中的String常量池中?还是说类加载的时候String的那些东西还是放在运行时常量池中,运行的时候才放进堆中的String的常量池中?
...全文
1172 46 打赏 收藏 举报
写回复
46 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
nobuglady 2018-09-06
引用 52 楼 monica12 的回复:
2、 String str2 = new String("hello"); 建立一个对象堆地址&02
str2.intern(); 把hello放入池中,
String str = "hello";//这里应该是从池中取得地址是堆地址&02
应该相等啊,但结果是错的。

仔细读一下13楼。
"hello"生成的对象不是str2 ,所以str 和str2不相等。
  • 打赏
  • 举报
回复
换成这个就可以的
public class StringTest {
public static void main(String[] args) {
String str1 = new String("abc") + new String("abc");
str1.intern();
String str2 = "abcabc";
System.out.println(str1 == str2);//true

String str3 = new String("abcd") + new String("abcd");
str3.intern();
String str4 = "abcdabcd";
System.out.println(str3 == str4);//true

}
}
原因是:
仔细看intern api如下:
intern
public String intern()返回字符串对象的规范化表示形式。
一个初始为空的字符串池,它由类 String 私有地维护。

当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(用 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并返回此 String 对象的引用。

它遵循以下规则:对于任意两个字符串 s 和 t,当且仅当 s.equals(t) 为 true 时,s.intern() == t.intern() 才为 true。

所有字面值字符串和字符串赋值常量表达式都使用 intern 方法进行操作。字符串字面值在 Java Language Specification 的 §3.10.5 定义。


返回:
一个字符串,内容与此字符串相同,但一定取自具有唯一字符串的池。

原因是:你执行intern方法时,常量池中已经有两个相同的字符串了,并不是唯一的。
  • 打赏
  • 举报
回复
monica888888 2018-09-06
1、String str1 = new String("abc") + new String("abc");
String str11= str1.intern();
String str2 = "abcabc";

因为第一次abcabc这个字符常量池中,没有,所以放abcabc入常量池,str1 返回常量池地址 ,那么str1==str11 true

2、 String str3 = "abcabc";//新加的一行代码,其余不变
String str4 = new String("abcabc")+ new String("abcabc");
System.out.println(str3.intern() == str3); //false
System.out.println(str4 == "youcan"); //false

str4:因为池中已经有了abcabc,所以返回的是堆地址,str3.intern()是常量地址,所以不等

总结:用new 建立字符对象时,如果是第一次建立一个新字符,常量池没有,需要放入到池中一份,返回常量池地址,
如果池有了,就直接在堆中创建一个对象。
  • 打赏
  • 举报
回复
流烟默 2018-09-06
引用 46 楼 stacksoverflow 的回复:
[quote=引用 45 楼 J080624 的回复:]
[quote=引用 1 楼 J080624 的回复:]
两个方法的唯一区别是一个进行了对象拼接 String str1 = new String("abc") + new String("abc");,一个直接单独一个String str2 = new String("hello");,为什么两个方法结果不同?



jdk1.7及jdk1.8下:

String str1 = new String("abc") + new String("abc"); 在堆中放new String("abcabc") 假设内存地址为&02;

故而 str1.intern();返回的是&02。

str1.intern();

String str11="abcabc";

str1==str11;//true;
----------------------------------------------------------------------------------------------------------------

String str2 = new String("hello");会在池中放“hello”的引用 假设地址&03,在堆中放new String("hello");假设地址&04

故而 str2.intern();返回的是&03。

str2.intern();

String str22="hello";

str2==str22;//false;

-----------------------------

String str="abc";则直接往pool中放abc对象。[/quote]

1.对象拼接最终会被编译成StringBuilder.append方式,
String str1 = new String("abc") + new String("abc");
即String str1 = new StringBuilder().append("abc").append("abc").toString();
看一下StringBuilder的toString()方法的源代码,相当于String str1 = new String(new char[]{'a','b','c','a','b','c'},0,6);

2.所以最终讨论的应该是
String str1 = new String(new char[]{'a','b','c','a','b','c'},0,6);

String str2 = new String("hello");
的区别:
前者传得是char[],后者传的是字符串,看一下String的源代码。
char[]方式传入的参数作一个拷贝,声称String对象,放在堆中。
new String("hello")方式有一个传参过程,相当于多了一步String temp = "hello", new String(temp);
所以String temp = "hello"这一步,首先检查pool中是否有该字符串的hashCode,如果没有则在堆中创建对象,并将该字符串的hashCode放入pool中,最终返回堆中的对象。

仔细理解一下13楼的解释。[/quote]

对的,大哥,我没有描述清楚。jdk1.8下String str1 = new String("abc") + new String("abc");会被编译成
String str1 = new StringBuilder().append("abc").append("abc").toString();

StringBuilder的toString方法是新建了一个对象:
 @Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}


故而,String str1 = new String("abc") + new String("abc")相当于在堆中创建了一个String对象,值为“abcabc”。假设该内存地址为&01;那么此时str1指向&01。

谢谢大哥!
  • 打赏
  • 举报
回复
流烟默 2018-09-06
引用 53 楼 monica12 的回复:
1、String str1 = new String("abc") + new String("abc");
String str11= str1.intern();
String str2 = "abcabc";

因为第一次abcabc这个字符常量池中,没有,所以放abcabc入常量池,str1 返回常量池地址 ,那么str1==str11 true

2、 String str3 = "abcabc";//新加的一行代码,其余不变
String str4 = new String("abcabc")+ new String("abcabc");
System.out.println(str3.intern() == str3); //false
System.out.println(str4 == "youcan"); //false

str4:因为池中已经有了abcabc,所以返回的是堆地址,str3.intern()是常量地址,所以不等

总结:用new 建立字符对象时,如果是第一次建立一个新字符,常量池没有,需要放入到池中一份,返回常量池地址,
如果池有了,就直接在堆中创建一个对象。


用new 建立字符对象时,如果是第一次建立一个新字符,常量池没有,需要放入到池中一份,返回常量池地址,
如果池有了,就直接在堆中创建一个对象。

这个不对,区别是:String str4 = new String("abcabc")+ new String("abcabc"); String str1 = new String("abc") ;的区别。
  • 打赏
  • 举报
回复
nobuglady 2018-09-05
引用 45 楼 J080624 的回复:
[quote=引用 1 楼 J080624 的回复:]
两个方法的唯一区别是一个进行了对象拼接 String str1 = new String("abc") + new String("abc");,一个直接单独一个String str2 = new String("hello");,为什么两个方法结果不同?


jdk1.7及jdk1.8下:

String str1 = new String("abc") + new String("abc"); 在堆中放new String("abcabc") 假设内存地址为&02;

故而 str1.intern();返回的是&02。

str1.intern();

String str11="abcabc";

str1==str11;//true;
----------------------------------------------------------------------------------------------------------------

String str2 = new String("hello");会在池中放“hello”的引用 假设地址&03,在堆中放new String("hello");假设地址&04

故而 str2.intern();返回的是&03。

str2.intern();

String str22="hello";

str2==str22;//false;

-----------------------------

String str="abc";则直接往pool中放abc对象。[/quote]

1.对象拼接最终会被编译成StringBuilder.append方式,
String str1 = new String("abc") + new String("abc");
即String str1 = new StringBuilder().append("abc").append("abc").toString();
看一下StringBuilder的toString()方法的源代码,相当于String str1 = new String(new char[]{'a','b','c','a','b','c'},0,6);

2.所以最终讨论的应该是
String str1 = new String(new char[]{'a','b','c','a','b','c'},0,6);

String str2 = new String("hello");
的区别:
前者传得是char[],后者传的是字符串,看一下String的源代码。
char[]方式传入的参数作一个拷贝,声称String对象,放在堆中。
new String("hello")方式有一个传参过程,相当于多了一步String temp = "hello", new String(temp);
所以String temp = "hello"这一步,首先检查pool中是否有该字符串的hashCode,如果没有则在堆中创建对象,并将该字符串的hashCode放入pool中,最终返回堆中的对象。

仔细理解一下13楼的解释。
  • 打赏
  • 举报
回复
流烟默 2018-09-05
引用 1 楼 J080624 的回复:
两个方法的唯一区别是一个进行了对象拼接 String str1 = new String("abc") + new String("abc");,一个直接单独一个String str2 = new String("hello");,为什么两个方法结果不同?


jdk1.7及jdk1.8下:

String str1 = new String("abc") + new String("abc"); 在堆中放new String("abcabc") 假设内存地址为&02;

故而 str1.intern();返回的是&02。

str1.intern();

String str11="abcabc";

str1==str11;//true;
----------------------------------------------------------------------------------------------------------------

String str2 = new String("hello");会在池中放“hello”的引用 假设地址&03,在堆中放new String("hello");假设地址&04

故而 str2.intern();返回的是&03。

str2.intern();

String str22="hello";

str2==str22;//false;

-----------------------------

String str="abc";则直接往pool中放abc对象。
  • 打赏
  • 举报
回复
没事,不用客气,我也是学习了。
  • 打赏
  • 举报
回复
流烟默 2018-09-05
引用 39 楼 weixin_40247263 的回复:
https://blog.csdn.net/SEU_Calvin/article/details/52291082,你看下这个文章,说的很明白。


非常感谢,做了jdk1.6和jdk1.7下,对pool中存放数据的对比。
  • 打赏
  • 举报
回复
流烟默 2018-09-05
关于pool中,即字符串池中放的是对象还是引用,总结一下:jdk1.6时,如果发现pool中没有,就把对象复制一份放到pool中;jdk1.7及jdk1.8就变了,发现没有,会把堆中对象的引用放到池中。
  • 打赏
  • 举报
回复
流烟默 2018-09-05
加载还是运行问题解决了,结果是运行时创建对象。那么String池/pool,中放的究竟是对象还是引用?

引用 31 楼 Surrin1999 的回复:
[quote=引用 30 楼 J080624 的回复:]
如果String str=new String("abc");不会对pool进行操作,那么下面这个该怎么解释,大哥:



但是按照我的理解,除非String str="abc";或者str.intern();才会操作pool。


new的话只要字符串原本没在池里没有 肯定会把字符串放进池里的 你可以命令行下 javap -verbose 类名.class 反编译class文件 可以看到const pool里有它的[/quote]

我觉得不对,大哥;第一 javap 看的是class文件的常量池;StackOverflow那个大佬指的是运行时的String 池,非class文件的常量池,也非方法区的运行时常量池。运行时的String池是在堆中的,而且创建对象是在运行的时候。
  • 打赏
  • 举报
回复
Dan淡淡的心 2018-09-05
String s = new String("1"); 第一句代码,生成了2个对象。常量池中的“1” 和 JAVA Heap 中的字符串对象。s.intern(); 这一句是 s 对象去常量池中寻找后发现 “1” 已经在常量池里了。 接下来String s2 = "1"; 这句代码是生成一个 s2的引用指向常量池中的“1”对象。 结果就是 s 和 s2 的引用地址明显不同。 来看第二段代码,从上边第二幅图中观察。第一段代码和第二段代码的改变就是 s3.intern(); 的顺序是放在String s4 = "11";后了。这样,首先执行String s4 = "11";声明 s4 的时候常量池中是不存在“11”对象的,执行完毕后,“11“对象是 s4 声明产生的新对象。然后再执行s3.intern();时,常量池中“11”对象已经存在了,因此 s3 和 s4 的引用是不同的。 第二段代码中的 s 和 s2 代码中,s.intern();,这一句往后放也不会有什么影响了,因为对象池中在执行第一句代码String s = new String("1");的时候已经生成“1”对象了。下边的s2声明都是直接从常量池中取地址引用的。 s 和 s2 的引用地址是不会相等的 原文链接:https://www.cnblogs.com/wxgblogs/p/5635099.html
  • 打赏
  • 举报
回复
https://blog.csdn.net/SEU_Calvin/article/details/52291082,你看下这个文章,说的很明白。
  • 打赏
  • 举报
回复
nobuglady 2018-09-05
引用 26 楼 J080624 的回复:
[quote=引用 16 楼 stacksoverflow 的回复:]
① String str="abc";//String str = new String("abc");// str.intern();都会对pool进行操作;
不对,String str = new String("abc");不对pool进行操作

② String str="abc";在类加载过程中并不会创建对象,而是在运行的时候创建对象。
加载过程没细研究,但从编程的角度来看,对于这个问题现只讨论运行时情况。

1)jdk1.8下 String pool 已经从方法区的运行时常量池中跳出来了,独立放在堆空间中,是否正确?
是的,整个字符串都放到常量池中的话,如果字符串比较大,会有性能问题,官方文档地址我不记得了,可以参考这个文档:
http://java-performance.info/string-intern-in-java-6-7-8/

2)String pool中,就像您说的那样,不放对象,放的是引用。那么String str="abc";在类编译和加载的过程中,不会创建对象,不会放pool里面放东西,是否正确?
编译阶段生成的是静态的class文件,可以理解为一条一条指令和一些静态的数据,
对象都是在运行时产生的,所以我们讨论的是运行时的常量池,
不对,这种直接赋值的语句的意思是,首先查看pool中是否有同样的字符串(hashCode),如果有,则返回该hashCode指向的对内存中对象的地址,
如果没有,则创建了一个如下结构的String对象
String{
char[] value = {'a','b','c'}
}
放在堆内存中,我假设这个对象的地址为&01,那么在常量池中同时存储"abc"的hashCode对&01的指向也就是:96354⇒&01

3)如果类加载过程中,String str="abc";不会创建对象,而是在运行的时候创建对象。那么是否意味着,编译的时候class文件的常量池中放的只是"abc"的字面量指。在运行的时候创建对象,而且不放在运行时常量池,而是放在String 池中?
编译的时候class存放指令和指令相关的东西,和我们讨论的运行时常量池完全是两个东西,不能混在一起想。
对象都是运行时创建的。

也就是编译阶段生成指令,运行阶段CPU读取并运行指令,遇到new关键字的指令,在堆内存中分配空间,也就是对象。
所以字符串常量池问题,应该是运行时的情况。

但是编译阶段比如String str = new String("a") + new String("b") + new String("c")
会被翻译成new StringBuilder().append("a").append("b").append("c").toString();
这些要注意一下。


大哥,您说new String("abc");不会对pool进行操作,但是看case1如下:

case1:String str = new String("abc");
等同于如下两行代码:
String temp = "abc";
String str = new String(temp);

其中String temp="abc";是会对pool进行操作的。[/quote]
是的,new String("abc");会对pool进行操作,
String str = new String(temp);不会对pool进行操作。
  • 打赏
  • 举报
回复
nobuglady 2018-09-05
引用 36 楼 stacksoverflow 的回复:
[quote=引用 26 楼 J080624 的回复:]
[quote=引用 16 楼 stacksoverflow 的回复:]
① String str="abc";//String str = new String("abc");// str.intern();都会对pool进行操作;
不对,String str = new String("abc");不对pool进行操作

② String str="abc";在类加载过程中并不会创建对象,而是在运行的时候创建对象。
加载过程没细研究,但从编程的角度来看,对于这个问题现只讨论运行时情况。

1)jdk1.8下 String pool 已经从方法区的运行时常量池中跳出来了,独立放在堆空间中,是否正确?
是的,整个字符串都放到常量池中的话,如果字符串比较大,会有性能问题,官方文档地址我不记得了,可以参考这个文档:
http://java-performance.info/string-intern-in-java-6-7-8/

2)String pool中,就像您说的那样,不放对象,放的是引用。那么String str="abc";在类编译和加载的过程中,不会创建对象,不会放pool里面放东西,是否正确?
编译阶段生成的是静态的class文件,可以理解为一条一条指令和一些静态的数据,
对象都是在运行时产生的,所以我们讨论的是运行时的常量池,
不对,这种直接赋值的语句的意思是,首先查看pool中是否有同样的字符串(hashCode),如果有,则返回该hashCode指向的对内存中对象的地址,
如果没有,则创建了一个如下结构的String对象
String{
char[] value = {'a','b','c'}
}
放在堆内存中,我假设这个对象的地址为&01,那么在常量池中同时存储"abc"的hashCode对&01的指向也就是:96354⇒&01

3)如果类加载过程中,String str="abc";不会创建对象,而是在运行的时候创建对象。那么是否意味着,编译的时候class文件的常量池中放的只是"abc"的字面量指。在运行的时候创建对象,而且不放在运行时常量池,而是放在String 池中?
编译的时候class存放指令和指令相关的东西,和我们讨论的运行时常量池完全是两个东西,不能混在一起想。
对象都是运行时创建的。

也就是编译阶段生成指令,运行阶段CPU读取并运行指令,遇到new关键字的指令,在堆内存中分配空间,也就是对象。
所以字符串常量池问题,应该是运行时的情况。

但是编译阶段比如String str = new String("a") + new String("b") + new String("c")
会被翻译成new StringBuilder().append("a").append("b").append("c").toString();
这些要注意一下。


大哥,您说new String("abc");不会对pool进行操作,但是看case1如下:

case1:String str = new String("abc");
等同于如下两行代码:
String temp = "abc";
String str = new String(temp);

其中String temp="abc";是会对pool进行操作的。[/quote]
是的,new String("abc");会对pool进行操作,
String str = new String(temp);不会对pool进行操作。[/quote]
① 的回答确实写错了,请无视
  • 打赏
  • 举报
回复
monica888888 2018-09-05
intern():主要是把字符串放入池中,建立对象时如果不调用此方法,对象在堆中,不放池中

1、String str1 = new String("abc") + new String("abc");
String str11= str1.intern();
String str2 = "abcabc";
System.out.println(str1 == str2);//true
System.out.println(str1==str11);//地址true

2、 String str2 = new String("hello");
str2.intern();
String str = "hello";
System.out.println(str==str2);// 运行后结果为false
理由:1、1》建立了两个abc字符对象,然后StringBuilder将两个字符串连接起来abcabc 建立一个新字符串,那么直接放池中了,返回一个池中的地址@01,所以 str1.intern() 返回的地址@01和str1@01相等,
2》 String str2 = "abcabc";是去池中看有没有,有返回地址@01 所以答案true
其实主要是在StringBuilder这个类中的append 方法,toString方法,把数据直接放入到池中,感觉是这样的。

2、str1是对象地址01,一直没有重新赋值, str2.intern()地址是02;就是把值放在池中,所以str取得是池中地址&02,也不相等。
所以对象地址和池中的地址不相等。


按别人的解释:如果jdk在1.8中,池是存放堆中的地址

String str1 = new String("abc") + new String("abc"); 如堆地址&01
String str11= str1.intern(); 把abcabc存入池中,那么返回堆地址&01
String str2 = "abcabc"; 查找常量值,有这个值,那么返回堆地址,
str1 ==str2 true 也对
2、 String str2 = new String("hello"); 建立一个对象堆地址&02
str2.intern(); 把hello放入池中,
String str = "hello";//这里应该是从池中取得地址是堆地址&02
应该相等啊,但结果是错的。




  • 打赏
  • 举报
回复
xiaocaikuan123 2018-09-05
写的还行不过现在用不到
  • 打赏
  • 举报
回复
nobuglady 2018-09-04
首先,搞懂下面几个case:
case1:String str = new String("abc");
等同于如下两行代码:
String temp = "abc";
String str = new String(temp);

case2:String str = new String("a") + new String("b") + new String("c")
等同于:
String str = new StringBuilder().append("a").append("b").append("c").toString();
也等同于:
String str = new String(new char[]{'a','b','c'},0,3);
也等同于:
String str = new String(new char[]{'a','b','c'});

case3:String str = "abc";

接下来的前提:
a)只考虑一下String结构
String{
char[] value; //存储字符串
}
b)只考虑pool和堆内存,jdk8环境的前提下,
注:jdk8的pool里存不是字符串,而是把字符串存到堆内存中,pool中方一个对该对内存的引用。我们假设用hashCode的值来表示对对内存的引用。
也就是相同的字符串对应的hashCode值是一样的。(abc对应的hashCode为96354)

然后,我们来看一下上面case对pool的影响:
1.String temp = "abc";
说明:首先查看pool里是否有该字符串的hashCode(96354),如果没有则在堆中生成两个对象,一个String对象和一个char[]对象存储abc
堆内存:String对象:地址为&01, char[]对象:地址为&02
pool:"abc"的hashCode值96354,和对应的String对象的地址&01

2.String str1 = new String(temp);
说明:在堆内存中生成一个String对象,同时将temp对象的char[]的地址引用赋给String对象的char[]
堆内存:String对象:地址为&03, char[]对象:地址为&02
pool:不对pool进行任何操作

3.String str2 = new String(new char[]{'a','b','c'});
说明:在堆内存中生成一个String对象,同时生成String对象的char[],将参数{'a','b','c'}的值copy给String对象的char[]
堆内存:String对象:地址为&04, char[]对象:地址为&03
pool:不对pool进行任何操作

4.String.intern操作:
得到String对应char[]的hashCode,查看pool中是否有该hashCode,
如果没有,则将该hashCode的值存到pool中,并且将该String对象的引用存储到pool中,返回该String对象,也就是自己。
如果有,则返回pool中对应的String对象,(不是自己)。

下面解释你的程序:(@符号后面为假设的内存地址,比如@&01)

public static void main(String[] args){
String str1 = new String("abc") + new String("abc");
str1.intern();
String str2 = "abcabc";
System.out.println(str1 == str2);//true

String str3 = new String("abcd") + new String("abcd");
String str4 = "abcdabcd";
str3.intern();
System.out.println(str3 == str4);//false
}

翻译成

public static void main(String[] args){
//堆内存:
//POOL:
String str1 = new String(new char[]{'a','b','c','a','b','c'});
//堆内存:str1@&01, str1.value={'a','b','c','a','b','c'}@&02, str1.hashCode=-1424388928
//POOL:
str1.intern(); //pool中没有,所以放到pool中,(注意:返回值为&01)
//堆内存:str1@&01, str1.value={'a','b','c','a','b','c'}@&02, str1.hashCode=-1424388928
//POOL:hashCode:-1424388928⇒&01
String str2 = "abcabc"; //pool中有,所以用pool的
//堆内存:str2&01, str2.value={'a','b','c','a','b','c'}@&02, str2.hashCode=-1424388928
//POOL:hashCode:-1424388928⇒&01
System.out.println(str1 == str2);// &01==&01,true

//不再考虑上面的堆内存和pool
//堆内存:
//POOL:
String str3 = new String(new char[]{'a','b','c','d','a','b','c','d'});
//堆内存:str3@&03, str3.value={'a','b','c','d','a','b','c','d'}@&04, str1.hashCode=1259550596
//POOL:
String str4 = "abcdabcd"; //pool中没有,所以放到pool中
//堆内存:
// str3@&03, str3.value={'a','b','c','d','a','b','c','d'}@&04, str1.hashCode=1259550596
// str4@&05, str4.value={'a','b','c','d','a','b','c','d'}@&06, str1.hashCode=1259550596
//POOL:1259550596⇒&05
str3.intern(); //pool已有,不做任何操作 (注意:返回值为&05,如果str3=str3.intern(),则结果不一样)
// str3@&03, str3.value={'a','b','c','d','a','b','c','d'}@&04, str1.hashCode=1259550596
// str4@&05, str4.value={'a','b','c','d','a','b','c','d'}@&06, str1.hashCode=1259550596
//POOL:1259550596⇒&05
System.out.println(str3 == str4); &03==&05//false
}



public static void main(String[] args){
String str2 = new String("hello");
str2.intern();
String str = "hello";
System.out.println(str==str2);// 运行后结果为false

String str3 = new String("world");
String str4 = "world";
str3.intern();
System.out.println(str4==str3);// 运行后结果为false
}

翻译成

public static void main(String[] args){
//堆内存:
//POOL:
String temp = "hello"; //pool中没有,所以放到pool中
//堆内存:temp@&10,temp.value={'h','e','l','l','o'}@&11, temp.hashCode=99162322
//POOL:99162322⇒&10
String str2 = new String(temp); //str2的value存储到temp的引用,并不分配新地址
//堆内存:
// temp@&10,temp.value={'h','e','l','l','o'}@&11, temp.hashCode=99162322
// str2@&12,str2.value={'h','e','l','l','o'}@&11, str2.hashCode=99162322
//POOL:99162322⇒&10
str2.intern(); //pool已有,不做任何操作 (注意:返回值为&10,如果str2=str2.intern(),则结果不一样)
// temp@&10,temp.value={'h','e','l','l','o'}@&11, temp.hashCode=99162322
// str2@&12,str2.value={'h','e','l','l','o'}@&11, str2.hashCode=99162322
//POOL:99162322⇒&10
String str = "hello";//pool已有,所以用pool的
// temp@&10,temp.value={'h','e','l','l','o'}@&11, temp.hashCode=99162322
// str2@&12,str2.value={'h','e','l','l','o'}@&11, str2.hashCode=99162322
// str@&10,str.value={'h','e','l','l','o'}@&11, str.hashCode=99162322
//POOL:99162322⇒&10
System.out.println(str==str2);// &10==&12 运行后结果为false

//不再考虑上面的堆内存和pool
//堆内存:
//POOL:
String temp1 = "world" //pool中没有,所以放到pool中
//堆内存:temp1@&20,temp.value={'w','o','r','l','d'}@&21, temp.hashCode=113318802
//POOL:113318802⇒&20
String str3 = new String(temp1); //str3的value存储到temp1的引用,并不分配新地址
//堆内存:
// temp1@&20,temp.value={'w','o','r','l','d'}@&21, temp.hashCode=113318802
// str3@&22,str3.value={'w','o','r','l','d'}@&21, str3.hashCode=113318802
//POOL:113318802⇒&20
String str4 = "world";//pool已有,所以用pool的
//堆内存:
// temp1@&20,temp.value={'w','o','r','l','d'}@&21, temp.hashCode=113318802
// str3@&22,str3.value={'w','o','r','l','d'}@&21, str3.hashCode=113318802
// str4@&20,str4.value={'w','o','r','l','d'}@&21, str4.hashCode=113318802
//POOL:113318802⇒&20
str3.intern();//pool已有,不做任何操作 (注意:返回值为&20,如果str3=str3.intern(),则结果不一样)
// temp1@&20,temp.value={'w','o','r','l','d'}@&21, temp.hashCode=113318802
// str3@&22,str3.value={'w','o','r','l','d'}@&21, str3.hashCode=113318802
// str4@&20,str4.value={'w','o','r','l','d'}@&21, str4.hashCode=113318802
//POOL:113318802⇒&20
System.out.println(str4==str3);// &20==&22 运行后结果为false
}
  • 打赏
  • 举报
回复
NinjaYinJey 2018-09-04
String str = new String("abc") + new String("abc");
==>
if + || "" ?
true !
#1 = ['a','b','c'] + ['a','b','c'];
if #pool eq 'a','b','c' ?
true !
return #pool;
false !
#pool add #1;
if end ?
false !
return #1;
if end ?

str.intern();
==>
if #pool ex '#1 ?
true !
return #1;
false !
#pool add #1;
if end ?
  • 打赏
  • 举报
回复
ba_wang_mao 2018-09-04
  • 打赏
  • 举报
回复
加载更多回复(26)
相关推荐
发帖
Java SE

6.2w+

社区成员

Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
帖子事件
创建了帖子
2018-09-03 06:37
社区公告
暂无公告