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

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 曹睿翔 于 2013-5-5 19:23 编辑

我领的是第五第六题。String练习中遇到的问题,以及StringBuilder与StringBuffer的区别。我觉得这两题关联很大。而且要讲的基本上主要是String与其它两类的区别。所以两题做一帖了。摘录一些很经典的代码段。个人觉得特别有代表性,能更深入的理解String以及StringBuilder。值得看看。参考了不少别人的资料,但都是自己仔细总结的。期待大家为我纠正错误之处。
=================================================================
                         String
一.String的内存解析。
这是刚开始接触java的同学相对较多遇到问题的地方吧。先看两个语句:
String str0 = "java";
String str1 = new String("hello java!");
两种声明字符串的方式。前一种,并没有在堆中分配内存。只是将"java"保存在常量池中。第二种方式,是将"hello java!"保存在常量池中,new操作所开辟的堆空间里会复制一份"hello java!"。然后讲str1指向new后的堆空间。这就是为什么说第二个语句实际上是两个对象。如图:

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
二.String是不可变的。
刚看到String是不可变的时候,有点不明白。后来查看一些资料,再看看String类的源代码,就明白了。这也解决了另外一个常被问到的面试题:问,String类可以被继承吗?先来看看String的源码,String类的声明是这样的:public final class String......这样是不是就有答案了。至于String是不可变的原因是,从源码可以看出,String的成员变量差不多都是被final修饰
的。再看两个语句:
String str = "java";
str = "hello";
String的不可变是指,虚拟机并没有改变原来的对象"java",而是新创建了一个对象"hello",再让str指向这个新的对象"hello"。

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
三.String相关的几段经典代码。
*****代码段一
  1. String str0 = "ab";   
  2.         String str1 = "a" + "b";   
  3.         System.out.println(str0 == str1);
复制代码
*****代码段二
  1. String str0 = "ab";   
  2.         String str1 = "b";   
  3.         String str2 = "a" + str1;   
  4.         System.out.println(str0 == str2);   
复制代码
*****代码段三
  1. String str = "java";
  2.        String str1 = new String("java");
  3.        System.out.println(str==str1.intern());
复制代码
代码段一,输出的结果是true,代码段二的结果是false。
对于以上两段代码的说明:
1.当使用String str = "java";方式来创建对象时,java运行时会拿这个"java"去String的常量池里去找是否有这个字符串,如果有,直接将常量池中地址给str。如果没有,讲"java"加入常量池。
2.当使用String str = new String("java");来创建对象时,同样会拿"java"去常量池中找,如果没有,则添加。并复制一份到new的堆空间,将堆空间地址给str。如果有,直接复制一份到new的堆空间,将堆空间地址给str。
3.使用常量来串联表达式时,不会在堆空间新建对象。只是在常量池中检查,有则直接指向,没有则在池中创建再被指向。如代码段一String str1 = "a" + "b";因为只在常量池中操作,所以返回true。
4.使用带变量表达式创建String,则不仅检查常量池,还会在堆中新建对象。如代码段二。返回false。
5.关于字符串常量池,还有一个intern()方法。以前没接触毕老师视频的时候,因为不知道字符串有个常量池,所以和一同学不理解编程中遇到的现象。为此查了不少资料才搞明白。当时在一资料上看到这个方法。印象比较深,这个方法的意思就是将常量池的字符串进行比较(我是这么理解的)。如代码段三。

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



未命名.jpg (15.63 KB, 下载次数: 0)

未命名.jpg

评分

参与人数 1技术分 +1 收起 理由
曹睿翔 + 1 很给力!

查看全部评分

26 个回复

正序浏览
黑马伍哲沂 发表于 2013-5-8 20:32
这个问题。我表示没有研究,不清楚就不乱说了。  但String类这里其实有个设计模式,叫做不变模式。  如果 ...

好吧,哥们加油。。。我在看看源代码去。。
回复 使用道具 举报
Miss小强 发表于 2013-5-8 20:27
追问:突然想到,如果这样的话,

用char数组存多个中文字符 比用string好在哪?

这个问题。我表示没有研究,不清楚就不乱说了。  但String类这里其实有个设计模式,叫做不变模式。  如果你想深究,你可以参考下不变模式,然后再找找字符数组与String的比较资料。  这个我暂时不想深究了。  因为还有很都多基础任务没完成。
回复 使用道具 举报
Miss小强 发表于 2013-5-8 20:21
我注释的地方是书上说的,可是我不知道为什么,看源代码也找不到为什么。。。
这几天跟你的贴,学到了好多 ...

