最近指导几位新人,学习了一下String,StringBuffer和StringBuilder类,从反馈的结果来看,总体感觉学习的深度不够,没有读出东西。其实,JDK的源码是越读越有味的。下面总结一下我读这些源码的收获吧。
注意:虽然源码的版本是JDK6,但是个人觉得学习这个版本的源码对于理解数据结构非常有帮助,因为String就是一个数据结构,它是char []的封装,实现了很多对char []的操作
(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
;
}
|
此函数对自增自减使用的非常好,可以参考参考。在此不再赘述。
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,入群方式为:
声明: 本文由金丝燕网原创编译,转载请保留链接: String,StringBuffer和StringBuilder源码解析[基于JDK6]