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-5 16:43 编辑

四.关于一道面试题的多种说法解析。

有位同学在论坛发这道面试题帖后,看到有很多不同的回答。我都一一思考了下。并查了相关资料。首先,说下比较好的不同理解的回答:
  1. public class Example
  2. {
  3.                 String str = new String("good");
  4.                 char[] ch = {'a','b','c'};
  5.          public static void main(String[] args)
  6.                {
  7.                                Example ex = new Example();
  8.                                ex.change(ex.str, ex.ch);
  9.                                System.out.print(ex.str+"......and.....");
  10.                                System.out.println(ex.ch);
  11.                }

  12.                public void change(String str,char ch[])
  13.               {
  14.                str = "test ok";    //加this处
  15.                               ch[0]='g';
  16.                }
  17. }
复制代码
1.字符串是不可变的;
2.值传递与引用传递的不同;
3.成员变量与局部变量区别;(这是我的理解和回答)
在我当时看来,觉得这三个理解比较靠谱。并且因为看到和自己不同的回答我怀疑自己理解错了。但经过验证,确实在程序change函数里的str前面加上this。就会出现不一样的结果。经过查阅许多资料后,我的理解是这样的:
1.字符串是不可变的。这话没有错。如第二部分的解说。也许也有些类似以上的题目,被解说为字符串是不可变的。我觉得这种说法,不太合适。这里的字符串不可变体现在,当change函数里str前加上this时,change被调用后,str指向了"test ok",原来的"good"暂时还在,只不过str不再指向它了。这就是说"good"是不可变的。我将程序稍微调整如代码段四。运行结果说明了所有问题。
******代码段四
  1. //相比前面代码增加了成员变量num,以及change函数增加同名形参。
  2. public class Example
  3. {
  4.                 String str = new String("good");
  5.                 char[] ch = {'a','b','c'};
  6.                 int num = 9;
  7.                 public static void main(String[] args)
  8.                {
  9.                  Example ex = new Example();
  10.                                 ex.change(ex.str, ex.ch,ex.num);
  11.                                 System.out.print(ex.str+"......and.....");
  12.                                 System.out.println(ex.ch);
  13.                                 System.out.println(ex.num);
  14.                }

  15.                public void change(String str,char ch[],int num)
  16.               {
  17.                               str = "test ok";
  18.                               ch[0]='g';
  19.                               num = 11;
  20.                }
  21. }
复制代码
2.关于值传递和引用传递的说法。查过资料说,String是对象类型,但有值传递的特征。我的理解是,同样如代码段四。这里是不是可以这么理解,具有值传递特征的变量才会出现局部变量和成员变量的问题。
3.如代码段五。这一段不需要任何解释了。结合上面一条说法,大家可以自己思考下。
******代码段五
  1. class Person
  2. {
  3.           String name;
  4.                  int age;
  5.                  Person(String name,int age)
  6.                 {
  7.                                 this.name = name;
  8.                                 this.age = age;
  9.                 }
  10. }
复制代码
最后,我觉得可以这样分析这道面试题(有错之处欢迎跟帖纠正):
因为String类以及Stirng类几乎所有成员变量都被final修饰,所以String是不可变的,因此String虽然是对象,但有值传递的特征。在java中,具有值传递特征的变量,当函数的形参与成员变量同名时,可以使用this来区分。这里change函数里没有添加this,所以结果为good gbc。(回答的是不是有点猛啊
,但我觉得回答的知识面越宽,越是有好处的)关于String的各种变种题目,我觉得只要透彻理解以上几点。什么变种都没关系了。

评分

参与人数 1技术分 +1 收起 理由
曹睿翔 + 1 赞一个!

查看全部评分

回复 使用道具 举报
本帖最后由 黑马伍哲沂 于 2013-5-5 16:45 编辑

                                                                    String、StringBuilder、StringBuffer

