String、StringBuilder、StringBuffer

字符串参考链接

java中的常量池 参考链接

1 三者区别

  • 字符修改上的区别:String是不可变字符序列,StringBuilder和StringBuffer是可变字符序列

  • 线程安全上的区别:StringBuffer线程安全,String和StringBuilder线程不安全

  • 初始化上的区别,String可以空赋值,后者不行,报错

    //String类型可以赋空值:
    String s = null;   
    String s = “abc”; 
    
    //StringBuilder和StringBuffer不能赋空值,但可以创建空对象:
    StringBuffer s = null; //结果警告:Null pointer access: The variable result can only be null at this location
    StringBuffer s = new StringBuffer();//StringBuffer对象是一个空的对象
    StringBuffer s = new StringBuffer(“abc”);//创建带有内容的StringBuffer对象,对象的内容就是字符串”
    

String的使用陷阱

  • 不可变

    String s = "abc"; //创建一个字符串
    s = s + "b"; //实际上原来的字符串对象"abc"已经被丢弃,又产生了一个新的字符串对象s + "b"(即"abcb")
    

一般情况下推荐使用 StringBuffer ,特别是字符串对象经常改变的情况下,在单线程使用字符缓冲区时推荐使用StringBuilder

一般情况下,StringBuffer的的效率是比String高的,但在以下情况下,String效率甚至比StringBuffer要高:

String S1 = "This is only a" + "simple" + "test";
StringBuffer Sb = new StringBuilder("This is only a").append("simple").append("test");
//在 JVM 眼里,这个String S1 = "This is only a" + "simple" + "test"; 其实就是:String S1 = "This is only a simple test"; 

String S2 = "This is only a";
String S3 = "simple";
String S4 = "test";
String S1 = S2 +S3 + S4;
//在这种情况下JVM会规规矩矩的按照原来的方式去做

2 创建String对象

//代码1  
String sa=new String("Hello world");            
String sb=new String("Hello world");      
System.out.println(sa==sb);  // false       
//代码2    
String sc="Hello world";    
String sd="Hello world";  
System.out.println(sc==sd);  // true   

代码1中局部变量sa,sb中存储的是JVM在中new出来的两个String对象的内存地址,虽然这两个String对象的值(char[]存放的字符序列)都是"Hello world", 但是"=="比较的是两个不同的堆地址,所以返回值是false。

代码2中局部变量sc,sd中存储的也是地址,但却都是常量池中"Hello world"指向的堆的唯一的那个拘留字符串对象

3 StringBuffer与String的可变性问题

//String   
public final class String  
{  
        private final char value[];  
  
         public String(String original) {  
              // 把原字符串original切分成字符数组并赋给value[];  
         }  
}  
  
//StringBuffer   
public final class StringBuffer extends AbstractStringBuilder  
{  
         char value[]; //继承了父类AbstractStringBuilder中的value[]  
         public StringBuffer(String str) {  
                 super(str.length() + 16); //继承父类的构造器,并创建一个大小为str.length()+16的value[]数组  
                 append(str); //将str切分成字符序列并加入到value[]中  
        }  
}  
  • String和StringBuffer中的value[]都用于存储字符序列

  • String中的是常量(final)数组,只能被赋值一次

  • StringBuffer中的value[]就是一个很普通的数组,而且可以通过append()方法将新字符串加入value[]末尾,这样也就改变了value[]的内容和大小

  • 总结:讨论String和StringBuffer可不可变,本质上是指对象中的value[]字符数组可不可变,而不是对象引用可不可变

    对象本身指的是存放在堆空间中的该对象的实例数据(非静态非常量字段)。

    对象引用指的是堆中对象本身所存放的地址.

    一般方法区和Java栈中存储的都是对象引用,而非对象本身的数据。

4 StringBuffer与StringBuilder的线程安全性问题

在线程安全性方面,StringBuffer允许多线程进行字符操作。这是因为在源代码中StringBuffer的很多方法都被关键字synchronized修饰了,而StringBuilder没有。

注意:是不是String也不安全呢?事实上不存在这个问题,String是不可变的。线程对于堆中指定的一个String对象只能读取,无法修改。试问:还有什么不安全的呢?

5 String和StringBuffer的效率问题

StringBuffer和StringBuilder可谓双胞胎,StringBuilder是1.5新引入的,其前身就是StringBuffer。StringBuilder的效率比StringBuffer稍高,如果不考虑线程安全,StringBuilder应该是首选。另外,JVM运行程序主要的时间耗费是在创建对象和回收对象上。

  • String常量与String变量的"+"操作比较

    String str="";
    str="Heart"+"Raid";
    //str="Heart"+"Raid"在编译阶段就连起来了,成为了字符串常量"HeartRaid",并指向堆中的拘留字符串对象。
    //运行时只需要将"HeartRaid"指向的拘留字符串对象地址取出,存放在局部变量str中
    
    String s1="Heart";
    String s2="Raid";
    String str="";
    str=s1+s2;
    //s1和s2存放的是两个不同的拘留字符串对象的地址。然后会通过下面三个步骤完成“+连接”:
    //StringBuilder temp=new StringBuilder(s1),
    //temp.append(s2);
    //str=temp.toString();
    
  • String对象的"累+"连接操作与StringBuffer对象的append()累和连接操作比较

  • 总结:

    • 在编译阶段就能够确定的字符串常量,完全没有必要创建String或StringBuffer对象。直接使用字符串常量的"+"连接操作效率最高。

    • StringBuffer对象的append效率要高于String对象的"+"连接操作。

    • 不停的创建对象是程序低效的一个重要原因。那么相同的字符串值能否在堆中只创建一个String对象那。显然拘留字符串能够做到这一点,除了程序中的字符串常量会被JVM自动创建拘留字符串之外,调用String的intern()方法也能做到这一点。当调用intern()时,如果常量池中已经有了当前String的值,那么返回这个常量指向拘留对象的地址。如果没有,则将String值加入常量池中,并创建一个新的拘留字符串对象。

上一篇:String StringBuffer 和 StringBuilder 的区别是什么? String 为什么是不可变的?


下一篇:22-system 和 systemBuilder类