Java源码阅读计划(1) String类
1.1不变性
HashMap
的key
建议使用不可变类,比容String
这种,这里的不可变类指的色类中的一个值一旦初始化,将无法改变,如果修改,那就将会还是新的一个类。接下来我简单的演示一下:
String s = "hello";
s = "world";
非常简单的例子,我们看看第一行s
的地址
再看看第二行s
的地址
我们打开rt.jar
,再打开java
底下的lang
包,找到String
类,
在这里我们看到了两个final
,
1.首先是类上的final
,String
无法被继承,也就是说所有String
中的方法无法被继承覆写
2.接着是保存字符串的char
数组,同样的被final
修饰,也就是说value
数组一旦被初始化,它的内存地址是无法被修改的,value
的权限是private
,外部访问不到,String
也没开放修改值的方法,所以我们可以认为value
一旦被赋值,内存地址是无法被修改的。
这就是String
不变性的原因,当我们想设计一个不变性类的时候,可以模仿这种样式。
所以大多数String
的方法,都必须有返回值的。
1.2相等判断
在String
类中,有两个判断相等的方法equals()
和equalsIgnoreCase()
,接下来我详细的讲讲这两个方法。
equals()
相信大家并不陌生,Java学习之初,就有一个equals()
和==
的区别这一个知识点,这区别今后再立一个专题来谈谈。
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;
}
equals()
的方法设计的很巧妙,依照String
的底层结构进行设计,先进行对象判断,接下先用长度,然后再一个一个字符进行比较,对方法性能有优化。
接着再看看equalsIgnoreCase()
,这个方法是忽略大小写的相等比较
public boolean equalsIgnoreCase(String anotherString) {
return (this == anotherString) ? true
: (anotherString != null)
&& (anotherString.value.length == value.length)
&& regionMatches(true, 0, anotherString, 0, value.length);
}
又是一个巧妙设计,利用&&
的短路判断有性能的优化,接下来看看regionMatches()
方法,
public boolean regionMatches(boolean ignoreCase, int toffset,
String other, int ooffset, int len) {
char ta[] = value;
int to = toffset;
char pa[] = other.value;
int po = ooffset;
if ((ooffset < 0) || (toffset < 0)
|| (toffset > (long)value.length - len)
|| (ooffset > (long)other.value.length - len)) {
return false;
}
while (len-- > 0) {
char c1 = ta[to++];
char c2 = pa[po++];
if (c1 == c2) {
continue;
}
if (ignoreCase) {
char u1 = Character.toUpperCase(c1);
char u2 = Character.toUpperCase(c2);
if (u1 == u2) {
continue;
}
if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
continue;
}
}
return false;
}
return true;
}
逻辑很易懂,这里虽然统一使用了大写作为比较标准,可之后又使用小写再进行判断,这是Java为了针对Georgian(格鲁吉亚)字母表奇怪的大小写转换规则而专门又增加了一步判断,就是转换成小写再比较一次。
1.3替换
替换也是平常常用的一个功能,有replace()
替换所有选定字符,有relaceAll()
替换字符串所有匹配给定的正则表达式的子字符串,还有replaceFirst()
等,在特定位置替换的方法。
先讲讲replace()
,Java
中的replace()
有两种重载形式,一种是单字符,另外一种是字符串,
public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value; /* avoid getfield opcode */
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;
}
这段比较容易懂,
public String replace(CharSequence target, CharSequence replacement) {
return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
}
另外一个方法,CharSequence
是一个接口,与String
不同,它是可读可写序列,至于下面的,QAQ等我看完Pattern
类之后在进行详细分析。
接下来说说replaceAll()
,该方法替换字符串所有匹配给定的正则表达式的子字符串,
public String replaceAll(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}
TODO:这个详解等Pattern
类看完一起处理。
至于repalceFirst()
也同样等Pattern
解决完一起来
public String replaceFirst(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceFirst(replacement);
}