StringBuilder是1.5才出现的。与StringBuffer有至少两个不一样的地方。第一,名字不一样(废话)。第二,StringBuilder是线程非安全的。也就是说,频繁的操作中不需要判断同步锁,在单线程情况下,可以大大提高效率。帮助文档里是这样介绍的:一个可变的字符序列。此类提供一个与StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。我打开两份帮助文档,分别找到StringBuilder和StringBuffer,一一比对,从上到下,每一个方法都是一模一样的。所以,再没什么好说的了。既然官方文档说字符串缓冲区被单个线程使用的情况很普遍,不如介绍下
StringBuilder里的一部分方法实在:
在 StringBuilder 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符添加或插入到字符串生成器中。append 方法始终将这些字符添加到生成器的末端;而 insert 方法则在指定的点添加字符。其它的方法,都参照帮助文档吧。之所以前面加个String,是这里有必要说明一下String与StringBuilder的区别(StringBuffer是一样的了)。如前文叙述,String是不可变的。通过以下两个语句来看看String是怎样完成字符串的串联操作的。
String str = new String("hello");
str += " java";
1.第一句,首先,检查常量池是否有"hello",没有则创建"hello",再将"hello"复制到new的堆空间,让str指向堆中的"hello"。
2.第二句,在常量池中创建" java"。但没有指向。然后创建一个StringBuilder对象(1.5前是StringBuffer),调用append(),将字符串串联后,通过StringBuilder.toString()转化为String。也即使说,String的串联操作是通过StringBuilder完成的。而且整个过程中创建了四个对象。创建对象以及频繁操作后多余的对象要被gc回收,都是非常耗费时间的。所以说StringBuilder效率高。可以自己试着写小程序来比较运行时间。(差距是非常大的)

回复 使用道具 举报
曹睿翔 来自手机 金牌黑马 2013-5-5 17:24:20
板凳
我还能说什么呢,顶起!
回复 使用道具 举报
这个要顶。谢谢分享!
回复 使用道具 举报
老伍都不说你了,还不按我要求,标题不对,帮你改了
回复 使用道具 举报
大家确实很给力,这次集合贴,榜上有名!!
技术分送上!
回复 使用道具 举报
{:soso_e116:}厉害,厉害,盗走LZ的果实,只能万分感谢~
回复 使用道具 举报
曹睿翔 发表于 2013-5-5 19:24
老伍都不说你了,还不按我要求,标题不对,帮你改了

sorry。。。兽sir= =。
回复 使用道具 举报
梁海君 发表于 2013-5-5 20:15
厉害,厉害,盗走LZ的果实,只能万分感谢~

哈哈  谢谢顶帖。
回复 使用道具 举报
顶......{:soso_e179:}
回复 使用道具 举报
楼主总结的相当好,虽然很基础的东西,但还是让我从头看到尾了,而且感觉又学到了很多
回复 使用道具 举报
风乐 发表于 2013-5-7 01:56
楼主总结的相当好,虽然很基础的东西,但还是让我从头看到尾了,而且感觉又学到了很多 ...

多谢支持。。。。
回复 使用道具 举报
黑马伍哲沂 发表于 2013-5-5 16:29
四.关于一道面试题的多种说法解析。

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

关于字符串String是不可变的我补充一点,在 HardCode Java 这本书中介绍过不可变类型,即immutable type;
怎么样将一个类变的不可变能,很简单,就是不提供属性的写方法,也就是set方法;同各属性也是私有的,同时为了保证不能被反射,这些属性都被声明为final,
也就是说你外界是无法改变的。String就是这样一个不可变类型。
回复 使用道具 举报
有个问题不知道怎么理解:

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

小明就是那个引用,而他女朋友就是指向的那个对象;
因此在change方法中要改变str和ch,只能是用this改变成员变量了,而不是传递进来的形式参数;
感觉你想问题比我们都深入,欢迎批评指正。。。
回复 使用道具 举报
Miss小强 发表于 2013-5-8 10:08
看过你对这道面试题的分析,我回去又好好看了下,从hardcode java这本书里面看到final关键字;
如果给chang ...

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

回复 使用道具 举报
Miss小强 发表于 2013-5-8 09:48
有个问题不知道怎么理解:

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

我觉得你应该把问题描述的更具体一点。   String就是用char[]数组实现的。可以看看源码。
回复 使用道具 举报
这也是群里的一个哥们问的。。。题目就这样
想了半天没明白为啥。
一个哥们的回答:
一个是基本类型 一个是对象类型 char数组多少个成员就站多少字节  String内存占用比char高
确切的说string无论里面多少数据 都至少占用28个字节
String开发时初始内存分配就是28bytes 在28的基础上 超过再递增28 依次类推
超过28 相当于你重新开了一个String
ps:Java中有没有什么方法计算某个变量占用的内存空间
回复 使用道具 举报
Miss小强 发表于 2013-5-8 10:42
这也是群里的一个哥们问的。。。题目就这样
想了半天没明白为啥。
一个哥们的回答:

上链接:http://tech.ccidnet.com/art/3539/20080508/1443759_1.html
看来属于内存优化的内容了。你认真看看就都明白了。
回复 使用道具 举报
12下一页
您需要登录后才可以回帖 登录 | 加入黑马