二、String类
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {}
String是一个用final声明的常量类,不能被任何类继承且对象中的字符序列是不可变的。
String实现的接口
1.Serializable接口:序列化标识接口
2.Comparable接口:用于比较两个字符串的大小(按顺序比较单个字符的ASCII码)
3.CharSequence接口:表示一个有序字符的集合
字段属性
//用来存储属性
private final char value[];
//缓存字符串的哈希码
private int hash; // Default to 0
//实现序列化的标识
private static final long serialVersionUID = -6849794470754667710L;
String的构造方法
String的方法
//String类重写了equals方法,比较的是组成字符串的每个字符是否相同,如果相同则返回true,否则返回false
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
/*
String的hashCode算法公式:
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
为什么要选择31作为乘积因子
①、31是一个不大不小的质数,是作为 hashCode 乘子的优选质数之一。
②、31可以被 JVM 优化,31 * i = (i << 5) - i。因为移位运算比乘法运行更快更省性能。*/
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
//通过传入的索引(数组下标),返回指定索引的单个字符
public char charAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
//按字母顺序比较两个字符串,是基于字符串中每个字符的 Unicode 值。当两个字符串某个位置的字符不同时,返回的是这一位置的字符 Unicode 值之差,当两个字符串都相同时,返回两个字符串长度之差。
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;
}
//在compareTo的基础上忽略大小写
public int compareToIgnoreCase(String str) {
return CASE_INSENSITIVE_ORDER.compare(this, str);
}
//将指定的字符串连接到此字符串的末尾
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
//参数ch是字符的Unicode值,返回指定字符在字符串中第一次出现的索引
public int indexOf(int ch) {
return indexOf(ch, 0);//从第一个字符开始搜索
}
public int indexOf(int ch, int fromIndex) {
final int max = value.length;//字符的长度
if (fromIndex < 0) { //指定索引的位置小于0,默认从0开始搜索
fromIndex = 0;
} else if (fromIndex >= max) {
return -1;//索引值大于等于字符长度,直接返回-1
}
if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
final char[] value = this.value;
for (int i = fromIndex; i < max; i++) {
if (value[i] == ch) {//判断字符串每个字符是否和指定字符都在此范围内
return i;
}
}
return -1;
} else {
//当字符大于 65536时,处理的少数情况,该方法会首先判断是否是有效字符,然后依次进行比较
return indexOfSupplementary(ch, fromIndex);
}
}
//内部调用 split(regex, 0) 方法
public String[] split(String regex) {
return split(regex, 0);
}
/* ①.limit>0,则pattern(模式)应用n - 1 次
②.limit=0,则pattern(模式)应用无限次并且省略末尾的空字串
③.limut<0,则pattern(模式)应用无限次
*/
public String[] split(String regex, int limit) {
char ch = 0;
if (((regex.value.length == 1 &&
".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
(regex.length() == 2 &&
regex.charAt(0) == '\\' &&
(((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
((ch-'a')|('z'-ch)) < 0 &&
((ch-'A')|('Z'-ch)) < 0)) &&
(ch < Character.MIN_HIGH_SURROGATE ||
ch > Character.MAX_LOW_SURROGATE))
{
int off = 0;
int next = 0;
boolean limited = limit > 0;//大于0,limited==true,反之limited==false
ArrayList<String> list = new ArrayList<>();
while ((next = indexOf(ch, off)) != -1) {
//当参数limit<=0 或者 集合list的长度小于 limit-1
if (!limited || list.size() < limit - 1) {
list.add(substring(off, next));
off = next + 1;
} else { //判断最后一个list.size() == limit - 1
list.add(substring(off, value.length));
off = value.length;
break;
}
}
//如果没有一个能匹配的,返回一个新的字符串,内容和原来的一样
if (off == 0)
return new String[]{this};
// 当 limit<=0时,limited==false,或者集合的长度小于limit时,截取添加剩下的字符串
if (!limited || list.size() < limit)
list.add(substring(off, value.length));
//当limit==0时,如果末尾添加的元素为空(长度为0),则集合长度不断减1,直到末尾不为空
int resultSize = list.size();
if (limit == 0) {
while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
resultSize--;
}
}
String[] result = new String[resultSize];
return list.subList(0, resultSize).toArray(result);
}
return Pattern.compile(regex).split(this, limit);
}
//将原字符串中所有的oldChar字符都替换为newChar字符,返回一个
public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value;
while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
if (i < len) {
char buf[] = new char[len];
for (int j = 0; j < i; j++) {
buf[j] = val[j];
}
while (i < len) {
char c = val[i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
return new String(buf, true);
}
}
return this;
}
//将匹配正则表达式regex的匹配项都替换成replacement字符串,返回一个新的字符串。
public String replaceAll(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}
//返回一个索引beginIndex开始到结束的子字符串
public String substring(int beginIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
int subLen = value.length - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
//返回一个从索引 beginIndex 开始,到 endIndex 结尾的子字符串。
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);
}
//调用一个String对象的intern()方法,如果常量池中有该对象了,直接返回该字符串的引用(存在堆中就返回堆中,存在池中就返回池中),如果没有,则将该对象添加到池中,并返回池中的引用。
public native String intern();
常量池
Java运行时会维护一个String Pool(String池), 也叫“字符串缓冲区”。String池用来存放运行时中产生的各种字符串,并且池中的字符串的内容不重复。
String真的不可变吗?
String str = "vae";
//打印原字符串
System.out.println(str);//vae
//获取String类中的value字段
Field fieldStr = String.class.getDeclaredField("value");
//因为value是private声明的,这里修改其访问权限
fieldStr.setAccessible(true);
//获取str对象上的value属性的值
char[] value = (char[]) fieldStr.get(str);
//将第一个字符修改为 V(小写改大写)
value[0] = 'V';
//打印修改之后的字符串
System.out.println(str);//Vae