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

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

第一部分:String源码解析

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

1
2
3
4
int length ( ) ;
char charAt ( int index ) ;
CharSequence subSequence ( int start , int end ) ;
public String toString ( ) ;

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

1
2
3
private final char value [ ] ;
private final int offset ;
private final int count ;

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
public String ( StringBuffer buffer )
{
     synchronized ( buffer )
     {
       this . value = Arrays . copyOf ( buffer . getValue ( ) , buffer . length ( ) ) ; }
}
public String ( StringBuilder builder )
{
     this . value = Arrays . copyOf ( builder . getValue ( ) , builder . length ( ) ) ;
}

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

1
2
3
4
5
6
7
public static char [ ] copyOf ( char [ ] original , int newLength )
{
         char [ ] copy = new char [ newLength ] ;
         System . arraycopy ( original , 0 , copy , 0 ,
                         Math . min ( original . length , newLength ) ) ;
         return copy ;
     }

此方法的泛型重载为:

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

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

1
2
3
4
5
6
7
8
9
public static < T , U > T [ ] copyOf ( U [ ] original , int newLength , Class < ? extends T [ ] > newType )
{
         T [ ] copy = ( ( Object ) newType == ( Object ) Object [ ] . class )
             ? ( T [ ] ) new Object [ newLength ]
             : ( T [ ] ) Array . newInstance ( newType . getComponentType ( ) , newLength ) ;
         System . arraycopy ( original , 0 , copy , 0 ,
                         Math . min ( original . length , newLength ) ) ;
         return copy ;
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public String trim ( )
{
int len = count ;
int st = 0 ;
int off = 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
public boolean startsWith ( String prefix )
{
return startsWith ( prefix , 0 ) ;
}
public boolean endsWith ( String suffix )
{
return startsWith ( suffix , count - suffix . count ) ;
}
 
public boolean startsWith ( String prefix , int toffset )
{
char ta [ ] = value ;
int to = offset + toffset ;
char pa [ ] = prefix . value ;
int po = prefix . offset ;
int pc = prefix . count ;
// Note: toffset might be near -1>>>1.
if ( ( toffset < 0 ) || ( toffset > count - pc ) )
{
     return false ;
}
while ( -- pc >= 0 )
{
     if ( ta [ to ++ ] != pa [ po ++ ] )
{
         return false ;
     }
}
return true ;
}

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

第二部分:AbstractStringBuilder源码解析

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

1
2
char value [ ] ;
int count ;

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void expandCapacity ( int minimumCapacity )
{
int newCapacity = ( value . length + 1 ) * 2 ;
         if ( newCapacity < 0 )
         {
             newCapacity = Integer . MAX_VALUE ;
         } else if ( minimumCapacity > newCapacity )
         {
     newCapacity = minimumCapacity ;
}
char newValue [ ] = new char [ 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
public void setLength ( int newLength )
{
if ( newLength < 0 )
     throw new StringIndexOutOfBoundsException ( newLength ) ;
if ( newLength > value . length )
     expandCapacity ( newLength ) ;
if ( count < newLength )
         {
     for ( ; count < newLength ; count ++ )
value [ count ] = '\0' ;
} else
         {
             count = newLength ;
         }
}

(5)瘦身

1
2
3
4
5
6
7
8
9
public void trimToSize ( )
{
         if ( count < value . length )
         {
             char [ ] newValue = new char [ 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]

你可能感兴趣的:(Java基础,转载文章)