String类与StringBuffer类的研究


1.了解String的内部实现


String的部分源码如下:


public final class String

    implements java.io.Serializable, Comparable<String>, CharSequence {


    private final char value[];  // 用于存储具体值的char数组,定义为final,不可改变

    privateinthash;// Default to 0

    private staticfinallongserialVersionUID = -6849794470754667710L;

    private staticfinal ObjectStreamField[]serialPersistentFieldsnew ObjectStreamField[0];

    /**

     * 最常用的构造函数,通过传入的String对象对其进行拷贝。

     */

    public String(String original) {

        this.value =original.value;

        this.hash =original.hash;

    }


public String(charvalue[]) {

        this.value = Arrays.copyOf(value,value.length);

    }


//..其他构造方法省略由上可以发现,String类通过内部维护一个final的char数组来实现字符串,即String类可以看作是对char数组的一层包装



2.关于使用“”创建一个String对象


除了使用String对象,还可以使用“”来创建一个String对象,如:

String str = new String("test");


该行代码对应的字节码:

0: new           #16                 // class java/lang/String

创建一个对象,并将其引用值压入栈顶。

3: dup

复制栈顶的值并将复制值压入栈顶。    

4: ldc           #18                 // String test

将常量值从常量池中推送至栈顶

6: invokespecial #20                 // Method java/lang/String."<init>":(Ljava/lang/String;)V

调用实例初始化方法

9: astore_1

将引用类型数值存入本地第一个变量中


上面字节码在执行时,会先在String常量池中查找“test”字符串,若无则创建一个新的字符串,然后通过传入该字符串作为String构造函数的参数再创建一个对象,所以上面的语句实际上创建了两个String对象,而一个是在常量池中,一个则是纯正的对象放在java堆上。

代码测试:

public class test {


public static void main(String[] args) {

    String str  ="test";

    String str2 =new String("test");

    System.out.println(str==str2);

    System.out.println(str=="test");

}

}

结果为: 
false
        ture
结论:使用“”创建String对象会先在String常量池中查询是否存放,存在则指向,不存在则创建。而使用String类的构造函数则与创建一个普通对象一样,存放在java堆上。

3.关于使用字符串的连接 “+”

String类中用于存放具体值的char数组是定义为final的,所以是不可改变的,java语法中可以使用“+”号来连接字符串,可以推测是新建了一个String对象,具体需要从字节码中了解。

测试代码:

String str1 ="test1";

Stringstr2 ="test2";

Stringsrt3 =str1 + str2;


对应的字节码:

0: ldc           #16                 // String test1

       2: astore_1

       3: ldc           #18                 // String test2

       5: astore_2

       6: new           #20                 // class java/lang/StringBuilder

       9: dup

      10: aload_1

      11: invokestatic  #22                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;

      14: invokespecial #28                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V

      17: aload_2

      18: invokevirtual #31                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

      21: invokevirtual #35                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;

      24: astore_3



结论:使用“+”号来连接字符串,实际上是通过创建一个StringBuilder对象,使用append方法连接,再使用toString方法返回一个字符串。所以当不断的对字符串进行“+”号连接时,实际上会不断创建 StringBuffer和String对象,开销很大,应该自己使用一个StringBuffer对象来完成。

4.了解StringBuffer的内部实现

StringBuffer继承自 AbstractStringBuilder,所以先看其部分源代码:

abstract class AbstractStringBuilder implements Appendable, CharSequence {


    char[] value;

    int count;


    AbstractStringBuilder() {

    }


    AbstractStringBuilder(int capacity) {

        value = new char[capacity];

    }


public AbstractStringBuilder append(String str) {

        if (str ==null)

            return appendNull();

        int len =str.length();

        ensureCapacityInternal(count + len);

        str.getChars(0, len, value, count);

        count += len;

        return this;

    }


StringBuffer部分源码:


public final class StringBuffer

    extends AbstractStringBuilder

    implements java.io.Serializable, CharSequence

{


    private transient char[] toStringCache;

    static finallongserialVersionUID = 3388685877147921107L;


    public StringBuffer() {

        super(16);

    }


    public StringBuffer(intcapacity) {

        super(capacity);

    }


  public synchronized StringBuffer append(Stringstr) {

        toStringCache =null;

        super.append(str);

        return this;

    }


public synchronized String toString() {

        if (toStringCache ==null) {

            toStringCache = Arrays.copyOfRange(value, 0,count);

        }

        return new String(toStringCache,true);

    }

}



可以看出, AbstractStringBuilder类中使用一个char数组来存储字符串的值,而且没有像String类中被定义为final,可以通过append方法来改变char数组实现字符串的连接等操作(append方法中会使用native方法对char数组进行操作,效率很高)。而StringBuffer类在其基础上多了一个char数组作为缓存,将append方法设置为同步方法,然后清楚缓存后调用父类的方法进行处理,还实现了toString方法。

5.为什么String类是final的,保存具体值的char数组也是final的 (个人理解)

我们知道用“”是在常量池中创建String对象,这里面的对象可以复用,下次创建同样的String时就不用再进行额外的开销,这是java用于优化性能的方法。因为常量池中的String会在不同的地方被引用,如果其值可以被修改,那么其他调用的地方也被修改了,造成了错乱,所以String的值应该是不可变的。为了保证这个约束,String类被设计成final的,防止子类破坏这个约定。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值