标签:
本文适合有一定java基础的同学。本博客宗旨:突出重点,分析难点。
先看一下String源码
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
......
}
String类名前面是final修复,变量char value[]也是final修饰。
可以得到三点:
Java中的常量池,实际上分为两种形态 : 静态常量池 和 运行时常量池。
public class StringTest {
public static void main(String[] args) {
String s = new String("abc");
String s1 = "abc";
String s2 = "abc";
String s3 = s.intern();
System.out.println(s == s1);//false
System.out.println(s == s2);//false
System.out.println(s == s3);//false
System.out.println(s1 == s3);//true
}
}
分析:
输出结果如注释所示,前两个结果前面已经说的很清楚了,现在拿最后一个说明,首先看看s3 = s.intern()这句,当调用s.intern()这句的时候,先去字符串常量池中找,是否有abc这个串,如果没有,则新增,同时返回引用,如果有,则返回已经存在的引用,此处s1和s2都指向常量池中的abc对象,所以此处是存在的,调用s.intern()后,s3和s1、s2指向同一个对象,所以s1==s3返回的是true。
intern()做到了一个很不寻常的行为:在运行期动态的在方法区创建对象,一般只有像new关键字可以在运行期在堆上面创建对象,所以此处比较特殊。属于及时编译的概念。
如对常量池感兴趣可以参看:《Java虚拟机原理图解》 2.2、常量池详解(上)
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value; //---------1---------
char v2[] = anotherString.value;//-------2----------
int i = offset;
int j = anotherString.offset;
while (n-- != 0) { // --------3------------
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
通过1和2获得两个字符串的字符数组,循环3进行对比。
后期文章会单独详解 重写equals和hashcode的
获取指定位置的字符.
public char charAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
用于截取字符串
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);
}
来看一下 new String(value, beginIndex, subLen) 源码
public String(char value[], int offset, int count) {
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
// Note: offset or count might be near -1>>>1.
if (offset > value.length - count) {
throw new StringIndexOutOfBoundsException(offset + count);
}
this.value = Arrays.copyOfRange(value, offset, offset+count);
}
有一个Arrays.copyOfRange(value, offset, offset+count); ,这个方法的大致意思是通过原字符数组拷贝一份新的字符串。
如果还需要了解那个方法的底层代码可以给我留言。
StringBuffer和StringBuilder都继承了抽象类AbstractStringBuilder,这个抽象类和String一样也定义了char[] value和int count,但是与String类不同的是,它们没有final修饰符。因此得出结论:String、StringBuffer和StringBuilder在本质上都是字符数组,不同的是,在进行连接操作时,String每次返回一个新的String实例,而StringBuffer和StringBuilder的append方法直接返回this,所以这就是为什么在进行大量字符串连接运算时,不推荐使用String,而推荐StringBuffer和StringBuilder。
public synchronized StringBuffer append(String str) {
super.append(i);
return this;
}
public StringBuilder append(String str) {
super.append(str);
return this;
}
由于StringBuffer方法上有synchronized所有是现成安全的。
继续看 super.append(i);源码
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
已StringBuilder为例,它集成AbstractStringBuilder抽象类。ensureCapacityInternal(count + len);是检查添加前字符数组长度是否满足append后数组的长度。如满足不做任何处理,如不满足创建一个新的字符数组,长度是现有数组的2倍,并将现有数组拷贝到新创建的数组中。来看一下str.getChars(0, len, value, count);源码
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);
}
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
看到 System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); 是将新字符串拷贝到原有字符串中。
arraycopy 方法说明:
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
有时间补上。
标签:
原文地址:http://blog.csdn.net/happy_85/article/details/51261671