jdk源码分析(1) string,stringBuffer和stringbuilder的异同

1、String分析

首先是创建一个String,可以看到一般有两种方法,就是Sting a = “abc”。另一种是String b = new String(“b”)

直接赋值
jvm为了提升性能和减少内存开销,避免字符的重复创建,其维护了一块特殊的内存空间,即字符串池,当需要使用字符串时,先去字符串池中查看该字符串是否已经存在,如果存在,则可以直接使用,如果不存在,初始化,并将该字符串放入字符创常量池中。使用String直接赋值,Sting a = "abc"可能创建一个或者不创建对象,如果”abc”在字符串池中不存在,会在java字符串池中创建一个String对象(”abc”),然后str指向这个内存地址,无论以后用这种方式创建多少个值为”abc”的字符串对象,始终只有一个内存地址被分配。==判断的是对象的内存地址,而equals判断的是对象内容。

使用new Sting创建

public String(char value[]) {
   this.value = Arrays.copyOf(value, value.length);
 }
/*其中value的属性是*/
 private final char value[];

因为value是final类型,所以不可能再指向其他地方,所以每一次String指向一个新的目标,其都是新创建了一个value,也就是新创建了一个String类型的变量。

总结
直接赋值应该是比使用new Sting创建要好一点,因为如果内存空间中已经有了这个值,那么就不需要在重新申请空间来创建,而new Sting创建则是每一次都需要重新创建一个对象。但是如果每一次赋值的值在内存空间中都不会有,那么这两者就没有区别了。总的来说,String类型还是比较消耗性能的,因为他一般每次都需要重新创建,而不会复用之前的。

2、StringBuffer和Stringbuilder的区别

相信大家都知道他们两最大的区别就是Stringbuild是线程不安全的,Stringbuffer是线程安全的,但是效率StringBuild较StringBuffer高一些。所以在一些单线程或者保证不会有线程不安全的情况下,可以使用StringBuilder来最大化性能提升。

首先我们来看下StringBuilder,StringBuilder类继承了AbstractStringBuilder,并实现了Serializable,说明了这个类对象是可以进行网络间的数据传输的。
在这里插入图片描述
我们来看下StringBuilder类对象的初始化

public StringBuilder() {
 	super(16);
}

AbstractStringBuilder(int capacity) {
    value = new char[capacity];
}

可以看出来创建一个StringBuilder对象就是创建了一个16byte大小的char数组,再来看下append操作是如何进行的

public StringBuilder append(String str) {
 	/*实际操作是在AbstractStringBuilder实现*/
     super.append(str);
     return this;
 }```

```java
public AbstractStringBuilder append(String str) {
    if (str == null)
        return appendNull();
    int len = str.length();
    /*这个函数就是保证当前的char数组能够确保容纳下全部的字符
       1、在第一次append时,count=0,len为新加入的字符串的长度,这时候因为一开始创建了16byte大小的数组,如果len<=16,则直接放入开始时创建的数组即可
       如果len>16,这时候需要进行扩容,就是在申请一块更加大的空间,这时暂定的大小为34(原来的空间*2+2)注意后面会不断增大,然后比较len和34的大小,如果34较大,则先取34为申请的空间的大小,不然为len的长度为新申请的长度
       注意这里还会判断是否越界这种情况,最大的空间可以为0x7fffffff-8
       2、如果不是第一次append,则在原来的count的长度上继续申请,新增加的字符串的长度超过当前的char数组的容量时就进行扩容*/
    ensureCapacityInternal(count + len);
    /*将String的字符串放入value这个存放字符串的char数组中*/
    str.getChars(0, len, value, count);
    /*更新count的值*/
    count += len;
    return this;
}

然后再去看下StringBuffer的情况,发现和StingBuilder几乎一致,只有两处地方有些区别

/*1、第一个区别就是StringBuffer类的所有函数前都使用了synchronized这个重型锁来保证线程安全*/
public synchronized StringBuffer append(Object obj) {
	/*2、多了一个toStringCache这个缓存,这个toStringCache是一个缓存,只要我们进行修改操作,这个缓存就会被刷新,其主要用在toString这个函数中*/
    toStringCache = null;
    super.append(String.valueOf(obj));
    return this;
}

public synchronized String toString() {
	/*第一次如果这个缓存为null,则复制一个最新的值到缓存中*/
   if (toStringCache == null) {
        toStringCache = Arrays.copyOfRange(value, 0, count);
    }
    /*之后如果没有进行更新操作,多次进行toString可以直接从缓存中取,而不需要每一次都进行一次拷贝
    */
    return new String(toStringCache, true);
}

3、StringBuffer和StringBuilder比较String的优势

通过上面的分析可以看到,因为StringBuffer和StringBuilder都是维护了内存中的一个char数组,一开始会申请一段较大的空间,如果之后再不断append过程中发现char数组内存不够了,那么就会进行扩容,扩容的大小为(原来的大小*2+2),扩容的方式也一般是重新申请一段扩容后大小的空间,然后将原来的数据复制到新的内存空间中。

那么我们一般再往扩容后的数组中添加数据时,除非继续添加较多的数据,一般情况下,不会引发再次扩容,这时候就可以将数据直接放入到char数组中,但是如果是String类型,无论第二次添加多少数据,每一次都还是需要重新申请一段空间,然后将原来的数据和新添加的数据一起放入到新申请的空间中。这种情况下String的消耗就严重多了。

简单的说,StringBuffer和StringBuilder利用先申请一段较大的内存,如果新增加的数据在原来的内存空间还容纳的下,可以不用申请新的空间,自然也少了复制原来空间中数据的性能消耗。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值