`
hjqhezgh
  • 浏览: 40091 次
  • 性别: Icon_minigender_1
  • 来自: 厦门
社区版块
存档分类
最新评论

String,StringBuffer,StringBuilder知识点整理

阅读更多

  对于JAVA的String在很长一段时间里都没有系统地去整理下,现在写个笔记,加深印象。

 

  String在JAVA中不属于8个基本数据类型,这个在笔试面试的时候要小心,String是JAVA中的一个类,他有自己的构造器,可以new,比较特殊的地方在于String是常量,一旦被创建出来就无法改变。

 

  举例说明 

 

String str = "a";
str = "ab";
str = "a";

 

  上面三句话实际上是产生了2个字符串对象,根据String的不可变特性,当str由a变为ab的时候,实际上并不是直接在str的内存地址上直接进行值修改,而是给了str另外一个值为ab的字符串对象的内存地址,因此a这个字符串还是存在的。

 

  另外一个要知道的知识点是字符串对象的存储区域,要知道在JVM中字符串对象被创建出来后就会被放置在一个称为字符串池的内存区域,这个区域在JAVA的内存模型中属于静态内存区,还是以上面的代码为例说明,第一句话,实际上是JVM在字符串池中创建了一个值为a的字符串对象并将该对象的内存地址赋给了str的内存区域,这样的操作就称为引用;第二句话,JVM还是在字符串池中创建了一个值为ab的字符串对象,并赋给str的内存新的一个地址,也就是ab字符串对象的地址;第三句话,由于值为a的对象已经在池中存在了,因此JVM不会再创建新的对象,而是将之前就有的对象的地址赋给了str,JVM以此来提高字符串这种常用对象的复用性。

 

  一开始的时候就讲到String也是有构造器的,也是可以new的,但是使用下面的代码

 

String str = new String("abc");

 

与直接赋字符串常量,实际的工作流程就不太一样了,JVM会在堆中开辟一块内存区域,然后将堆的地址赋给str的内存区域。因此就有了下面的经典考题

 

String str1 = "abc";
String str2 = new String("abc");
String str3 = new String("abc"); 

System.out.println(str1 == str2);//false
System.out.println(str1 == str3);//false
System.out.println(str2 == str3);//false

 

上面三句代码一共产生了几个String对象,答案应该是3个,在字符串池中1个,在堆中2个,并且所有的打印也应该都输出false,原因是他们的内存地址都不一样。

 

 然后还有一个疑问的是那么对于字符串的+操作,到底产生了多少String对象呢,这里要考虑下面两种情况。

 

 第一种情况是字符串常量连接,代码如下:

 

String str1 = "ab" + "cd";
String str2 = "abcd";
System.out.prinltn(str1 == str2);//true

  

 你发现在整个字符串连接的表达式中出现的都是字符串常量,那么这种情况,在编译期他们就直接被合并为abcd这个字符串常量了,而不需要产生中间的ab和cd,因此只产生了一个字符串对象。所以打印的结果是true,str1和str2都是池中对象的引用。

 

  第二种情况是字符串变量连接,代码如下:

 

String str1 = "ab";
String str2 = "cd";
String str3= str1 + str2;//new String("ab")+new String("cd")
String str4 = "abcd";
System.out.println(str3 == str4); // false

 

你发现在字符串连接的表达式中出现了字符串变量(只要是编译器不能直接确定值的都是变量,所以使用构造器生成的String也视为变量),这种情况,JVM会在堆中创建一个StringBuffer类,并使用ab的值作为这个StringBuffer类的初始化,然后调用StringBuffer的append方法,进行字符串追加,追加完毕后,在调用StringBuffer的toString方法在堆中创建一个String对象,并将该String对象的地址赋给str3的内存区域。所以上面的代码str1,str2,str4在池中创建了3个String对象,str3由于toString方法在堆中创建了一个String对象,由于+操作符在堆中创建了一个StringBuffer对象。所以最后的打印结果也就容易理解了,str3的值是堆中的String的地址,而str4是池中的String的地址,然后不一样。

 

  最后是String中有个方法比较特殊,叫intern方法,当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(用 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中(这里的添加是指在池中创建一个值相同的String对象,而原来的String对象还是被保留着的),并返回此 String 对象的引用。 所以看下下面代码理解下这个方法的意思。

 

String str1 = new String("abc");
System.out.println(str1 == str1.intern());//false
String str2 = "abc";
System.out.println(str2 == str1.intern());//true

 

  另外补充一点是所有的字符串操作方法,如substring,concat等,都会在堆中产生一个新的String对象。所以现在对于常见的问一段代码中产生了多少字符串对象的问题应该都能够应对自如了。

 

  然后,我们就可以开始介绍StringBuffer,由于String的不可变性,如果要对String进行追加或者裁减的操作会产生很多额外的String对象,所以我们推荐当需要进行大量的字符串操作的时候,使用StringBuffer来完成,因为StringBuffer是可变的。

 

StringBuffer sb="ab"; 
s.append("cd"); 

 

  如上面实际上就只产生一个StringBuffer对象。

 

  贴一个百度百科对StringBuffer的介绍,还是比较全面的 http://baike.baidu.com/view/1486261.htm

 

  StringBuffer和StringBuilder可以算是双胞胎了,这两者的方法没有很大区别。但在线程安全性方面,StringBuffer允许多线程进行字符操作。这是因为在源代码中StringBuffer的很多方法都被关键字synchronized 修饰了,而StringBuilder没有。

 

  做个总结就是:单线程就用StringBuilder吧,多线程就用StringBuffer,要是你只是存取下数据String就可以了。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics