Java源码之String

继承三个接口的说明:

Comparable接口:

实现对象之间比较的接口,它的核心方法只有一个:

public int compareTo(T o);
CharSequence接口:

CharSequence是char值的可读序列。 该接口提供对许多不同种类的char序列的统一只读访问。CharSequence是一个接口,它只包括length(), charAt(int index), subSequence(int start, int end)这几个API接口。除了String实现了CharSequence之外,StringBuffer和StringBuilder也实现了CharSequence接口。

那么String为什么实现Charsequence这个接口呢。这里就要涉及一个java的重要特性,也就是多态。看下面的代码

public void charSetTest(CharSequence charSequence){
System.out.println(charSequence+“实现了多态”);
}
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
StringTest strTest = new StringTest();
strTest.charSetTest(new String(“我是String”));
strTest.charSetTest(new StringBuffer(“我是StringBuffer”));
strTest.charSetTest(new StringBuilder(“我是StringBuilder”));
}
执行结果:

我是String实现了多态
我是StringBuffer实现了多态
我是StringBuilder实现了多态
继承这个接口的原因就很明显:

因为String对象是不可变的,StringBuffer和StringBuilder这两个是可变的,所以我们在构造字符串的过程中往往要用到StringBuffer和StringBuilder。如果那些方法定义String作为参数类型,那么就没法对它们用那些方法,先得转化成String才能用。但StringBuffer和StringBuilder转换为String再转换过来很化时间的,用它们而不是直接用String的“加法”来构造新String本来就是为了省时间。

Serializable接口:

继承该接口,就是表明这个类是是可以别序列化的,这里就标记了string这个类是可以被序列化的,序列化的定义以及使用时机可以移步这里。

2.常用方法的使用和源码解析:
2.1构造方法
string总共提供了15种构造方法:

当然,这么多的构造方法,只需搞明白四个构造方法就可以了,因为其他的构造方法就是这四个构造方法的调用:

在看构造方法之前,先看看String的属性

private final char value[];
private int hash; // Default to 0
private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];
通过属性了解到,底层声明了一个final类型的char数组,这个char数组就是String的构造方法的核心,因为String的本质就是字符char(只针对javaSE8 以前的版本),下面来分析有代表的四个构造方法:

String()和String(String original)
这两个构造方法我们平时用的比较经常,源码如下:

public String() { this.value = “”.value; }

public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
分析:

通过源码可以看到,核心还是和String类声明的char[]数组value属性建立联系,进而来处理这个属性的值。

在String()这个空构造的方法中,就是属性char数组的值声明为一个空的字符串赋值给属性value.

而在String(String original),也是将方法参数的value的属性赋值给声明char[]数组的value属性,方便String的其他方法对char[]数组处理。

记住,在java的String操作中,大多数情况下还是对char[]数组的操作,这点很重要。

一般情况下定义一个新的字符串,有下面的两种方式:

String chen = new String(“chen”); // 这个我们一般不会使用
String chen = “chen”;
我们一般会选择第二种,这又是什么原因呢:

其实这两种声明的方式在JVM看来时等价的。

划重点:

但是String password=“chen”,利用了字符串缓冲池,也就是说如果缓冲池中已经存在了相同的字符串,就不会产生新的对象,而直接返回缓冲池中的字符串对象的引用。

如:
String a = “chen”;
String b = “chen”;
String c = new String(“chen”);
String d = new String(“chen”);

System.out.println(ab);//将输出"true";因为两个变量指向同一个对象。利用了字符串的缓冲池
System.out.println(c
d);//将输出"flase";因为两个变量不指向同一个对象。虽然值相同,只有用c.equals(d)才能返回true.
所以实际中,建议用第一种,可以减少系统资源消耗。

String(char[]vlaue,int offest,int count) 与字符相关的构造方法
源码如下:

public String(char value[], int offset, int count) {
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
if (count <= 0) {
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
if (offset <= value.length) {
this.value = “”.value;
return;
}
}
// 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);
}
分析:

这个构造方法的作用就是传入指定大小的char[] 数组,指定一个起始位置,然后构造出从起始位置开始计算的指定长度个数的字符串,具体用法:

public static void main(String[] args) {
char[] chars = new char[]{‘a’,‘b’,‘c’,‘d’};

   // 从chars数组的第二位开始,总数为3个字符的字符串 
    String rangeChar=new String(chars,1,3);
    System.out.println(rangeChar);
}

