首页 > 代码库 > String stringbuffer StringBuilder
String stringbuffer StringBuilder
最近指导几位新人,学习了一下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 = http://www.mamicode.com/Arrays.copyOf(buffer.getValue(), buffer.length()); } } public String(StringBuilder builder) { this.value = http://www.mamicode.com/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[]表示函数的返回值,<T,U>表示函数的参数类型。为什么这样写呢?这属于泛型的知识范围了,本文不再深究,如果对此处泛型内容感兴趣,请等待后续文章。
(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 = http://www.mamicode.com/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 = http://www.mamicode.com/new char[count]; System.arraycopy(value, 0, newValue, 0, count); this.value = http://www.mamicode.com/newValue; } } |
需要注意的方法是:
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);很常用的方法啊。
String stringbuffer StringBuilder