String,StringBuffer,StringBuilder的异同

String,StringBuffer,StringBuilder的异同

说明:该文档系浙江大学java应用技术第一次作业。作业内容阅读String、StringBuffer与StringBuilder类的源程序,分析比较三个类的结构、功能设置、相同与不同。

http://10.214.47.99:8080/masm/Koders/lang/String.htm
http://10.214.47.99:8080/masm/Koders/lang/StringBuffer.htm
http://10.214.47.99:8080/masm/Koders/lang/StringBuilder.htm

文章目录

  • String,StringBuffer,StringBuilder的异同
        • 小结一:StringBuffer和StringBuilder内存利用率更高
        • 6.String,StringBuilder, StringBuffer性能测试
        • 7. JVM 自动优化+String的验证
        • 参考资料:

####1. String,StringBuffer, StringBulider 不可被继承

public final class String implements Serializable, Comparable, CharSequence
public final class StringBuffer implements Serializable, CharSequence
public final class StringBuilder

关键字final意味着String,StringBuffer, StringBulider不可被继承

####2. String成员函数返回值特点:new
String通过char[]来保存字符串,并且类成员函数的操作大都会把结果放到新开辟的一块内存然后返回一个新的字符串对象。String的操作不会改变原有的字符串。

 public String concat(String str)
  {
    if (str.count == 0)
      return this;
    if (count == 0)
      return str;
    char[] newStr = new char[count + str.count];
    VMSystem.arraycopy(value, offset, newStr, 0, count);
    VMSystem.arraycopy(str.value, str.offset, newStr, count, str.count);
    // Package constructor avoids an array copy.
    return new String(newStr, 0, newStr.length, true);
  }

public String toLowerCase(Locale loc)
  {
    // First, see if the current string is already lower case.
    boolean turkish = "tr".equals(loc.getLanguage());
    int i = count;
    int x = offset - 1;
    while (--i >= 0)
      {
        char ch = value[++x];
        if ((turkish && ch == '\u0049')
            || ch != Character.toLowerCase(ch))
          break;
      }
    if (i < 0)
      return this;

    // Now we perform the conversion. Fortunately, there are no multi-character
    // lowercase expansions in Unicode 3.0.0.
    char[] newStr = (char[]) value.clone();
    do
      {
        char ch = value[x];
        // Hardcoded special case.
        newStr[x++] = (turkish && ch == '\u0049') ? '\u0131'
          : Character.toLowerCase(ch);
      }
    while (--i >= 0);
    // Package constructor avoids an array copy.
    return new String(newStr, offset, count, true);
  }

public char[] toCharArray()
  {
    // XXX ORP 1.0.9 crashes on (char[]) clone() during bootstrap, so we
    // omit this optimization for now.
    // if (count == value.length)
    //   return (char[]) value.clone();
    char[] copy = new char[count];
    VMSystem.arraycopy(value, offset, copy, 0, count);
    return copy;
  }

值得一提的是:对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象

####3. StringBuffer和StringBuilder直接在原字符串上进行操作
我做了一个小测试供读者直观感受

public class test {
    public static void main(String []args) {
        StringBuffer s = new StringBuffer("123");
        s.replace(1,2,"ABC");
        String a = new String("123");
        a.replace("A","ABC");
        StringBuilder b = new StringBuilder("123");
        b.replace(1,2,"ABC");
        System.out.println(s); // out: 1ABC3
        System.out.println(a); // out: 123
        System.out.println(b); // out: 1ABC3
    }
}

其实我们对比一下三个类的replace源码就能很直观的看出来

/*String*/
public String replace(char oldChar, char newChar)
  {
    if (oldChar == newChar)
      return this;
    int i = count;
    int x = offset - 1;
    while (--i >= 0)
      if (value[++x] == oldChar)
        break;
    if (i < 0)
      return this;
    char[] newStr = (char[]) value.clone();
    newStr[x] = newChar;
    while (--i >= 0)
      if (value[++x] == oldChar)
        newStr[x] = newChar;
    // Package constructor avoids an array copy.
    return new String(newStr, offset, count, true);
  }
  
  /*StringBuffer*/
  public synchronized StringBuffer replace(int start, int end, String str)
  {
    if (start < 0 || start > count || start > end)
      throw new StringIndexOutOfBoundsException(start);

    int len = str.count;
    // Calculate the difference in 'count' after the replace.
    int delta = len - (end > count ? count : end) + start;
    ensureCapacity_unsynchronized(count + delta);

    if (delta != 0 && end < count)
      VMSystem.arraycopy(value, end, value, end + delta, count - end);

    str.getChars(0, len, value, start);
    count += delta;
    return this;
  }