客气了。多交流,相互学习。更容易进步。其实我也只是菜鸟。。。  不过我整理帖子会比较认真。所以,别的人看起来就好像我懂很多一样。。  都是假象  哈哈  努力中。。。  
回复 使用道具 举报
追问:突然想到,如果这样的话,

用char数组存多个中文字符 比用string好在哪?
这个问题的答案应该是使用字符数组更高效了??
回复 使用道具 举报
Miss小强 发表于 2013-5-8 20:19
结论:使用StringBuilder而不是StringBuffer和String;
使用多个append而不是+号;
ps:看了你的帖子回去 ...

使用append而不使用+号  是可以理解的。  因为+号就是用来连接字符串的。
你在append方法里用+号,就是两个字符串的链接。两个字符串串联会产生很多多余对象。
之前好像看到有资料说+是调用了concat方法。但我不确定。
回复 使用道具 举报
我注释的地方是书上说的,可是我不知道为什么,看源代码也找不到为什么。。。
这几天跟你的贴,学到了好多东西,真幸运。。。谢谢哥们了。。。
回复 使用道具 举报
黑马伍哲沂 发表于 2013-5-5 16:29
String、StringBuilder、StringBuf ...
  1. package cn.itcast.problem;

  2. public class StringBufferTest {

  3.         /**
  4.          * @param args
  5.          */
  6.         public static void main(String[] args) {
  7.                 long begin=System.currentTimeMillis();
  8.                 testStringBuilder();
  9.                 long end=System.currentTimeMillis();
  10.                 System.out.println("cost.."+(end-begin));
  11.         }

  12.         private static void testString() {
  13.                 String str=new String();
  14.                 //这里循环不能太大,否则会耗时很久,甚至内存溢出;
  15.                 for(int i=0;i<20000;i++){
  16.                         //这样最垃圾的操作,每次都会有很多的new String();对象;
  17.                         str+=" * "+i+" * ";
  18.                 }
  19.         }
  20.        
  21.         private static void testStringBuffer(){
  22.                 StringBuffer sb=new StringBuffer();
  23.                 for(int i=0;i<20000;i++){
  24.                         //这样用+号,每次+都会有一个StringBuilder创建出来
  25.                         sb.append(" * "+i+"  * ");
  26.                 }
  27.                 String str=sb.toString();
  28.         }
  29.        
  30.         private static void testStringBuffer2(){
  31.                 StringBuffer sb=new StringBuffer();
  32.                 for(long i=0;i<100000;i++){
  33.                         //注意和上面+有所不同,这样虽然更麻烦,但是更加高效;他只有一个StringBuffer;
  34.                         sb.append(" * ");
  35.                         sb.append(i);
  36.                         sb.append(" * ");
  37.                 }
  38.                 String str=sb.toString();
  39.         }
  40.        
  41.         private static void testStringBuilder(){
  42.                 StringBuilder sb=new StringBuilder();
  43.                 //这里为了看到效果设置大一点:
  44.                 for(long i=0;i<100000;i++){
  45.                         //这是最高效的方式。推荐使用;
  46.                         sb.append(" * ");
  47.                         sb.append(i);
  48.                         sb.append(" * ");
  49.                        
  50.                 }
  51.                 String str=sb.toString();
  52.         }
  53.        
  54.         private static void testStringBuilder2(){
  55.                 StringBuilder sb=new StringBuilder();
  56.                 for(long i=0;i<100000;i++){
  57.                         //这样不推荐使用,跟上面的一样。。。
  58.                         sb.append(" * "+i+"  * ");
  59.                 }
  60.                 String str=sb.toString();
  61.                
  62.         }

  63. }
复制代码
结论:使用StringBuilder而不是StringBuffer和String;
使用多个append而不是+号;
ps:看了你的帖子回去查了下资料,虽然试验了下,如书上所说,可是我找不到原因,悲剧啊。。
建议版主加精。。。
回复 使用道具 举报
Miss小强 发表于 2013-5-8 10:42
这也是群里的一个哥们问的。。。题目就这样
想了半天没明白为啥。
一个哥们的回答:

