阅读java源码的一些体会
一、CharSequence接口
public interface CharSequence {
int length();
char charAt(int index);
CharSequence subSequence(int start, int end);
public String toString();
public default IntStream chars() {……}
public default IntStream codePoints() {……}
}
default是java8的新特性,具体可以想见文档,显然此接口主要是保证有获取内容的公共接口。还实现了转换为IntStream的默认函数。
二、String类的一些感觉
1.结构
- 请看类的实现:
public final class String
implements java.io.Serializable, Comparable, CharSequence{
private fianl char value[];
private int hash;
private static final long serialVersionUID = -6849794470754667710L;
private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];
/**省略实现细节
**/
}
可以看到此时的结构体一共有四个变量。扣除为了实现进程间传递的变量。主要的变量就是存储内容value和hash。其中value为其取值,而hash则是String的hash函数值。
这里可以看到String类型中的value变量是final类型的,是不可变的变量,同时String类型也没有提供修改char[]数组的接口。这就可以肯定value在构造之初之后就不能改变。这表示新的内容产生新的对象。
2.常用代码分析
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
//将内容从value中复制到dst数组中,arraycopy是native方法,这个方法显
//然可以有相当强大的效率
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
public String substring(int beginIndex, int endIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
if (endIndex > value.length) {
throw new StringIndexOutOfBoundsException(endIndex);
}
int subLen = endIndex - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
//产生了新的对象哦
return ((beginIndex == 0) && (endIndex == value.length)) ? this
: new String(value, beginIndex, subLen);
}
public String trim() {
int len = value.length;
int st = 0;
char[] val = value; /* avoid getfield opcode */
while ((st < len) && (val[st] <= ' ')) {
st++;
}
while ((st < len) && (val[len - 1] <= ' ')) {
len--;
}
//还是产生了新对象哦
return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}
此外,replace、splite都和Pattern和matcher有关,将数组内容替换,并返回新的对象。
二、StringBuilder
1.结构
- 请看类的实现:
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence{
/** use serialVersionUID for interoperability */
static final long serialVersionUID = 4383685877147921099L;
…………
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
}
这里可以看到并没有其他的变量,那么我们看其父类:
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char[] value;
/**
* The count is the number of characters used.
*/
int count;
/**
* Creates an AbstractStringBuilder of the specified capacity.
*/
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
public AbstractStringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
…………
}
其中这里有一段关于vlaue数组长度扩展的关键代码:
/**
* This implements the expansion semantics of ensureCapacity with no
* size check or synchronization.
*/
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
value = Arrays.copyOf(value, newCapacity);
}
综上所述,我们可以看到,StringBuilder直接调用AbstractStringBuilder的父接口,其append每次首先检查char[]数组长度,如果大小不够,就重新扩充大小,并拷贝原来的数组内容,可以说StringBuilder是缓存的String,因此效率更高。想reaplace、delete这些方法都是在缓存数组上操作,然后返回对象本身。但如果需要获得String类型,则还是会返回String类型的对象。
三、StringBuffer
1.结构
- 请看类的实现(通过我的read,方法都有synchronized,是线程安全得):
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
/**
* A cache of the last value returned by toString. Cleared
* whenever the StringBuffer is modified.
* 关键字代表此变量之存在于内存中,而不能传输(例如持久化)
*/
private transient char[] toStringCache;
/** use serialVersionUID from JDK 1.0.2 for interoperability */
static final long serialVersionUID = 3388685877147921107L;
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
…………
@Override
public synchronized void ensureCapacity(int minimumCapacity) {
if (minimumCapacity > value.length) {
expandCapacity(minimumCapacity);
}
}
}
2.关键代码
/**
* @throws IndexOutOfBoundsException {@inheritDoc}
* @see #length()
*/
@Override
public synchronized void setCharAt(int index, char ch) {
if ((index < 0) || (index >= count))
throw new StringIndexOutOfBoundsException(index);
toStringCache = null;
value[index] = ch;
}
@Override
public synchronized void setCharAt(int index, char ch) {
if ((index < 0) || (index >= count))
throw new StringIndexOutOfBoundsException(index);
toStringCache = null;
value[index] = ch;
}
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
@Override
public synchronized StringBuffer insert(int index, char[] str, int offset,
int len)
{
toStringCache = null;
super.insert(index, str, offset, len);
return this;
}
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
可以说StringBuffer是线程安全得,但是其他的核心代码几乎是StringBuilder的拷贝。显然StringBuilder效率上要更好,然而多线程下显然是有危险的。
四、总结
StringBuffer和StringBuilder异同:
- StringBuffer是线程安全得可变序列,而StringBuilder是5.0新增的一个特性,用于和StringBuffer做兼容,其不保证线程安全,可以看到StringBuilder显然要比StringBuffer快。
- 为了保证快速的toString,StringBuffer提供了字符缓存数组,显然这时候非常快。
- String类每次改变都将产生新对象。因而StringBuffer一般情况下要比String快。
- System.arraycopy是native方法,显然拷贝效率要更好,如果需要拷贝,可以 优先考虑使用arraycopy拷贝普通数组。
- default关键字主要用于扩展接口的同时避免修改集成树,可以提供接口的默认实现,transient保证变量在持久化的过程中智能待在内存。