输出:
abc
这个构造方法的核心在于:

this.value = Arrays.copyOfRange(value, offset, offset+count);

//而这个方法在追一层,就到了Arrays这个工具类copyOfRange这个方法
public static char[] copyOfRange(char[] original, int from, int to) {
int newLength = to - from;
if (newLength < 0)
throw new IllegalArgumentException(from + " > " + to);
char[] copy = new char[newLength];
System.arraycopy(original, from, copy, 0,
Math.min(original.length - from, newLength));
return copy;
}
其实看到这里,他的实现原理就基本清楚了,分析copyOfRange()这个方法的执行步骤:

首先是获取原字符数组的orginal[]要构造字符串的长度,也就是 这一行代码:

int newLength = to - from;
然后异常判断,并声明新的数组,来存储原数组指定长度的值

if (newLength < 0)
throw new IllegalArgumentException(from + " > " + to);
char[] copy = new char[newLength];
将原字符数组指定长度的值拷贝到新数组,返回这个数组:

System.arraycopy(original, from, copy, 0,
Math.min(original.length - from, newLength));
return copy;
最后再将数组的值赋值给String的属性value,完成初始化:

this.value = Arrays.copyOfRange(value, offset, offset+count);
归根结底还是和String声明的属性value建立联系,完成相关的操作。

String(byte[] bytes,int offest,int length,Charset charset) 字节相关的 构造方法
这个构造方法的作用就是将指定长度的字节数组,构造成字符串,且还可以指定编码值:

涉及的源码如下:

public String(byte bytes[], int offset, int length, Charset charset) {
if (charset == null)
throw new NullPointerException(“charset”);
checkBounds(bytes, offset, length);
this.value = StringCoding.decode(charset, bytes, offset, length);
}

// StringCoding中的方法:
static char[] decode(Charset cs, byte[] ba, int off, int len) {
// 1, 构造解码器
CharsetDecoder cd = cs.newDecoder();
int en = scale(len, cd.maxCharsPerByte());
char[] ca = new char[en];
if (len == 0)
return ca;
boolean isTrusted = false;
if (System.getSecurityManager() != null) {
if (!(isTrusted = (cs.getClass().getClassLoader0() == null))) {
ba = Arrays.copyOfRange(ba, off, off + len);
off = 0;
}
}
cd.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE)
.reset();
if (cd instanceof ArrayDecoder) {
int clen = ((ArrayDecoder)cd).decode(ba, off, len, ca);
return safeTrim(ca, clen, cs, isTrusted);
} else {
ByteBuffer bb = ByteBuffer.wrap(ba, off, len);
CharBuffer cb = CharBuffer.wrap(ca);
try {
CoderResult cr = cd.decode(bb, cb, true);
if (!cr.isUnderflow())
cr.throwException();
cr = cd.flush(cb);
if (!cr.isUnderflow())
cr.throwException();
} catch (CharacterCodingException x) {
// Substitution is always enabled,
// so this shouldn’t happen
throw new Error(x);
}
return safeTrim(ca, cb.position(), cs, isTrusted);
}
}

private static char[] safeTrim(char[] ca, int len,
Charset cs, boolean isTrusted) {
if (len == ca.length && (isTrusted || System.getSecurityManager() == null))
return ca;
else
return Arrays.copyOf(ca, len);
}
这个方法构造的方法的复杂之处就是在于对于指定编码的处理,但是我们如果看完这个方法调用的整个流程最终还是落到

return Arrays.copyOf(ca, len);
返回一个指定编码的字符数组,然后和String类的value属性建立联系
字节数组构造方法的基本逻辑:就是将字节数组转化为字符数组,再和String的value属性建立联系,完成初始化。

String(StringBuilder builder) 和String(StringBuffer buffer)与String扩展类相关的构造方法:
这个构造方法就是将StringBuilder或者StringBuffer类初始化String类,源码如下:

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());
}
分析:

核心的代码还是这一句:

this.value = Arrays.copyOf(builder.getValue(), builder.length());
​ 在往下看builder.getValue(),的源码

final char[] getValue() {
return value;
}
返回一个字符数组
深圳网站建设www.sz886.com

你可能感兴趣的:(Java源码之String)