Java中String、StringBuilder、StringBuffer的区别

Java中String、StringBuilder、StringBuffer的区别

String

是被 final 修饰的,包括value也被final修饰,因此无法修改值,每一次赋值(修改)并不会改变原对象的值而是另外新建一个对象来保存新的值并指向新对象,相当于重新new一个出来,故而频繁修改String会导致创建了很多String常量而浪费很多内存。

        String a = new String("Hello");
       String b = new String("Hello");
       System.out.println(a == b);              //输出:false,String的==不是比较值而是比较对象。
       a = "你好";
       b = "你好";
       System.out.println(a == b);           //输出:true,证明a和b指向同一个对象。
       a = a + "呀";
       System.out.println("a为:"+a+",b为:"+b+",是否同一对象:"+(a == b));//输出:a为:你好呀,b为:你好,是否同一对象:false
//a内容已修改,但b没有,证明已经重新指向一个新的对象,并不是修改原来的对象

StringBuilder、StringBuffer

StringBuilder和StringBuffer两者都是 extends AbstractStringBuilder implements Serializable, Comparable<StringBuilder>, CharSequence,继承了AbstractStringBuilder,默认new的时候都是16位byte[],两个都是可变长度字符串,本质是当内容长度需要扩容时,新建一个byte[]数组,扩容长度默认为原长度+2(即将数组长度变成原长度*2+2),如果新加字符串长度大于这个长度,则使用新加字符串长度作为扩容长度,然后将内容复制到新数组去。

    private void ensureCapacityInternal(int minimumCapacity) {
       int oldCapacity = this.value.length >> this.coder;
       if (minimumCapacity - oldCapacity > 0) {
           this.value = Arrays.copyOf(this.value, this.newCapacity(minimumCapacity) << this.coder);
      }
  }

private int newCapacity(int minCapacity) {
       int oldCapacity = this.value.length >> this.coder;
       int newCapacity = (oldCapacity << 1) + 2;
       if (newCapacity - minCapacity < 0) {
           newCapacity = minCapacity;
      }

       int SAFE_BOUND = 2147483639 >> this.coder;
       return newCapacity > 0 && SAFE_BOUND - newCapacity >= 0 ? newCapacity : this.hugeCapacity(minCapacity);
  }

   private int hugeCapacity(int minCapacity) {
       int SAFE_BOUND = 2147483639 >> this.coder;
       int UNSAFE_BOUND = 2147483647 >> this.coder;
       if (UNSAFE_BOUND - minCapacity < 0) {
           throw new OutOfMemoryError();
      } else {
           return minCapacity > SAFE_BOUND ? minCapacity : SAFE_BOUND;
      }
  }

StringBuilder和StringBuffer为了弥补String在操作时时间和内存上效率太低,实现基本一样,但是StringBuffer大量函数被synchronized修饰,实现了线程安全,而StringBuilder没有,因此StringBuilder效率高,线程不安全,StringBuffer效率相对StringBuilder低,但线程安全。

        String s = "";
       long starts = new Date().getTime();
       for (int i = 0; i < 100000; i++) {
           s += i;
      }
       System.out.println(new Date().getTime() - starts);


       StringBuilder sbuilder = new StringBuilder();
       long startsb1 = new Date().getTime();
       for (int i = 0; i < 100000; i++) {
           sbuilder.append(i);
      }
       System.out.println(new Date().getTime() - startsb1);



       StringBuffer sbuffer = new StringBuffer();
       long startsb2 = new Date().getTime();
       for (int i = 0; i < 100000; i++) {
           sbuffer.append(i);
      }
       System.out.println(new Date().getTime() - startsb2);

输出结果:

​看到时间差距还是很大的​

总结

  1. String 不能修改值,每次都是建立新常量对象并指向,所以时间和内存消耗都比较高。适合在不需要频繁操作时使用。

  2. StringBuffer 线程安全,虽然效率比StringBuilder低,但仍远高于String。适合在多线程需要频繁操作的时候使用。

  3. StringBuilder 线程不安全,但是效率比StringBuffer高,适合在单线程需要频繁操作的时候使用。

延伸

它们在jdk9前本质上是char[],jdk9后修改成byte[](字符为英文时只需要1字节就能表示(此时源码中私有变量coder为0),中文时才会和char一样使用2字节(此时源码中coder为1),节省内存空间,获取String.length()也是return value.length >> coder,但也使中文等字符串在内存足够的情况下最大长度缩短一半,但也有10.7亿多长度,也够了哈)

以上都系自我见解啦,有什么不适合的请指导下

posted @ 2020-09-28 10:57  lionjian  阅读(297)  评论(0)    收藏  举报