最近指导几位新人,学习了一下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];
,
                        
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);
,
                        
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;
;
intoff=offset;      /*
avoid getfield opcode */
char[]val=value;    /*
avoid getfield opcode */
while((st<len)&&(val[off+st]<='
'))
{
    st++;
}
]<='
'))
{
    len--;
}
)||(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)
{
);
}
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.
)||(toffset>count-pc))
{
    returnfalse;
}
)
{
    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)
{
;
)
        {
            newCapacity=Integer.MAX_VALUE;
        }elseif(minimumCapacity>newCapacity)
        {
    newCapacity=minimumCapacity;
}
charnewValue[]=newchar[newCapacity];
,count);
value=newValue;
}

(4)补长或者截断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
publicvoidsetLength(intnewLength)
{
)
    thrownewStringIndexOutOfBoundsException(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
publicvoidtrimToSize()
{
        if(count<value.length)
        {
            char[]newValue=newchar[count];
,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]

String,StringBuffer和StringBuilder源码解析[基于JDK6]的更多相关文章

  1. String、StringBuffer和StringBuilder源码解析

    1.String 1.1类的定义 public final class String implements java.io.Serializable, Comparable<String> ...

  2. [Java源码解析] -- String类的compareTo(String otherString)方法的源码解析

    String类下的compareTo(String otherString)方法的源码解析 一. 前言 近日研究了一下String类的一些方法, 通过查看源码, 对一些常用的方法也有了更透彻的认识,  ...

  3. String、StringBuffer、StringBuilder源码分析

    利用反编译具体看看"+"的过程 1 public class Test 2 { 3 public static void main(String[] args) 4 { 5 int ...

  4. Stringbuffer与Stringbuilder源码学习和对比

    >>String/StringBuffer/StringBuilder的异同 (1)相同点观察源码会发现,三个类都是被final修饰的,是不可被继承的.(2)不同点String的对象是不可 ...

  5. String、StringBuffer、StringBuilder源码解读

    序 好长时间没有认真写博客了,过去的一年挺忙的.负责过数据库.线上运维环境.写代码.Code review等等东西挺多. 学习了不少多方面的东西,不过还是需要回归实际.加强内功,方能扛鼎. 去年学习M ...

  6. StringBuffer 和Stringbuilder源码分析

    首先看一下他们的继承关系   这个两个对象都继承了AbstractStringBuilder抽象类.   1.他们的实现方式都一样的,唯一区别的StringBuffer在多线程的时候是保证了数据安全, ...

  7. 【Java并发集合】ConcurrentHashMap源码解析基于JDK1.8

    concurrentHashMap(基于jdk1.8) 类注释 所有的操作都是线程安全的,我们在使用时无需进行加锁. 多个线程同时进行put.remove等操作时并不会阻塞,可以同时进行,而HashT ...

  8. String的equals()方法源码解析

    每个String对象在创建的时候都构造了一个char类型的final声明的不可替换数组,数组中的每一个元素即字符串对应的每一个字符如图: String的equals()在比较的时候会进行两步比较: 1 ...

  9. Spring源码解析-基于注解依赖注入

    在spring2.5版本提供了注解的依赖注入功能,可以减少对xml配置. 主要使用的是 AnnotationConfigApplicationContext: 一个注解配置上下文 AutowiredA ...

随机推荐

  1. centos下cmake安装

    步骤一.安装gcc等必备程序包(已安装则略过此步,用gcc -v检测) yum install -y gcc gcc-c++ make automake 步骤二.安装wget (已安装则略过此步) y ...

  2. mongo .update

    db.classes.update({"count":{$gt:20}},{$set:{"name":"c4"}},false,false) ...

  3. 【codevs1036】商务旅行 LCA 倍增

    1036 商务旅行  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond 题目描述 Description 某首都城市的商人要经常到各城镇去做生意,他们按自己的 ...

  4. Spring学习笔记之整合hibernate

    1.web.xml里边要配置好对应的springxml的路径 <context-param> <param-name>contextConfigLocation</par ...

  5. 元素ID命名规范

    因为本框架默认所有内容都位于一个Document中,所以为元素命名为ID的时候需要做到唯一性,如果确实不可避免的会出现有重读ID的现象,需要操作当前页片(页面片段,就是子页面)的时候,尽量用: $.C ...

  6. 谁说固态硬盘没容量?4TB诞生、明年8TB!

    固态硬盘已经逐渐取代机械硬盘成为很多用户的首选,但唯一欠缺的就是容量,或者说单位容量的价格,但是如今,机械硬盘的容量提升举步维艰,固态硬盘却在突飞猛进. 近日,SanDisk就宣布了全球第一款容量高达 ...

  7. 用命令 安装/卸载 windows服务(转)

    第一种方法: 1. 开始 ->运行 ->cmd 2. cd到C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727(Framework版本号按IIS配置 ...

  8. CentOS中配置LNMP环境打开提示File not found

    在centos系统中配置好php环境了,但是发现能运行html页面并不能运行php文件了,这样我就在gg的帮助下一步不解决了,下面来看问题的具体解决过程.     安装之后测试发现,怎么Html能运行 ...

  9. win7下利用笔记本无线网卡创建AP 组建无线局域网(可以连魔兽,TCP、UDP也没问题)

    转自:http://blog.163.com/fghok_018/blog/static/122599670201072773924530/ 近一个月,宿舍的好多同学都买了笔记本电脑,当然,我也买了, ...

  10. C++数据结构之Stack(栈)

    stack,栈,是好比堆积木似的数据结构,从上之下堆积,取出时按"LIFO"-last int first out后进先出的规则.栈一般为线程所独有,也就是每个线程有其自有的栈,与 ...