Java中使用String类来表示字符串,每个字符串都是String类的实例。由于String类是被final关键词修饰的,因此实际上字符串都是常亮,变切实线程安全的。java
String 类被声明为 final,说明它不能再被继承。同时它实现了三个接口,分别为 Serializable、Comparable 和 CharSequence。其中 Serializable 接口代表其能够序列化。数组
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence
复制代码
==value==是一个字符数组,被声明为final,是一个不可变数组,字符串实际上就是由这样一个字符数组的形式存储。
==hash==存储的是String实例的hash值,可是只有第一次调用hashCode()方法会计算hash值,而后会缓存下hash值,下次能够直接调用。缓存
/** 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内部只有一个内部类CaseInsensitiveComparator,实现了Comparator, java.io.Serializable接口,分别提供比较器功能和序列化功能。
安全
private static class CaseInsensitiveComparator
implements Comparator<String>, java.io.Serializable
复制代码
比较器内部主要的方法是compare()方法,提供比较字符串的功能。具体流程以下:bash
public int compare(String s1, String s2) {
int n1 = s1.length();
int n2 = s2.length();
int min = Math.min(n1, n2);
for (int i = 0; i < min; i++) {//1
char c1 = s1.charAt(i);
char c2 = s2.charAt(i);
if (c1 != c2) {
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
if (c1 != c2) {
c1 = Character.toLowerCase(c1);
c2 = Character.toLowerCase(c2);
if (c1 != c2) {
// No overflow because of numeric promotion
return c1 - c2;//2
}
}
}
}
return n1 - n2;//3
}
复制代码
String构造方法有不少,只挑选一部分分析。
默认的无参构造函数和以String为参数的构造方法都是经过直接给成员变量赋值完成。函数
public String() {
this.value = "".value;
}
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
复制代码
若是传入的参数是一个字节数组,在赋值给value的时候不是直接赋值,而是从新复制一个数组,将新数组赋值给value,避免外部经过那个数组引用修改String内部的value。ui
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
复制代码
传入构造参数的参数重包含了编码格式,可使用指定的编码格式来建立字符串。this
public String(byte bytes[], int offset, int length, String charsetName)
throws UnsupportedEncodingException {
if (charsetName == null)
throw new NullPointerException("charsetName");
checkBounds(bytes, offset, length);
this.value = StringCoding.decode(charsetName, bytes, offset, length);
}
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);
}
复制代码
传入参数为StringBuffer和StringBuilder,这个个类型的实例实际上表示的是可变字符串,内部存储也是经过字节数组来存储字符串信息,因此是直接讲传入的可变字符串内部的字符数组复制后,赋值给给value。编码
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());
}
复制代码
返回字符串的长度,实际上就是内部字符数组的长度。spa
public int length() {
return value.length;
}
复制代码
判断字符串是否为空,实际上就是判断内部字符数组长度是否为0。
public boolean isEmpty() {
return value.length == 0;
}
复制代码
寻找指定索引处的字符,会首先判断是否越界,若是没有越界就返回内部字符数组对应索引的字符。
public char charAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
复制代码
返回指定索引处的码点(字符对应的int值),会先判断是否越界,而后调用Character类的方法获得对应的码点。
public int codePointAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return Character.codePointAtImpl(value, index, value.length);
}
复制代码
将字符串中的一部分赋值给两一个字符数组,是经过将字符串内部的数组复制后再赋值给目的数组。
void getChars(char dst[], int dstBegin) {
System.arraycopy(value, 0, dst, dstBegin, value.length);
}
复制代码
得到字符串对应的字节数组,能够指定编码格式。
public byte[] getBytes(String charsetName)
throws UnsupportedEncodingException {
if (charsetName == null) throw new NullPointerException();
return StringCoding.encode(charsetName, value, 0, value.length);
}
public byte[] getBytes(Charset charset) {
if (charset == null) throw new NullPointerException();
return StringCoding.encode(charset, value, 0, value.length);
}
public byte[] getBytes() {
return StringCoding.encode(value, 0, value.length);
}
复制代码
经过字方法来判断两个字符串是否相等。具体流程以下:
public boolean equals(Object anObject) {
if (this == anObject) {//1
return true;
}
if (anObject instanceof String) {//2
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {//3
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {//4
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
复制代码
判断是否和其余字符序列内部的内容相同。其实主要是用来喝Stringbuffer和StringBuilder比较。具体流程以下:
public boolean contentEquals(CharSequence cs) {
// Argument is a StringBuffer, StringBuilder
if (cs instanceof AbstractStringBuilder) {//1
if (cs instanceof StringBuffer) {//2
synchronized(cs) {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
} else {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
}
// Argument is a String
if (cs instanceof String) {//3
return equals(cs);
}
// Argument is a generic CharSequence
char v1[] = value;
int n = v1.length;
if (n != cs.length()) {//4
return false;
}
for (int i = 0; i < n; i++) {
if (v1[i] != cs.charAt(i)) {
return false;
}
}
return true;
}
private boolean nonSyncContentEquals(AbstractStringBuilder sb) {
char v1[] = value;
char v2[] = sb.getValue();
int n = v1.length;
if (n != sb.length()) {
return false;
}
for (int i = 0; i < n; i++) {
if (v1[i] != v2[i]) {
return false;
}
}
return true;
}
复制代码
忽略大小写,基佬两个字符串是否相等。先判断引用是否相等,相等直接返回true,若是不相等就判断带比较的字符串是否为空,长度是否和本字符串相等,而且调用regionMatches()方法是否返回为true,若是都知足返回true,表示相等。regionMatches()方法是忽略大小写的状况下比较两个字符串的子串是否相等。
public boolean equalsIgnoreCase(String anotherString) {
return (this == anotherString) ? true
: (anotherString != null)
&& (anotherString.value.length == value.length)
&& regionMatches(true, 0, anotherString, 0, value.length);
}
复制代码
标胶两个字符串的的大小。具体流程以下:
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);//1
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {//2
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
复制代码
此方法时能够指定字符串前缀开始的偏移量,而后判断是偏移量开始的位置是否与前缀字符串相同。判断是否以某字符串为前缀和后缀都是基于此实现。具体流程以下:
public boolean startsWith(String prefix, int toffset) {
char ta[] = value;
int to = toffset;
char pa[] = prefix.value;
int po = 0;
int pc = prefix.value.length;
// Note: toffset might be near -1>>>1.
if ((toffset < 0) || (toffset > value.length - pc)) {//1
return false;
}
while (--pc >= 0) {//2
if (ta[to++] != pa[po++]) {
return false;
}
}
return true;
}
复制代码
计算hash值。具体流程以下:
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {//1
char val[] = value;
for (int i = 0; i < value.length; i++) {//2
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
复制代码