上链接:http://tech.ccidnet.com/art/3539/20080508/1443759_1.html
看来属于内存优化的内容了。你认真看看就都明白了。
回复 使用道具 举报
这也是群里的一个哥们问的。。。题目就这样
想了半天没明白为啥。
一个哥们的回答:
一个是基本类型 一个是对象类型 char数组多少个成员就站多少字节  String内存占用比char高
确切的说string无论里面多少数据 都至少占用28个字节
String开发时初始内存分配就是28bytes 在28的基础上 超过再递增28 依次类推
超过28 相当于你重新开了一个String
ps:Java中有没有什么方法计算某个变量占用的内存空间
回复 使用道具 举报
Miss小强 发表于 2013-5-8 09:48
有个问题不知道怎么理解:

用char数组存多个中文字符 比用string好在哪?

我觉得你应该把问题描述的更具体一点。   String就是用char[]数组实现的。可以看看源码。
回复 使用道具 举报
Miss小强 发表于 2013-5-8 10:08
看过你对这道面试题的分析,我回去又好好看了下,从hardcode java这本书里面看到final关键字;
如果给chang ...

其实也不是想的更深入,只是遇到问题,感兴趣的就会多查点资料。
final关键字,在毕老师的大概第6-8天左右的视频里有讲的(具体哪天记不得),final用于修饰变量主要有两种情况:
1.一些数据的出现的是固定值,为了增强阅读性,给这些值起个名字。方便于阅读。比如圆周率。这个值有可能在程序中多次出现,但又不需要改变的。加上final之后,就变成常量。
2,内部类定义在类中的局部位置上,只能访问该局部被final修饰的局部变量。(这个不看资料我也记不住,也没注意到是什么原因。)
所以,这里应该不需要这么做。我觉得独立思考总是好的。虽然我的独立思考经常被证明不是对的,但我还是坚持的很好...嘿。。。

回复 使用道具 举报
看过你对这道面试题的分析,我回去又好好看了下,从hardcode java这本书里面看到final关键字;
如果给change函数的形式参数列表都加上final关键字修饰,将解决所有问题,也就是:
  1. public void change(final String str,final char ch[])
复制代码
根据final特性,引用本身不能变,但是引用指向的对象是可变的;打个比方说,
小明和她女朋友结婚了,那么这辈子他只能是跟着他女朋友一起白头到老,也就是他只能有他女朋友这么一个了,在也不能换了。
而对于他女朋友来说,他女朋友可以变漂亮,可以变苗条;

小明就是那个引用,而他女朋友就是指向的那个对象;
因此在change方法中要改变str和ch,只能是用this改变成员变量了,而不是传递进来的形式参数;
感觉你想问题比我们都深入,欢迎批评指正。。。
回复 使用道具 举报
有个问题不知道怎么理解:

用char数组存多个中文字符 比用string好在哪?
没一点思路
回复 使用道具 举报
黑马伍哲沂 发表于 2013-5-5 16:29
四.关于一道面试题的多种说法解析。

有位同学在论坛发这道面试题帖后,看到有很多不同的回答。我都一一思 ...

关于字符串String是不可变的我补充一点,在 HardCode Java 这本书中介绍过不可变类型,即immutable type;
怎么样将一个类变的不可变能,很简单,就是不提供属性的写方法,也就是set方法;同各属性也是私有的,同时为了保证不能被反射,这些属性都被声明为final,
也就是说你外界是无法改变的。String就是这样一个不可变类型。
回复 使用道具 举报
风乐 发表于 2013-5-7 01:56
楼主总结的相当好,虽然很基础的东西,但还是让我从头看到尾了,而且感觉又学到了很多 ...

多谢支持。。。。
回复 使用道具 举报
楼主总结的相当好,虽然很基础的东西,但还是让我从头看到尾了,而且感觉又学到了很多
回复 使用道具 举报
顶......{:soso_e179:}
回复 使用道具 举报
梁海君 发表于 2013-5-5 20:15
厉害,厉害,盗走LZ的果实,只能万分感谢~

哈哈  谢谢顶帖。
回复 使用道具 举报
曹睿翔 发表于 2013-5-5 19:24
老伍都不说你了,还不按我要求,标题不对,帮你改了

sorry。。。兽sir= =。
回复 使用道具 举报
12下一页
您需要登录后才可以回帖 登录 | 加入黑马