• String,StringBuffer和StringBuilder源码解析[基于JDK6]


    最近指导几位新人,学习了一下String,StringBuffer和StringBuilder类,从反馈的结果来看,总体感觉学习的深度不够,没有读出东西。其实,JDK的源码是越读越有味的。下面总结一下我读这些源码的收获吧。
    注意:虽然源码的版本是JDK6,但是个人觉得学习这个版本的源码对于理解数据结构非常有帮助,因为String就是一个数据结构,它是char []的封装,实现了很多对char []的操作

    第一部分:String源码解析

    (1)String实现了CharSequence接口,这个接口的方法不多,就下面几个:

    1
    2
    3
    4
    intlength();
    charcharAt(intindex);
    CharSequence subSequence(intstart,intend);
    publicStringtoString();

    关于“接口”的深刻理解,等抽空再写出来吧。我个人感觉,“接口”这个名称很容易让人产生误解的,不利于面向接口编程。面向对象编程是语言设计上的一个壮举,实现了子类继承父类这样类似生物学的完美逻辑,但是接口概念的提出,彻底颠覆了类和对象的观念,抛弃了类和对象的观念,将思维的灵活性推向了极致。
    (2)String的成员变量

    1
    2
    3
    privatefinalcharvalue[];
    privatefinalintoffset;
    privatefinalintcount;

    final修饰一个成员变量(属性),必须要显示初始化。通常有两种初始化方式,一种是在变量声明的时候初始化;第二种方法是在声明变量的时候不赋初值,但是要在这个变量所在的类的所有的构造函数中对这个变量赋初值。其实,从数据结构的角度来看,String就是一个数据结构,它是char []的封装,具体的属性包括:char [] value,存放字符;offset表示偏移量,count表示char的数量。value.length和count还是有区别的,这一点在AbstractStringBuilder类的体现的更明确。

    (3)和StringBuffer,StringBuilder的关系:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    publicString(StringBuffer buffer)
    {
        synchronized(buffer)
        {
          this.value=Arrays.copyOf(buffer.getValue(),buffer.length());}
    }
    publicString(StringBuilder builder)
    {
        this.value=Arrays.copyOf(builder.getValue(),builder.length());
    }

    对于StringBuffer而言,处处要考虑其在多线程环境下的并发问题。需要注意是Arrays.copyOf()方法。这个方法的具体实现如下所示:

    1
    2
    3
    4
    5
    6
    7
    publicstaticchar[]copyOf(char[]original,intnewLength)
    {
            char[]copy=newchar[newLength];
            System.arraycopy(original,0,copy,0,
                             Math.min(original.length,newLength));
            returncopy;
        }

    此方法的泛型重载为:

    1
    2
    3
    4
    publicstatic<T>T[]copyOf(T[]original,intnewLength)
    {
        return(T[])copyOf(original,newLength,original.getClass());
    }

    而copyOf(original,
    newLength, original.getClass());的具体实现如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    publicstatic<T,U>T[]copyOf(U[]original,intnewLength,Class<?extendsT[]>newType)
    {
            T[]copy=((Object)newType==(Object)Object[].class)
                ?(T[])newObject[newLength]
                :(T[])Array.newInstance(newType.getComponentType(),newLength);
            System.arraycopy(original,0,copy,0,
                             Math.min(original.length,newLength));
            returncopy;
    }

    【注】对于上面的泛型方法,建议深刻的理解,其中T[]表示函数的返回值,<T,U>表示函数的参数类型。为什么这样写呢?这属于泛型的知识范围了,本文不再深究,如果对此处泛型内容感兴趣,请等待后续文章。
    (4)trim方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    publicStringtrim()
    {
    intlen=count;
    intst=0;
    intoff=offset;      /* avoid getfield opcode */
    char[]val=value;    /* avoid getfield opcode */
    while((st<len)&&(val[off+st]<=' '))
    {
        st++;
    }
    while((st<len)&&(val[off+len-1]<=' '))
    {
        len--;
    }
    return((st>0)||(len<count))?substring(st,len):this;
    }

    avoid getfield opcode是基于效率考虑的,String对象是在堆中生成的,所以将offset和value取出来放在off和val临时变量上,效果更好。类似,js中的对象链一样。
    (5)intern方法,可以看JDK的描述,讲解的非常透彻:
    A pool of strings, initially empty, is maintained privately by the class String.
    When the intern method is invoked, if the pool already contains a string equal to this String  object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.
    It follows that for any two strings s and t, s.intern()==t.intern() is true if and only if s.equals(t) is true.
    All literal strings and string-valued constant expressions are interned. 
    字符串常量池,初始值为空,它由类String类独自维护。
    当调用intern 方法时,如果池中已经包含一个等于此String 对象的字符串(是否相等由 equals(Object)方法确定),则返回池中的字符串引用。否则,将此 String 对象添加到池中,并且返回此String 对象的引用。例如:对于任何两个字符串s和t,当且仅当s.equals(t)为true时,s.intern()==t.intern()才为true。
    所有字面值字符串和字符串赋值常量表达式都是intern实现的。
    【注】关于字符串常量,网上的内容很多,各种观点都有,看起来有点可笑,其实,看看这段JDK的注释,所有的疑问都消失了。如果对此内容有疑问,想讨论讨论请加群再细说吧,联系方式见本文末尾。
    (6)startsWith方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    publicbooleanstartsWith(Stringprefix)
    {
    returnstartsWith(prefix,0);
    }
    publicbooleanendsWith(Stringsuffix)
    {
    returnstartsWith(suffix,count-suffix.count);
    }
     
    publicbooleanstartsWith(Stringprefix,inttoffset)
    {
    charta[]=value;
    intto=offset+toffset;
    charpa[]=prefix.value;
    intpo=prefix.offset;
    intpc=prefix.count;
    // Note: toffset might be near -1>>>1.
    if((toffset<0)||(toffset>count-pc))
    {
        returnfalse;
    }
    while(--pc>=0)
    {
        if(ta[to++]!=pa[po++])
    {
            returnfalse;
        }
    }
    returntrue;
    }

    此函数对自增自减使用的非常好,可以参考参考。在此不再赘述。

    第二部分:AbstractStringBuilder源码解析

    StringBuffer,StringBuilder的关系都是继承于AbstractStringBuilder,所以先从它入手分析。
    (1)成员变量:

    1
    2
    charvalue[];
    intcount;

    无offset偏移量,字符都是从value数组的0位置开始的。
    (2)capacity方法:
    返回的value.length,表示可以存储的空间。
    (3)扩容方式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    voidexpandCapacity(intminimumCapacity)
    {
    intnewCapacity=(value.length+1)*2;
            if(newCapacity<0)
            {
                newCapacity=Integer.MAX_VALUE;
            }elseif(minimumCapacity>newCapacity)
            {
        newCapacity=minimumCapacity;
    }
    charnewValue[]=newchar[newCapacity];
    System.arraycopy(value,0,newValue,0,count);
    value=newValue;
    }

    (4)补长或者截断

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    publicvoidsetLength(intnewLength)
    {
    if(newLength<0)
        thrownewStringIndexOutOfBoundsException(newLength);
    if(newLength>value.length)
        expandCapacity(newLength);
    if(count<newLength)
            {
        for(;count<newLength;count++)
    value[count]='';
    }else
             {
                count=newLength;
             }
    }

    (5)瘦身

    1
    2
    3
    4
    5
    6
    7
    8
    9
    publicvoidtrimToSize()
    {
            if(count<value.length)
            {
                char[]newValue=newchar[count];
                System.arraycopy(value,0,newValue,0,count);
                this.value=newValue;
             }
    }

    需要注意的方法是:
    public static native void arraycopy(Object src, int  srcPos, Object dest, int destPos, int length);很常用的方法啊。

    最后想说的一点是,由于时间的原因,JDK部分的源码分析只能到此结束,虽然有部分内容没有深入挖掘,例如泛型部分,常量池部分等,但是只能到此了。源码分析涉及的内容很多,而本文只找了些重点部分进行介绍。如果对本部分内容有异议或者想了解更多请加群:278721352,入群方式为:Java学习群[1]

    声明: 本文由金丝燕网原创编译,转载请保留链接: String,StringBuffer和StringBuilder源码解析[基于JDK6]

  • 相关阅读:
    特斯拉PET钥匙-蓝牙无钥匙
    超声波倒车雷达
    KiCad层次原理图标签和引脚的操作
    Nuget常用命令
    TCP/IP协议入门
    unbuntu(18.04)下体验dotnet core
    c#中的异步
    Git的不完全使用指南
    c#语言特性8.0
    c#语言特性7.x
  • 原文地址:https://www.cnblogs.com/xieji233/p/6155661.html
Copyright © 2020-2023  润新知