A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 匡文 中级黑马   /  2013-10-26 00:27  /  2549 人查看  /  7 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 匡文 于 2013-10-26 14:50 编辑

老师说String 类型对象一旦声明了就不能够再改变,但是测试了一下下面这个是可以输出kuangood的呀,那就是说s改变了呀,好矛盾,这个怎么理解?
  1. public class Demo4{
  2.         public static void main(String[] args){
  3.                 String s = "kuan";
  4.                 s = s + "good";
  5.                 System.out.println(s);
  6.         }
  7. }
复制代码

评分

参与人数 1技术分 +1 收起 理由
杨增坤 + 1

查看全部评分

7 个回复

倒序浏览
FFF 金牌黑马 2013-10-26 00:40:06
沙发
String 类型对象一旦声明了就不能够再改变的意思你理解不太正确。
String S="kuan";是声明了一个长度为4,内容为kuan的对象。而s只是对这个对象的引用。
你再把s=s+"good"是再声明一个长度为4内容为"good"的对象。
最后合成了一个长度为8,内容为kuangood的对象。
一共生成了三个对象。"kuan","good","kuangodd"有三个内存地址值,你改变的,只是他的指向的内存地址,内容你没有改变。
String是final类型的,不能改变的。

我个人理解是这样的,如果有理解得不正确的,欢迎指出。

评分

参与人数 1技术分 +1 收起 理由
乔兵 + 1

查看全部评分

回复 使用道具 举报
String  s = "sss", s存的是地址,它指向内存里面的一块空间,这块空间存储的数据是"sss",
String的值不能改变的意思是不能再不改变s地址的前提下对s指向的数据进行修改,
String s = "kuan"; 创建一个String对象s, s = s + "good";会重新创建对象,在内存中重新开辟空间存储新数据,s存储的地址发生改变

评分

参与人数 1技术分 +1 收起 理由
乔兵 + 1

查看全部评分

回复 使用道具 举报
String(字符串)类型对象的内容一旦声明则不可改变这肯定是没问题的。
你这个疑问可以通过我画的这个图来理解:

当运行String s = "kuan";这句语句的时候,s就指向堆内存中存储"kuan"的内存地址;
当执行s = s + "good";这句语句的时候,相当于再堆内存中开辟了一个对象"good";
最后合成的s的内容就是"kuangood",它所在的堆内存地址被s所指向,s就不再指向"kuan"这个对象的内存地址了,就会被垃圾回收掉了。

总结:一个String对象内容的改变实际上是通过内存地址的“断开-连接”变化完成的,而本身字符串中的内容并没有任何的变化。


希望能够帮到您!{:soso_e183:}

评分

参与人数 1技术分 +1 收起 理由
乔兵 + 1

查看全部评分

回复 使用道具 举报
s是存储地址内容存储在字符串常量池,不能改变的是常量池里的内容而不是地址,s指向新的地址抛弃原有资源等来java回收机制回收。
回复 使用道具 举报
String s = "kuan";
s = s + "good";
String 类型对象一旦声明了就不能够再改变:说的是kuan这个字符串一旦声明就不能够再改变了,而String s的s只是一个引用,其存放的是kuan这个字符串的内存地址
s+"good"是完成两个字符串的连接,形成一个新的字符串kuangood,而s=。。。。   实质就是将新字符串在内存的地址赋给s
不知道我的就是你是否能懂,不懂可以再问我
回复 使用道具 举报
String s1 = "He"+"llo";
String s2 = "Hello";
System.out.println(s1.equals(s2));
System.out.println(s1 == s2);

String s1 = "He";
String s2 = "llo";
String s3 = s1 + s2;
String s4 = "Hello";
System.out.println(s3.equals(s4));
System.out.println(s3 == s4);

第一段代码s1==s2是true,而第二段代码s3==s4是false

把上面两段代码都理解了就行了

String s1 = "He"+"llo";// 这代码jvm会在编译时使它变成  String s1 = "Hello"; 所以第一段代码都是常量池的Hello,结果为true
String s3 = s1 + s2;//这代码实际是调用了StringBuffer,其操作相当于StringBuffer sb = new StringBuffer();   sb.append(s1);     sb.append(s2);  String s3 = sb; 这里new了一个对象,与常量池中的不是同一个地址的 所以第二段代码s3==s4是false

评分

参与人数 1技术分 +1 收起 理由
乔兵 + 1

查看全部评分

回复 使用道具 举报
本帖最后由 黄小贝 于 2013-10-26 10:31 编辑

楼上的基本上都是很抽象的回答,我带你Debug一下

将断点做在   s = s + "good";

然后开始debug,F5跟进

首先进入的是ClassLoader.loadClassInternal(String name)   此时 name = "java.lang.StringBuilder"



它自动用类加载器加载了一个  java.lang.StringBuilder

然后一路单步,我们又回到了 s = s + "good"  继续 F5


到了String.valueOf(Object obj) 此时 obj = "kuan",所以这里重新new出了一个String 它的值是 kuan



F6调过这一行,又回到了 s = s + "good",再次F5,进入了StringBuilder.StringBuilder(String str)  此时  str = kuan,构造了一个值为kuan的StringBuilder





也就是说,它重新new了一个”kuan“的String然后赋值给了一个StringBuilder


F6跳过下面的几步,回到 s = s + "good" ,再F5,到了StringBuilder.append(String str)   此时 str = "good",这里用append来完成拼接




是不是觉得一切都变得明朗起来,字符串的拼接实际上都是用StringBuilder完成的,继续跟进,到了 StringBuilder.toString(),这个时候它又重新new了一个String出来哦



这个时候s = s + "good"才算执行完成

回顾一下,

(1)类加载器加载 StringBuilder
(2)new一个新的"kuan"(从源码看出String.value()会重新new一个新的string)
(3)把这个string丢给 StringBuilder,创建一个值为kuan的StringBuilder
(4)通过StringBuilder.append()把"good"拼上去
(5)通过StringBuilder的toString得到最后需要的String(这里注意又new了一个新的String哟,看源码)


楼主可以自己调试一下,如果不会留言给我就行




评分

参与人数 1技术分 +1 收起 理由
乔兵 + 1

查看全部评分

回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马