java源码阅读–String类
字符串操作类是相当重要的一个类,到现在都没有整理过里面的方法和实现。话不多说,用我抠脚的技术整理一波。
String类实现的接口
Serializable接口
实现java.io的类支持类的序列化。Serializable接口。不实现此接口的类将不会对其任何状态进行序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。
CharSequence接口
CharSequence类是java.lang包下的一个接口,此接口对多种不同的对char访问的统一接口,像String、StringBuffer、StringBuilder类都是CharSequence的子接口;
- 抽象方法
(1)返回此字符序列的长度。长度是序列中16位字符的个数 (16位字符的个数有点不是很懂)
/**
* Returns the length of this character sequence. The length is the number
* of 16-bit <code>char</code>s in the sequence.
*
* @return the number of <code>char</code>s in this sequence
*/
int length();
(2)返回指定索引处的char值。索引的范围从0到length() - 1。序列的第一个char值在索引0处,下一个在索引1处,以此类推,对于数组索引来说。
如果索引指定的char值是代理,则返回代理值
/**
* Returns the <code>char</code> value at the specified index. An index ranges from zero
* to <tt>length() - 1</tt>. The first <code>char</code> value of the sequence is at
* index zero, the next at index one, and so on, as for array
* indexing.
*
* <p>If the <code>char</code> value specified by the index is a
* <a href="{@docRoot}/java/lang/Character.html#unicode">surrogate</a>, the surrogate
* value is returned.
*
* @param index the index of the <code>char</code> value to be returned
*
* @return the specified <code>char</code> value
*
* @throws IndexOutOfBoundsException
* if the <tt>index</tt> argument is negative or not less than
* <tt>length()</tt>
*/
char charAt(int index);
(3)返回一个字符序列,该字符序列是该序列的子序列。子序列以指定索引处的char值开始,以索引结束- 1处的char值结束。返回序列的长度(以字符为单位)是end - start,因此如果start == end,则返回一个空序列。
/**
* Returns a <code>CharSequence</code> that is a subsequence of this sequence.
* The subsequence starts with the <code>char</code> value at the specified index and
* ends with the <code>char</code> value at index <tt>end - 1</tt>. The length
* (in <code>char</code>s) of the
* returned sequence is <tt>end - start</tt>, so if <tt>start == end</tt>
* then an empty sequence is returned.
*
* @param start the start index, inclusive
* @param end the end index, exclusive
*
* @return the specified subsequence
*
* @throws IndexOutOfBoundsException
* if <tt>start</tt> or <tt>end</tt> are negative,
* if <tt>end</tt> is greater than <tt>length()</tt>,
* or if <tt>start</tt> is greater than <tt>end</tt>
*/
CharSequence subSequence(int start, int end);
- default方法
/**
* Returns a stream of {@code int} zero-extending the {@code char} values
* from this sequence. Any char which maps to a <a
* href="{@docRoot}/java/lang/Character.html#unicode">surrogate code
* point</a> is passed through uninterpreted.
*
* <p>If the sequence is mutated while the stream is being read, the
* result is undefined.
*
* @return an IntStream of char values from this sequence
* @since 1.8
*/
public default IntStream chars() {
class CharIterator implements PrimitiveIterator.OfInt {
int cur = 0;
public boolean hasNext() {
return cur < length();
}
public int nextInt() {
if (hasNext()) {
return charAt(cur++);
} else {
throw new NoSuchElementException();
}
}
@Override
public void forEachRemaining(IntConsumer block) {
for (; cur < length(); cur++) {
block.accept(charAt(cur));
}
}
}
return StreamSupport.intStream(() ->
Spliterators.spliterator(
new CharIterator(),
length(),
Spliterator.ORDERED),
Spliterator.SUBSIZED | Spliterator.SIZED | Spliterator.ORDERED,
false);
}
/**
* Returns a stream of code point values from this sequence. Any surrogate
* pairs encountered in the sequence are combined as if by {@linkplain
* Character#toCodePoint Character.toCodePoint} and the result is passed
* to the stream. Any other code units, including ordinary BMP characters,
* unpaired surrogates, and undefined code units, are zero-extended to
* {@code int} values which are then passed to the stream.
*
* <p>If the sequence is mutated while the stream is being read, the result
* is undefined.
*
* @return an IntStream of Unicode code points from this sequence
* @since 1.8
*/
public default IntStream codePoints() {
class CodePointIterator implements PrimitiveIterator.OfInt {
int cur = 0;
@Override
public void forEachRemaining(IntConsumer block) {
final int length = length();
int i = cur;
try {
while (i < length) {
char c1 = charAt(i++);
if (!Character.isHighSurrogate(c1) || i >= length) {
block.accept(c1);
} else {
char c2 = charAt(i);
if (Character.isLowSurrogate(c2)) {
i++;
block.accept(Character.toCodePoint(c1, c2));
} else {
block.accept(c1);
}
}
}
} finally {
cur = i;
}
}
public boolean hasNext() {
return cur < length();
}
public int nextInt() {
final int length = length();
if (cur >= length) {
throw new NoSuchElementException();
}
char c1 = charAt(cur++);
if (Character.isHighSurrogate(c1) && cur < length) {
char c2 = charAt(cur);
if (Character.isLowSurrogate(c2)) {
cur++;
return Character.toCodePoint(c1, c2);
}
}
return c1;
}
}
return StreamSupport.intStream(() ->
Spliterators.spliteratorUnknownSize(
new CodePointIterator(),
Spliterator.ORDERED),
Spliterator.ORDERED,
false);
}
chars():官方解释:返回一个int 0流——从这个序列扩展char值。任何映射到代理代码点的字符都将通过未解释的方式传递
codePoints():从这个序列返回一个代码点值流。序列中遇到的任何代理对都按字符组合。然后将结果传递给流。任何其他代码单元,包括普通BMP字符、未配对的代理程序和未定义的代码单元,都是从零开始扩展为int值,然后传递给流。
总之:通过调用这个方法返回IntStream流,源码扒了一下,感觉不到有什么用,以后阅读多了再回来补充一下。
Comparable接口
里面就只有一个方法
public int compareTo(T o);
java8Api文档解释:
int compareTo(T o)
将此对象与指定的对象进行比较以进行排序。 返回一个负整数,零或正整数,因为该对象小于,等于或大于指定对象。
实现程序必须确保sgn(x.compareTo(y)) == -sgn(y.compareTo(x))所有x和y。 (这意味着x.compareTo(y)必须抛出异常iff y.compareTo(x)引发异常。)
实施者还必须确保关系是可传递的: (x.compareTo(y)>0 && y.compareTo(z)>0)表示x.compareTo(z)>0 。
最后,实施者必须确保x.compareTo(y)==0意味着sgn(x.compareTo(z)) == sgn(y.compareTo(z)) ,对于所有z 。
强烈建议,但不要严格要求(x.compareTo(y)==0) == (x.equals(y)) 。 一般来说,任何实现Comparable接口并违反这种情况的类应清楚地表明这一点。 推荐的语言是“注意:此类具有与equals不一致的自然排序”。
在前面的描述中,符号sgn( ) 表达式表示数学符号函数,其定义根据表达式的值是否为负,零或正返回的-1一个,0,或1。
参数
o - 要比较的对象。
结果
负整数,零或正整数,因为该对象小于,等于或大于指定对象。
异常
NullPointerException - 如果指定的对象为空
ClassCastException - 如果指定的对象的类型阻止它与该对象进行比较。
Comparable和 Comparator的区别
Comparable可以认为是一个内比较器,实现了Comparable接口的类有一个特点,就是这些 类是可以和自己比较的,至于具体和另一个实现了Comparable接口的类如何比较,则依赖compareTo方法的实现,compareTo方法也被称为自然比较方法。如果开发者add进入一个Collection的对象想要Collections的sort方法帮你自动进行排序的话,那么这个对象必须实现Comparable接口。compareTo方法的返回值是int,有三种情况:
1、比较者大于被比较者(也就是compareTo方法里面的对象),那么返回正整数
2、比较者等于被比较者,那么返回0
3、比较者小于被比较者,那么返回负整数
Comparator可以认为是是一个外比较器,个人认为有两种情况可以使用实现Comparator接口的方式:
1、一个对象不支持自己和自己比较(没有实现Comparable接口),但是又想对两个对象进行比较。
2、一个对象实现了Comparable接口,但是开发者认为compareTo方法中的比较方式并不是自己想要的那种比较方式。
Comparator接口里面有一个compare方法,方法有两个参数T o1和T o2,是泛型的表示方式,分别表示待比较的两个对象,方法返回值和Comparable接口一样是int,有三种情况:
1、o1大于o2,返回正整数
2、o1等于o2,返回0
3、o1小于o3,返回负整数
总结一下,这两种比较器Comparable和Comparator,后者相比前者有如下优点:
- 个性化比较:如果实现类没有实现Comparable接口,又想对两个类进行比较(或者实现类实现了Comparable接口,但是对compareTo方法内的比较算法不满意),那么可以实现Comparator接口,自定义一个比较器,写比较算法。
- 解耦:实现Comparable接口的方式比实现Comparator接口的耦合性要强一些,如果要修改比较算法,要修改Comparable接口的实现类,而实现Comparator的类是在外部进行比较的,不需要对实现类有任何修改。从这个角度说,其实有些不太好,尤其在我们将实现类的.class文件打成一个.jar文件提供给开发者使用的时候。
成员变量
- String类中使用字符数组(value[])用来存放数据,value同样被final修饰的,这意味着value初始化后将不能指向其它引用,但是数组内容是可以更改的
- 成员变量hash用于缓存hashcode,String重写了hashCode方法并重新定义了hashcode的生成规则;成员变量serialVersionUID与serialPersistentFields都是用于对象序列化
构造方法
String类的构造方法很多,具体请参考api文档:http://www.matools.com/api/java8
举例详解:
通过使用平台的默认字符集解码指定的字节数组来构造新的 String 。中this方法:
checkBounds(bytes, offset, length);检查数组是否越界。
平台的默认字符集解码:具体解释请看:https://zhuanlan.zhihu.com/p/31390080,补充:通过设置idea文件编码也可以改变默认字符集解码
打印输出为GBK;
改为UTF-8
其余的构造方法请参考上面的api文档。
具体方法
- charAt(int index):返回 char指定索引处的值。利用成员变量缓存的数组直接索引读取
public char charAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
- codePointAt(int index):返回指定索引处的字符(Unicode代码点)。返回指定索引位置字符的Unicode的值,比如:a=97
public int codePointAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return Character.codePointAtImpl(value, index, value.length);
}
- getChars(arguments):将此字符串中的字符复制到目标字符数组中。
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(Object src, int srcPos,Object dest, int destPos,int length);进行行数组复制。
参数:src:源数组;desc:目标数组;srcPos:原数组的下标索引;destPos:目标数组下标索引;length:复制的长度。
从源数组src下标srcPos的位置复制长度为length到目标数组dest下标destPos位置长度length,如果长度超出两个数组的长度抛出java.lang.ArrayIndexOutOfBoundsException。如果不是数组类型则会抛出java.lang.ArrayStoreException
- getBytes():使用平台的默认字符集将此 String编码为字节序列,将结果存储到新的字节数组中。返回上字符集的编码。平台默认字符集上面构造方法有解释。
该方法有多个重载方法,具体请看api文档。例如:getBytes(String chartName):chartName:字符集的名字 - equals(Object anObject):将此字符串与指定对象进行比较。注意参数是Object类型哦!
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实现的方式:先“==”比较两个对象的地址是否相等,如果地址相等直接返回true。如果对象的地址不相等,则去比较两个字符串的每个字符是否相等,如果全部相等返回true,否则返回false。
@Test
public void test4() {
String s1 ="123456";
String s2 ="123456";
String s3 =new String("123456");
System.out.println(s1==s2);
System.out.println(s1.equals(s2));
System.out.println(s1==s3);
System.out.println(s1.equals(s3));
}
返回结果:true;true;false;true
由上面例子可以知道,当用=创建新字符串时,并没有在堆区创建一个新的字符串,而是复制对象的地址给了另外一个。
通过new创建的字符串则实实在在的在堆区开辟了一个空间用于存放字符串。
- boolean contentEquals(CharSequence cs):将此字符串与指定的CharSequence进行 CharSequence
public boolean contentEquals(CharSequence cs) {
// Argument is a StringBuffer, StringBuilder
if (cs instanceof AbstractStringBuilder) {
if (cs instanceof StringBuffer) {
synchronized(cs) {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
} else {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
}
// Argument is a String
if (cs instanceof String) {
return equals(cs);
}
// Argument is a generic CharSequence
char v1[] = value;
int n = v1.length;
if (n != cs.length()) {
return false;
}
for (int i = 0; i < n; i++) {
if (v1[i] != cs.charAt(i)) {
return false;
}
}
return true;
}
该方法传的参数是CharSequence,里面的实现可以看出,先是与AbstractStringBuilder类型比较,AbstractStringBuilder是一个抽象类,里面的方法大部分和String类里面的方法差不多,也做了一些改变,源码没仔细阅读。它子类是StringBuffer和StringBuilder,当类型为StringBuffer加了锁synchronized(同步)。当为char类型时,比较每个字符是否相等。
- boolean equalsIgnoreCase(String anotherString):忽略大小写字符串比较。
public boolean equalsIgnoreCase(String anotherString) {
return (this == anotherString) ? true
: (anotherString != null)
&& (anotherString.value.length == value.length)
&& regionMatches(true, 0, anotherString, 0, value.length);
}
如果对象地址相等直接返回ture,如果为null和长度不想等返回false,否则regionMatches(true, 0, anotherString, 0, value.length);比较
boolean regionMatches(boolean ignoreCase, int toffset,
String other, int ooffset, int len):测试两个字符串区域是否相等。
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;
// Note: toffset, ooffset, or len might be near -1>>>1.
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) {
// If characters don't match but case may be ignored,
// try converting both characters to uppercase.
// If the results match, then the comparison scan should
// continue.
char u1 = Character.toUpperCase(c1);
char u2 = Character.toUpperCase(c2);
if (u1 == u2) {
continue;
}
// Unfortunately, conversion to uppercase does not work properly
// for the Georgian alphabet, which has strange rules about case
// conversion. So we need to make one last check before
// exiting.
if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
continue;
}
}
return false;
}
return true;
}
该方法有一个参数boolean ignoreCase是否忽略大小写,为true忽略大小写,flase反之。toffset源目标字符串开始下标索引,other比较的目标字符串,ooffset目标字符串开始下表索引,len比较的长度。总之把字符串放到缓存的字符数组value里面,然后依次比较字符是否==,如果为true,continue跳出本次循环,如果为flase去比较字符转换成同大写和同小写是否相等,相等返回true,不想等返回flase。
- int compareTo(String anotherString): 按字典顺序比较两个字符串。 比较是基于字符串中每个字符的Unicode值。 由该String对象表示的字符序列按字典顺序与由参数字符串表示的字符序列进行比较。 如果String对象按字典顺序排列在参数字符串之前,结果为负整数。 结果是一个正整数,如果String对象按字典顺序跟随参数字符串。 如果字符串相等,结果为零; compareTo返回0 ,当equals(Object)方法将返回true 。
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;
}
- 静态内部类private static class CaseInsensitiveComparator
private static class CaseInsensitiveComparator
implements Comparator<String>, java.io.Serializable {
// use serialVersionUID from JDK 1.2.2 for interoperability
private static final long serialVersionUID = 8575799808933029326L;
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++) {
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;
}
}
}
}
return n1 - n2;
}
/** Replaces the de-serialized object. */
private Object readResolve() { return CASE_INSENSITIVE_ORDER; }
}
该类实现了Comparator自定了一个忽略大小写的外部比较器。里面的compare方法也是同转为大写去比较Unicode值。不等于市返回字符Unicode值相减的值。
知识点: java允许我们在一个类里面定义静态类。比如内部类(nested class)。把nested class封闭起来的类叫外部类。在java中,我们不能用static修饰顶级类(top level class)。只有内部类可以为static。
静态内部类和非静态内部类之间到底有什么不同呢?下面是两者间主要的不同。
(1)内部静态类不需要有指向外部类的引用。但非静态内部类需要持有对外部类的引用。
(2)非静态内部类能够访问外部类的静态和非静态成员。静态类不能访问外部类的非静态成员。他只能访问外部类的静态成员。
(3)一个非静态内部类不能脱离外部类实体被创建,一个非静态内部类可以访问外部类的数据和方法,因为他就在外部类里面。
- boolean regionMatches(int toffset, String other, int ooffset,int len):用于比较一个字符串中特定区域与另一特定区域
public boolean regionMatches(boolean ignoreCase, int toffset,String other, int ooffset, int len):用于比较一个字符串中特定区域与另一特定区域(忽略大小写)
public boolean regionMatches(int toffset, String other, int ooffset,
int len) {
char ta[] = value;
int to = toffset;
char pa[] = other.value;
int po = ooffset;
// Note: toffset, ooffset, or len might be near -1>>>1.
if ((ooffset < 0) || (toffset < 0)
|| (toffset > (long)value.length - len)
|| (ooffset > (long)other.value.length - len)) {
return false;
}
while (len-- > 0) {
if (ta[to++] != pa[po++]) {
return false;
}
}
return true;
}
- boolean startsWith(String prefix, int toffset):判断给定的字符串prefix与当前字符串从toffset索引开始检索,如果包含返回为true,不包含返回flase。
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)) {
return false;
}
while (--pc >= 0) {
if (ta[to++] != pa[po++]) {
return false;
}
}
return true;
}
public boolean startsWith(String prefix) {
return startsWith(prefix, 0);
}
从中可以发现一些思想,当程序走完返回true,没走完flase,意思就是有字符不相等。
ps:如果当前我写代码的话,就会判断等于然后继续走,而这段代码是不等于直接flase。总之就是反过来去实现业务,有时候代码更精简和效率高。
public boolean endsWith(String suffix):测试此字符串是否以指定的后缀结尾。
public boolean endsWith(String suffix) {
return startsWith(suffix, value.length - suffix.value.length);
}
上面几个方法都是掉用startsWith()方法,充分利用方法重用和重载。注意:空字符串测试都是为true,如果长度越界返回false。
- indexOf(char[] source, int sourceOffset, int sourceCount,char[] target, int targetOffset, int targetCount,int fromIndex):
indexOf(params)里面的主要调用方法。注意改放方法同名的重载方法里面的参数。
static int indexOf(char[] source, int sourceOffset, int sourceCount,
char[] target, int targetOffset, int targetCount,
int fromIndex) {
if (fromIndex >= sourceCount) {
return (targetCount == 0 ? sourceCount : -1);
}
if (fromIndex < 0) {
fromIndex = 0;
}
if (targetCount == 0) {
return fromIndex;
}
char first = target[targetOffset];
int max = sourceOffset + (sourceCount - targetCount);
for (int i = sourceOffset + fromIndex; i <= max; i++) {
/* Look for first character. */
if (source[i] != first) {
while (++i <= max && source[i] != first);
}
/* Found first character, now look at the rest of v2 */
if (i <= max) {
int j = i + 1;
int end = j + targetCount - 1;
for (int k = targetOffset + 1; j < end && source[j]
== target[k]; j++, k++);
if (j == end) {
/* Found whole string. */
return i - sourceOffset;
}
}
}
return -1;
}
参数意义跟上面的类似。源码有点没看懂下次补充。
- int lastIndexOf(String str):检索字符最后一次出现索引的位置。
- substring(params):从指定索引截取子字符串。
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);
}
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);
}
public CharSequence subSequence(int beginIndex, int endIndex) {
return this.substring(beginIndex, endIndex);
}
这三个方法都是创建一个新的子字符串。不是修改目标字符串。
- public String concat(String str):将指定的字符串连接到此字符串的末尾。如果参数字符串的长度为0,则返回此字符串对象。否则,返回一个字符串对象,该对象表示一个字符序列,该字符序列是由这个字符串对象表示的字符序列和参数字符串表示的字符序列的串联。
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);
}
总体显示对调用该方法的字符串扩容(扩容大小:该字符串加参数字符串的大小),然后在把给的参数字符串加进去。
- public boolean matches(String regex) :指示此字符串是否与给定正则表达式匹配。调用这种形式的str.matches(regex)方法会得到与表达式完全相同的结果
public boolean matches(String regex) {
return Pattern.matches(regex, this);
}
public static boolean matches(String regex, CharSequence input) {
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(input);
return m.matches();
}
该方法需要将给定的正则表达式匹配编译成Pattern对象,Pattern对象解释:正则表达式的编译表示形式。正则表达式指定为字符串,必须首先编译成该类的实例。然后,可以使用生成的模式创建一个Matcher对象,该对象可以根据正则表达式匹配任意字符序列。执行匹配所涉及的所有状态都位于匹配器中,因此许多匹配器可以共享相同的模式。(具体怎么实现的还没看源码)
- public boolean contains(CharSequence s):当且仅当此字符串包含指定的char值序列时,返回true。
public boolean contains(CharSequence s) {
return indexOf(s.toString()) > -1;
}
该方法调用indexOf(params)方法判读!如果大于-1,说明在该字符串包含给定字符串,否则反之。
- public String replaceFirst(String regex, String replacement):用给定替换替换与给定正则表达式匹配的字符串的第一个子字符串
public String replaceAll(String regex, String replacement):用给定替换替换与给定正则表达式匹配的字符串的每个子字符串。
public String replaceFirst(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceFirst(replacement);
}
设级到正则表达式的,都要转换成Pattern对象,生成的模式创建一个Matcher对象,在去调用replaceFirst(replacement)方法。replaceAll同理
public String replaceFirst(String replacement) {
if (replacement == null)
throw new NullPointerException("replacement");
reset();
if (!find())
return text.toString();
StringBuffer sb = new StringBuffer();
appendReplacement(sb, replacement);
appendTail(sb);
return sb.toString();
}
以后再补充这个方法。
- public String replace(CharSequence target, CharSequence replacement):将此字符串中与文字目标序列匹配的每个子字符串替换为指定的文字替换序列。替换从字符串的开始到结束,例如,将字符串“aaa”中的“aa”替换为“b”,将得到“ba”而不是“ab”
public String replace(CharSequence target, CharSequence replacement) {
return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
}
- public String[] split(String regex):将此字符串分割为给定正则表达式的匹配项。该方法的工作原理就像调用具有给定表达式和一个零极限参数的双参数拆分方法一样。因此,结果数组中不包含尾随空字符串。
public String[] split(String regex) {
return split(regex, 0);
}
public String[] split(String regex, int limit) {
/* fastpath if the regex is a
(1)one-char String and this character is not one of the
RegEx's meta characters ".$|()[{^?*+\\", or
(2)two-char String and the first char is the backslash and
the second is not the ascii digit or ascii letter.
*/
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;
ArrayList<String> list = new ArrayList<>();
while ((next = indexOf(ch, off)) != -1) {
if (!limited || list.size() < limit - 1) {
list.add(substring(off, next));
off = next + 1;
} else { // last one
//assert (list.size() == limit - 1);
list.add(substring(off, value.length));
off = value.length;
break;
}
}
// If no match was found, return this
if (off == 0)
return new String[]{this};
// Add remaining segment
if (!limited || list.size() < limit)
list.add(substring(off, value.length));
// Construct result
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);
}
源码有点复杂,下次复习补。