Java之String、StringBuffer、StringBuilder

参考自:
http://www.iteye.com/topic/522167

综述

String(大姐,出生于JDK1.0时代) 不可变字符序列
StringBuffer(二姐,出生于JDK1.0时代) 线程安全的可变字符序列
StringBuilder(小妹,出生于JDK1.5时代) 非线程安全的可变字符序列

  • 在编译阶段就能够确定的字符串常量,完全没有必要创建String或StringBuffer对象。直接使用字符串常量的”+”连接操作效率最高。
  • StringBuffer对象的append效率要高于String对象的”+”连接操作。
  • 在线程安全性方面,多线程编程中StringBuffer比StringBuilder更安全。StringBuffer允许多线程进行字符操作。这是因为在源代码中StringBuffer的很多方法都被关键字synchronized 修饰,而StringBuilder没有。
  • 注意:是不是String也不安全呢?事实上不存在这个问题,String是不可变的。线程对于堆中指定的一个String对象只能读取,无法修改。试问:还有什么不安全的呢?

详细介绍

String

对象的创建

1、关于类对象的创建,很普通的一种方式就是利用构造器,String类也不例外:String s=new String(“Hello world”); 问题是参数”Hello world”是什么东西,也是字符串对象吗?莫非用字符串对象创建一个字符串对象?
2、当然,String类对象还有一种大家都很喜欢的创建方式:String s=”Hello world”; 但是有点怪呀,怎么与基本数据类型的赋值操作(int i=1)很像呀?

预备知识
  • Java class文件和常量池
    http://blog.csdn.net/luanlouis/article/details/39892027

    Java程序要运行,首先需要编译器将源代码文件编译成字节码文件(也就是.class文件)。然后在由JVM解释执行。

    class文件是8位字节的二进制流 。这些二进制流的涵义由一些紧凑的有意义的项 组成。比如class字节流中最开始的4个字节组成的项叫做魔数(magic),其意义在于分辨class文件(值为0xCAFEBABE)与非class文件。class字节流大致结构如下图左侧。
    这里写图片描述
    这里写图片描述

    • JVM运行class文件:JVM首先会用类装载器加载进class文件。然后需要创建许多内存数据结构来存放class文件中的字节数据。比如class文件对应的类信息数据、常量池结构、方法中的二进制指令序列、类方法与字段的描述信息等等。当然,在运行的时候,还需要为方法创建栈帧等。这么多的内存结构当然需要管理,JVM会把这些东西都组织到几个“运行时数据区 ”中。这里面就有我们经常说的“方法区 ”、“堆 ”、“Java栈 ”等
创建实例
  • String s=new String(“Hello world”);

JVM为”Hello world”在堆中创建了一个拘留字符串( 值得注意的是:如果源程序中还有一个”Hello world”字符串常量,那么他们都对应了同一个堆中的拘留字符串)。然后用这个拘留字符串的值来初始化堆中用new指令创建出来的新的String对象,局部变量s实际上存储的是new出来的堆对象地址。 大家注意了,此时在JVM管理的堆中,有两个相同字符串值的String对象:一个是拘留字符串对象,一个是new新建的字符串对象。

字符串相等
//代码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”。 因此”==”比较的是两个不同的堆地址。代码2中局部变量sc,sd中存储的也是地址,但却都是常量池中”Hello world”指向的堆的唯一的那个拘留字符串对象的地址 。自然相等了。

字符串“+”操作
public static void plusTest() {
        System.out.println("starting String plusTest");
        // 代码1
        String sa = "ab";
        String sb = "cd";
        String sab = sa + sb;
        String s = "abcd";
        System.out.println(sab == s); // false
        // 代码2
        String sc = "ab" + "cd";
        String sd = "abcd";
        System.out.println(sc == sd); // true
    }
  • sa,sb存储的是堆中两个拘留字符串对象的地址
  • 当执行sa+sb时,JVM首先会在堆中创建一个StringBuilder类,同时用sa指向的拘留字符串对象完成初始化;然后调用append方法完成对sb所指向的拘留字符串的合并操作,接着调用StringBuilder的toString()方法在堆中创建一个String对象,最后将刚生成的String对象的堆地址存放在局部变量sab中。
  • 局部变量s存储的是常量池中”abcd”所对应的拘留字符串对象的地址。
  • -

StringBuffer

  • 源码比较
//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)数组,只能被赋值一次(value在默认构造函数里初始化!!)。比如:new String(“abc”)使得value[]={‘a’,’b’,’c’},之后这个String对象中的value[]再也不能改变了。这也正是大家常说的,String是不可变的原因 。
  • StringBuffer中的value[]就是一个很普通的数组,而且可以通过append()方法将新字符串加入value[]末尾(构造的长度是str.length()+16))。这样也就改变了value[]的内容和大小了。
  • 总结,讨论String和StringBuffer可不可变。本质上是指对象中的value[]字符数组可不可变,而不是对象引用可不可变。

StringBuilder

效率
  1. StringBuilder的效率比StringBuffer稍高,如果不考虑线程安全,StringBuilder应该是首选。
  2. 实现与StringBuffer相同的接口
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值