/*StringBuilder*/
 public StringBuilder replace(int start, int end, String str)
  {
    if (start < 0 || start > count || start > end)
      throw new StringIndexOutOfBoundsException(start);

    int len = str.count;
    // Calculate the difference in 'count' after the replace.
    int delta = len - (end > count ? count : end) + start;
    ensureCapacity(count + delta);

    if (delta != 0 && end < count)
      System.arraycopy(value, end, value, end + delta, count - end);

    str.getChars(0, len, value, start);
    count += delta;
    return this;
  }

小结一:StringBuffer和StringBuilder内存利用率更高

由于String对象每次操作都是返回一个新的字符串,我们可以想象在一个程序中假如存在大量的String对象,或者更形象点有个10000次的循环中有着String对象的操作,这些操作会产生大量冗余的内存,不利于程序的性能。
不过需要指出的是,在一些特殊情况下例如字符串常量的拼接,String对象比StringBuffer和StringBuilder更有速度的上的优势,例如

String S1 = “This is only a” + “ simple” + “ test”;
StringBuffer S2 = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”);

S1的速度要比S2快。原因是在JVM看来 String S1 = “This is only a” + “ simple” + “test”; 其实就是:String S1 = “This is only a simple test”;而S2的构建则设计几个对象的构造,自然会慢一些。

####4. StringBuffer的特点关键字**“synchronized”
阅读StringBuffer的源码不难发现很多成员前都有”synchronized"的关键字,这也意味着StringBuffer是
线程安全**的

public synchronized StringBuffer append(char ch)
  {
    ensureCapacity_unsynchronized(count + 1);
    value[count++] = ch;
    return this;
  }
  
public synchronized StringBuffer append(char[] data, int offset, int count)
  {
    if (offset < 0 || count < 0 || offset > data.length - count)
      throw new StringIndexOutOfBoundsException();
    ensureCapacity_unsynchronized(this.count + count);
    VMSystem.arraycopy(data, offset, value, this.count, count);
    this.count += count;
    return this;
  }

synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:

  1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
  2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
  3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
  4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

理解:

  1. 一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞。
  2. 当一个线程访问对象的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该对象中的非synchronized(this)同步代码块。
  3. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
  4. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
  5. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。——摘自一博客
    参考: http://blog.csdn.net/luoweifu/article/details/46613015

插语:我也不懂什么是线程安全什么是线程不安全,这学期才开始学操作系统。网上学习了下,大致是这个意思:

  • 线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。
  • 线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据
  • 线程安全问题都是由全局变量及静态变量引起的。
    参考:http://blog.csdn.net/ghevinn/article/details/37764791

所以说StringBuffer有’'synchronized"修饰,所以是线程安全的,StringBuilder则是线程不安全的。

####5.StringBuilder线程不安全
StringBuilder和StringBuffer功能上差不多,但是StringBuilder没有关键字”synchronized"修饰,也就意味着StringBuilder是线程不安全的。

在单线程程序下,StringBuilder效率更快,因为它不需要加锁,不具备多线程安全而StringBuffer则每次都需要判断锁,效率相对更低。
——http://www.cnblogs.com/myhappylife/p/6542633.html

6.String,StringBuilder, StringBuffer性能测试

该测试来自海子博主的博客,感谢海子这篇深入浅出的文章。但是里面反编译这块的知识我还是看不懂,甚是遗憾。
博客链接为:http://www.cnblogs.com/dolphin0520/p/3778589.html
博主Mac2016, IntelliJ IDEA的测试结果为:
String,StringBuffer,StringBuilder的异同_第1张图片
测试代码如下:

public class Main {
    private static int time = 50000;
    public static void main(String[] args) {
        testString();
        testStringBuffer();
        testStringBuilder();
        test1String();
        test2String();
    }
     
     
    public static void testString () {
        String s="";
        long begin = System.currentTimeMillis();
        for(int i=0; i

7. JVM 自动优化+String的验证

上面提到过,string += "hello"会被JVM自动优化为

StringBuilder str = new StringBuilder(string);
str.append(“hello”);
str.toString();

海子博主给了测试代码,博主Mac2016, IntelliJ IDEA的测试结果为:
String,StringBuffer,StringBuilder的异同_第2张图片
和海子博主在win7上的测试结果有点差别,我也不知道是为什么, 被自己菜哭了(╥╯^╰╥)。海子博主的结果为:
String,StringBuffer,StringBuilder的异同_第3张图片

测试代码:

public class Main {
    private static int time = 50000;
    public static void main(String[] args) {
        testString();
        testOptimalString();
    }


    public static void testString () {
        String s="";
        long begin = System.currentTimeMillis();
        for(int i=0; i

参考资料:

http://www.cnblogs.com/dolphin0520/p/3778589.html
http://blog.csdn.net/ghevinn/article/details/37764791
http://blog.csdn.net/rmn190/article/details/1492013
http://www.cnblogs.com/myhappylife/p/6542633.html
浙江大学楼学庆老师提供的的String,StringBuffer,StringBuilder的源码

感谢大佬们的分享,收益良多(~ ̄▽ ̄)~

你可能感兴趣的:(java)