美文网首页Java面试
Java提高篇(二)——String,StringBuffer,

Java提高篇(二)——String,StringBuffer,

作者: SnowDragonYY | 来源:发表于2018-05-31 17:58 被阅读6次

    1先来分析一下这三个类之间的关系(都是通过字符数组来实现的)

    乍一看它们都是用于处理字符串的java类,而且长得也都差不多,相信肯定有人会以为StringBuffer和StringBuilder都是继承自String这个类,即认为String类是其他两个类的超类。这种想法似乎很合理,但其实是不对的,事实上StringBuffer和StringBuilder确实是继承自某个类,但是这个类并不是String,至于是哪个类呢?我i们来看一下JDK源码(本文基于jdk1.8):
    String类部分源码

    public final class String
        implements java.io.Serializable, Comparable<String>, CharSequence {
    }
    

    StringBuffer类部分源码

     public final class StringBuffer
        extends AbstractStringBuilder
        implements java.io.Serializable, CharSequence{
    }
    

    StringBuilder类部分源码

    public final class StringBuilder
        extends AbstractStringBuilder
        implements java.io.Serializable, CharSequence{
    }
    
    

    看到这里,这三个类的关系基本清晰:StringBuffer和StringBuilder都继承自AbstractStringBuilder这个类,而AbstractStringBuilder和String都继承自Object这个类(Object是所有java类的超类)。

    String是不可变类,而StringBuffer, StringBuilder是可变类

    我们查看这三个类的源码,发现String类没有append()、delete()、insert()这三个成员方法,而StringBuffer和StringBuilder都有这些方法,这就很容易理解了(这里就不粘代码了,大家可以找源码看看)。所以我们可以归纳如下:

    String —— 字符串常量;

    StringBuffer —— 字符串变量;

    StringBuilder —— 字符串变量。

    这里再补充一点:从源代码仔细追究下去,可以发现StringBuffer和StringBuilder中的append、delete、insert这几个成员方法都是通过System类的arraycopy方法来实现的,即将原数组复制到目标数组。

    线程安全与非安全

    StringBuffer是线程安全的,而StringBuilder是非线程安全的,至于原因我们依然可以从它们的源码中找到。

    StringBuffer类的部分源码

        public StringBuffer(CharSequence seq) {
            this(seq.length() + 16);
            append(seq);
        }
    
        @Override
        public synchronized int length() {
            return count;
        }
    
        @Override
        public synchronized int capacity() {
            return value.length;
        }
    
    
        @Override
        public synchronized void ensureCapacity(int minimumCapacity) {
            super.ensureCapacity(minimumCapacity);
        }
    
        /**
         * @since      1.5
         */
        @Override
        public synchronized void trimToSize() {
            super.trimToSize();
        }
    
        /**
         * @throws IndexOutOfBoundsException {@inheritDoc}
         * @see        #length()
         */
        @Override
        public synchronized void setLength(int newLength) {
            toStringCache = null;
            super.setLength(newLength);
        }
    
        /**
         * @throws IndexOutOfBoundsException {@inheritDoc}
         * @see        #length()
         */
        @Override
        public synchronized char charAt(int index) {
            if ((index < 0) || (index >= count))
                throw new StringIndexOutOfBoundsException(index);
            return value[index];
        }
    
        /**
         * @since      1.5
         */
        @Override
        public synchronized int codePointAt(int index) {
            return super.codePointAt(index);
        }
    
        /**
         * @since     1.5
         */
        @Override
        public synchronized int codePointBefore(int index) {
            return super.codePointBefore(index);
        }
    
        /**
         * @since     1.5
         */
        @Override
        public synchronized int codePointCount(int beginIndex, int endIndex) {
            return super.codePointCount(beginIndex, endIndex);
        }
    
        /**
         * @since     1.5
         */
        @Override
        public synchronized int offsetByCodePoints(int index, int codePointOffset) {
            return super.offsetByCodePoints(index, codePointOffset);
        }
    
        /**
         * @throws IndexOutOfBoundsException {@inheritDoc}
         */
        @Override
        public synchronized void getChars(int srcBegin, int srcEnd, char[] dst,
                                          int dstBegin)
        {
            super.getChars(srcBegin, srcEnd, dst, dstBegin);
        }
    
        /**
         * @throws IndexOutOfBoundsException {@inheritDoc}
         * @see        #length()
         */
        @Override
        public synchronized void setCharAt(int index, char ch) {
            if ((index < 0) || (index >= count))
                throw new StringIndexOutOfBoundsException(index);
            toStringCache = null;
            value[index] = ch;
        }
    
        @Override
        public synchronized StringBuffer append(Object obj) {
            toStringCache = null;
            super.append(String.valueOf(obj));
            return this;
        }
    
        @Override
        public synchronized StringBuffer append(String str) {
            toStringCache = null;
            super.append(str);
            return this;
        }
    

    StringBuilder类的部分源码

       public StringBuilder() {
            super(16);
        }
    
        /**
         * Constructs a string builder with no characters in it and an
         * initial capacity specified by the {@code capacity} argument.
         *
         * @param      capacity  the initial capacity.
         * @throws     NegativeArraySizeException  if the {@code capacity}
         *               argument is less than {@code 0}.
         */
        public StringBuilder(int capacity) {
            super(capacity);
        }
    
        /**
         * Constructs a string builder initialized to the contents of the
         * specified string. The initial capacity of the string builder is
         * {@code 16} plus the length of the string argument.
         *
         * @param   str   the initial contents of the buffer.
         */
        public StringBuilder(String str) {
            super(str.length() + 16);
            append(str);
        }
    
        /**
         * Constructs a string builder that contains the same characters
         * as the specified {@code CharSequence}. The initial capacity of
         * the string builder is {@code 16} plus the length of the
         * {@code CharSequence} argument.
         *
         * @param      seq   the sequence to copy.
         */
        public StringBuilder(CharSequence seq) {
            this(seq.length() + 16);
            append(seq);
        }
    
        @Override
        public StringBuilder append(Object obj) {
            return append(String.valueOf(obj));
        }
    
        @Override
        public StringBuilder append(String str) {
            super.append(str);
            return this;
        }
    
    

    我们可以发现StringBuffer类中的大部分成员方法都被synchronized关键字修饰,而StringBuilder类没有出现synchronized关键字;至于StringBuffer类中那些没有用synchronized修饰的成员方法,如insert()、indexOf()等,通过源码上的注释可以知道,它们是调用StringBuffer类的其他方法来实现同步的。注意:toString()方法也是被synchronized关键字修饰的。

    以下下通多实例说明,String,Stringbuilder,StringBuffer的运行速度

    代码如下

    public class TestOne {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
             /*   1   */
            String string = "a" + "b" + "c";
            /*   2   */
            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append("a");
            stringBuffer.append("b");
            stringBuffer.append("c");
            
             string = stringBuffer.toString();
        }
    
    }
    
    

    看到这部分代码很多程序员都表示,stringbuffer快于string+。唯有一些工作经验较丰富人表示是string+的速度快。
      而这里确实string+的写法要比stringbuffer快,是因为在编译这段程序的时候,编译器会进行常量优化,它会将a、b、c直接合成一个常量abc保存在对应的class文件当中。这里贴出了编译后的class文件的反编译代码,如下。

    public class TestOne {
    
        public static void main(String[] args) {
              String string = "abc";
    
                StringBuffer stringBuffer = new StringBuffer();
                stringBuffer.append("a");
                stringBuffer.append("b");
                stringBuffer.append("c");
                string = stringBuffer.toString();
        }
    
    }
    
    

    因此给各位猿友一个建议,如果是有限个string+的操作,可以直接写成一个表达式的情况下,那么速度其实与stringbuffer是一样的,甚至更快,因此有时候没必要就几个字符串操作也要建个stringbuffer(如果中途拼接操作的字符串是线程间共享的,那么也建议使用stringbuffer,因为它是线程安全的)。但是如果把string+的操作拆分成语句去进行的话,那么速度将会指数倍下降。

    总之,我们大部分时候的宗旨是,如果是string+操作,我们应该尽量在一个语句中完成。如果是无法做到,并且拼接动作很多,比如数百上千成万次,则必须使用stringbuffer,不能用string+,否则速度会很慢。
    而在考虑线程安全的时候,用StringBuffer。在不考虑线程安全的时候用StringBuilder则会更快,应为代码中没有Synchronized机制。

    相关文章

      网友评论

        本文标题:Java提高篇(二)——String,StringBuffer,

        本文链接:https://www.haomeiwen.com/subject/azaqsftx.html