Java字符串String类学习笔记一 ,这篇文章主要学习了String的域及以下的几个几个方法:
int length()
isEmpty()
charAt(int index)
getBytes(String charsetName)
contentEquals(StringBuffer sb)
regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len)
compareTo(String anotherString)
compareToIgnoreCase(String str)
接下来的这篇文章我会学习String类的如下几个方法以及其重载的方法:
-
boolean startsWith(String prefix, int toffset)
-
boolean endsWith(String suffix)
-
int hashCode()
-
int indexOf(int ch, int fromIndex)
-
int lastIndexOf(int ch, int fromIndex)
-
int indexOf(String str, int fromIndex)
-
int indexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex)
-
int lastIndexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex)
-
String substring(int beginIndex)
-
String substring(int beginIndex, int endIndex)
-
CharSequence subSequence(int beginIndex, int endIndex)
-
String concat(String str)
-
String replace(char oldChar, char newChar)
-
boolean matches(String regex)
-
boolean contains(CharSequence s)
-
String replaceFirst(String regex, String replacement)
-
String replaceAll(String regex, String replacement)
-
String replace(CharSequence target, CharSequence replacement)
-
String[] split(String regex, int limit)
-
static String join(CharSequence delimiter, CharSequence... elements)
-
static String join(CharSequence delimiter, Iterable<? extends CharSequence> elements)
-
String toLowerCase(Locale locale)
-
String toUpperCase(Locale locale)
-
String trim()
-
String toString()
-
char[] toCharArray()
-
static String format(String format, Object... args)
-
static String format(Locale l, String format, Object... args)
-
static String valueOf(Object obj)
-
static String valueOf(char data[])
-
static String valueOf(char data[], int offset, int count)
-
static String copyValueOf(char data[], int offset, int count)
-
static String copyValueOf(char data[])
-
static String valueOf(boolean b)
-
static String valueOf(int i)
-
static String valueOf(long l)
-
static String valueOf(float f)
-
static String valueOf(double d)
-
native String intern()
上面的方法基本我都会看看源码,有没看懂的我也会写在文章中,还希望大家可以共同学习探讨,互相学习。
String类有一个静态的方法,也就是可以直接用类名.方法名的形式调用,这个方法是valueOf(param)
,该方法又一个参数,可以是int
,long
,float
,boolean
,double
基础类型,调用该方法可以将基础类型转为字符串类型,实现方法就是各自的valueOf
方法中用基础类型对应的包装类,调用toString()
方法来进行转换,这里只看其中一个的源码,如下所示:
public static String valueOf(double d) {
return Double.toString(d);
}
此处需要注意的是,当你调用String的valueOf
方法时,你应该判断即将转为字符串的内容是否为null
,如果为null
,调用该方法就会报NullPointException
异常;如果不为null
,则可以调用该方法将其转为字符串。下面是当内容为null
时,报空指针异常的代码:
public class TestString {
public static void main(String[] args){
System.out.println(String.valueOf(null));
}
}
异常:
Exception in thread "main" java.lang.NullPointerException
at java.lang.String.<init>(String.java:166)
at java.lang.String.valueOf(String.java:3008)
at com.java.lee.TestString.main(TestString.java:11)
public boolean startsWith(String prefix) {
return startsWith(prefix, 0);
}
boolean startsWith(String prefix)
这个方法是我经常会在开发中用到的,来判断字符串是否以某个字符或者某些字符开始的,来看一下它的实现,通过源码,我们可以看到他调用了boolean startsWith(String prefix,int index)
这个方法,第一个参数是要判断以什么字符开头,第二个参数传的是0,表示是从这个字符串的开头来做匹配,如果匹配到则返回true,否则返回false。
下面我们来看一下方法boolean startsWith(String prefix, int toffset)
的源码:
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;
}
该方法有两个参数,我在前面也已经介绍过了,首先是判断传入的索引位置,如果该索引值小于0或者大于字符串的长度,则直接返回false,否则进行下一步的判断;这个循环的终止条件就是当传入的字符串的长度递减小于0时,终止循环,字符数组ta表示的是原字符串的字符数组表示,字符数组pa表示的是匹配的字符串前缀数组表示,因为是循环遍历数组,所以要防止数组越界,所以临界条件就是pc的值。
public boolean endsWith(String suffix) { return startsWith(suffix, value.length - suffix.value.length); }
boolean endsWith(String suffix)
该方法表示字符串以某字符结尾,里面具体是调用了startsWith
方法,第一个参数还是结尾判断的子字符串,第二个参数是字符串的长度与子字符串长度的差值,也就是说,做比较的时候,是从差值的索引在原字符串中取值与子字符串的第一个字符做比较,其余字符以此类推。
此处你可以这样理解第二个参数的传值,比如第一个字符串是abcdef
,第二个子字符串是ef
,我们要判断前者是否以后者为后缀结尾的,那么我们就需要知道从原字符串的哪里做比较,按照索引值来说,我们应该从第一个字符串的索引为4的地方开始,这正好也是原字符串与子字符串的长度差值,所以第二个参数值的来源就是因为这个原因。
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;
}
hashCode()
该方法主要是用来返回字符串的哈希值,主要是按照for循环中的那个计算来进行返回。
public int indexOf(int ch, int fromIndex) {
final int max = value.length;
if (fromIndex < 0) {
fromIndex = 0;
} else if (fromIndex >= max) {
// Note: fromIndex might be near -1>>>1.
return -1;
}
if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
// handle most cases here (ch is a BMP code point or a
// negative value (invalid code point))
final char[] value = this.value;
for (int i = fromIndex; i < max; i++) {
if (value[i] == ch) {
return i;
}
}
return -1;
} else {
return indexOfSupplementary(ch, fromIndex);
}
}
int indexOf(int ch, int fromIndex)
该方法有两个参数,第一个参数是字符的unicode value,你可以通过unicode表来查找,链接如下所示:unicode characters,第二个参数表示的是索引,比如一个字符串中有多个字符组成,每个字符的unicode value是不相同的,所以我们如果想知道某个字符第几次出现在这个字符串中,我们可以用该值,实现思路:
- 获取字符串的长度
- 判断第二个参数fromIndex,如果小于0,则按照0来算;如果大于等于字符串的长度,则返回-1;其余的情况执行下面的if条件判断。
- value字符数组的内容就是字符串的字符数组表示形式,然后从fromIndex开始循环,一直循环完这个字符串,如果索引i的值与传入的unicode point的值相等,则返回该索引,否则返回-1,结束方法。
下面举个例子,这个例子你也可以在youtobe上面看到:
public class TestString {
public static void main(String[] args){
String str = "JAVA";
//A的unicode point值为65
System.out.println(str.codePointAt(1));
//返回值为1
System.out.println(str.indexOf(65,0));
//返回值为3
System.out.println(str.indexOf(65,2));
}
}
从上面的程序片段中我们可以看出第一个打印语句调用了codePointAt(int index)
方法,该方法返回了字符串中索引为1的字符的unicode point的值,然后我们分别有了下面两个打印语句,分别是看从索引为0和2 开始匹配第一个出现该字符A的索引位置。分别返回了1和3是A在字符串中的两个索引位置。
public int codePointAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return Character.codePointAtImpl(value, index, value.length);
}
该方法是根据传入的索引来返回字符串中该索引所对字符的unicode point值,里面调用了Character
的codePointAtImpl
方法
public int lastIndexOf(int ch, int fromIndex) {
if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
// handle most cases here (ch is a BMP code point or a
// negative value (invalid code point))
final char[] value = this.value;
int i = Math.min(fromIndex, value.length - 1);
for (; i >= 0; i--) {
if (value[i] == ch) {
return i;
}
}
return -1;
} else {
return lastIndexOfSupplementary(ch, fromIndex);
}
}
lastIndexOf(int ch, int fromIndex)
该方法返回的是该字符在字符串中最后一次出现的索引位置。参数ch的值就是unicode point,范围是从0到0xFFFF,两个个参数的意思分别是:
- ch:表示该字符的unicode code point
- fromIndex:表示要从该字符串的哪个位置开始搜索匹配该字符,如果该值大于或者等于字符串的长度,就相当于要搜索整个字符串来匹配该字符,注意循环,是从该字符串的末尾字符开始匹配,如果匹配到则返回该字符的索引值,如果没有匹配到,则返回-1。
public int indexOf(String str) {
return indexOf(str, 0);
}
int indexOf(String str)
该方法是我们在字符串中常用的操作,用来返回某个字符在字符串中首次出现的索引位置,可以用来判断字符串中是否包含该字符。通过该方法的源码我们可以看出,这个方法的参数是一个String类型的,与前面的不同前面是int类型的。它返回了indexOf(str, 0)
表示调用该方法,并且第一个参数就是要查找的字符,第二个参数表示要从字符串的哪里开始查找。
public int indexOf(String str, int fromIndex) {
return indexOf(value, 0, value.length,
str.value, 0, str.value.length, fromIndex);
}
public class TestString {
public static void main(String[] args){
String str = "JAVA";
//返回A在字符串中第一出现的索引位置,从字符串索引为0处开始匹配,返回值为1
System.out.println(str.indexOf('A'));
//返回A在字符串中第一出现的索引位置,从字符串索引为2处开始匹配,返回值为3
System.out.println(str.indexOf('A',2));
//返回A在字符串中第一出现的索引位置,从字符串索引为5处开始匹配,此时5大于了字符串的长度,所以返回了-1
System.out.println(str.indexOf('A',5));
}
}
该方法的根源实现是如下代码:
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;
}
我对该方法的参数解释一下:
- source:是一个字符数组,表示的是该字符要在某个字符串中搜索,所搜索的字符串的字符数组表现形式就是source
- sourceOffset:表示要从字符数组中的哪个索引开始搜索
- sourceCount:原字符串的长度
- target:就是你要搜索的那个字符串字符数组表现形式
- targetOffset:表示要搜索的那个字符串字符数组哪个索引开始搜索
- targetCount:搜索的目标字符串的长度
- fromIndex:表示要从哪个索引位置开始搜索
public int lastIndexOf(String str) {
return lastIndexOf(str, value.length);
}
public int lastIndexOf(String str, int fromIndex) {
return lastIndexOf(value, 0, value.length,
str.value, 0, str.value.length, fromIndex);
}
static int lastIndexOf(char[] source, int sourceOffset, int sourceCount,
String target, int fromIndex) {
return lastIndexOf(source, sourceOffset, sourceCount,
target.value, 0, target.value.length,
fromIndex);
}
static int lastIndexOf(char[] source, int sourceOffset, int sourceCount,
char[] target, int targetOffset, int targetCount,
int fromIndex) {
/*
* Check arguments; return immediately where possible. For
* consistency, don't check for null str.
*/
int rightIndex = sourceCount - targetCount;
if (fromIndex < 0) {
return -1;
}
if (fromIndex > rightIndex) {
fromIndex = rightIndex;
}
/* Empty string always matches. */
if (targetCount == 0) {
return fromIndex;
}
int strLastIndex = targetOffset + targetCount - 1;
char strLastChar = target[strLastIndex];
int min = sourceOffset + targetCount - 1;
int i = min + fromIndex;
startSearchForLastChar:
while (true) {
while (i >= min && source[i] != strLastChar) {
i--;
}
if (i < min) {
return -1;
}
int j = i - 1;
int start = j - (targetCount - 1);
int k = strLastIndex - 1;
while (j > start) {
if (source[j--] != target[k--]) {
i--;
continue startSearchForLastChar;
}
}
return start - sourceOffset + 1;
}
}
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);
}
substring(int beginIndex)
该方法是用来对字符串进行截取,参数是表示要从哪里开始截取,一直截取到字符串长度结束,截取的结果是包含参数所在位置的字符。
- 如果索引值小于0 则抛出异常
- 如果字符串的长度减掉要截取得索引值,结果小于0,则抛出异常
- 除了上述两种情况外,如果截取的索引为0,则返回当前字符串对象,否则就调用String类的构造方法
String(value, beginIndex, subLen)
来返回子字符串。
下面是该方法的demo:
public class TestString {
public static void main(String[] args){
String str = "JAVA";
//JAVA
System.out.println(str.substring(0));
//AVA
System.out.println(str.substring(1));
}
}
下面是上面substring
的一个重载方法,源码如下:
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);
}
该方法有两个参数,第一个参数是指定要从字符串哪里开始截取,第二个字符串表示截取的字符串到哪里为止,这个方法有个特点就是,截取结果是前闭后开,意思就是截取的时候开始索引的位置字符包含在截取的内容中,而截至位置的字符是不包含在截取的内容中。下面个一个demo
public class TestString {
public static void main(String[] args){
String str = "wonders group";
//ond
System.out.println(str.substring(1,4));
}
}
你也可以这样理解该方法,从开始索引截取字符串,截取的长度为endIndex与beginIndex的差值。
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);
}
String concat(String str)
该方法就是将字符串进行连接,参数是你要将某个字符进行连接在调用者的尾部。如果参数的长度为0,则直接返回当前字符串对象,否则的话就调用Arrays类的copyOf方法转为字符数组,然后在调用String类的getChars(char[] ch,int len)
方法来进行粘合,最后通过String类的构造方法将其进行转为字符串的形式。下面是该方法的demo:
public class TestString {
public static void main(String[] args){
String str = "wonders group";
//wonders group based in shanghai
System.out.println(str.concat(" based in shanghai"));
}
}
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;
}
String replace(char oldChar, char newChar)
该方法将原有字符串中所有的oldChar字符替换为了newChar,构造了一个新的字符串返回。第一个参数是要替换的字符,第二个参数是用来替代原有字符的字符。如果oldChar在原有字符串中不存在,那么返回的结果就是原有字符串。
看一下源码的实现思路:
- 如果oldChar与newChar相等,则直接返回当前对象字符串;
- 当oldChar与newChar不相等的时候,然后进行逻辑处理,我看了一下代码的逻辑,它的实现是,先循环一次字符数组,找到第一个与oldChar相等的字符的下标,创建一个新的字符数组,然后进行一次循环,将该下标之前的所有字符全部赋值到新的字符数组中,然后开始新的一个循环,临界条件就是索引必须小于字符串的长度,此时,将原字符数组的值赋予字符变量c,然后用三目表达式来判断,如果c与oldChar相等,则返回newChar给该索引的值,否则就原样返回,变量i自增,最后返回新的字符串。
public boolean matches(String regex) {
return Pattern.matches(regex, this);
}
boolean matches(String regex)
该方法是用来判断字符串是否与参数中的正则表达式匹配,如果matches的话,则返回true,否则返回false。正则表达是还是需要学习和研究的,我的正则表达式很Low,所以这个方法了解一下,具体就没有demo了。
public boolean contains(CharSequence s) {
return indexOf(s.toString()) > -1;
}
boolean contains(CharSequence s)
该方法是用来判断字符串中是否包含了某个字符序列,如果包含则返回true,否则返回false。它里面的实现也是调用indexOf方法来做判断,如果能存在的话,就返回非负数,这样符合大于-1,此时contains
方法就会返回true,反之就会返回false.
public String replaceFirst(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceFirst(replacement);
}
String replaceFirst(String regex, String replacement)
该方法是用来替换字符串中首次出现的字符序列,第一个参数可以是一个正则表达式,也可以是一个字符序列,第二个参数是新的字符序列用来替换字符串中与参数regex所匹配的第一个字符串。下面是一个小的demo程序:
String Str = new String("Welcome to Tutorialspoint.com");
System.out.print("Return Value :" );
//Return Value :AMROOD
System.out.println(Str.replaceFirst("(.*)Tutorials(.*)", "AMROOD"));
System.out.print("Return Value :" );
//Return Value :Welcome to AMROODpoint.com
System.out.println(Str.replaceFirst("Tutorials", "AMROOD"));
public String replaceAll(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}
String replaceAll(String regex, String replacement)
该方法是用来替换字符串中所有符合条件的字符,然后返回一个新的字符串。下面对参数进行一下说明:
- regex:String类型,它可以是一个正则表达式,也可以是一个字符串或者字符,用来匹配原有字符串中的字符序列
- replacement:该参数是一个String类型,用来替换第一个参数所匹配的字符。
public String replace(CharSequence target, CharSequence replacement) {
return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
}
String replace(CharSequence target, CharSequence replacement)
该方法也是用来替换字符串中的字符序列,只会替换匹配的目标字符,参数说明:
- target:要被替换的字符序列
- replacement:需要代替target字符序列的字符
public class TestString {
public static void main(String[] args){
String Str = new String("aaabaa");
//helloabhello
System.out.println(Str.replace("aa","hello"));
}
}
可以从代码中看出来,这个方法是如果匹配到了target字符,然后就用replacement进行替换,替换完之后然后再继续匹配target字符,如果还有,则继续替换,直到匹配完所有字符。
public String[] split(String regex) {
return split(regex, 0);
}
String[] split(String regex)
该方法是用来将字符串按照正则表达式或者字符序列来进行拆分,它调用了split的重载方法,下面是该重载方法的源码
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);
}
split
该方法会返回一个String类型的数组,数组的内容是按照第一个参数进行匹配拆分的,如果limt为0,则从字符串的开始匹配第一个参数,如果匹配到则将该字符之前的内容作为数组的一个元素,然后进行下一个的匹配,直到整个字符串遍历完毕。该数组中的元素也是按照字符串中字符出现的顺序进行显示的。如果没有匹配到的话,则返回原有的字符串。
public static String join(CharSequence delimiter, CharSequence... elements) {
Objects.requireNonNull(delimiter);
Objects.requireNonNull(elements);
// Number of elements not likely worth Arrays.stream overhead.
StringJoiner joiner = new StringJoiner(delimiter);
for (CharSequence cs: elements) {
joiner.add(cs);
}
return joiner.toString();
}
static String join(CharSequence delimiter, CharSequence... elements)
该方法是String类的一个静态方法,返回值类型为String类型,有两个参数,分别是:
- delimiter:是一个CharSequence的字符序列,代表的是限定符;
- elements:是字符序列类型,它是一个可变的参数列表,表示此处可有多个参数,都是CharSequence的类型。
该方法就是用delimiter来分隔第二个参数elements元素,然后返回一个字符串。下面是一个demo示例:
public class TestString {
public static void main(String[] args){
String str = " ";
//wondersgroup based in shanghai
System.out.println(String.join(str,"wondersgroup","based","in","shanghai"));
List<String> list= Arrays.asList("Steve", "Rick", "Peter", "Abbey");
String names = String.join(" | ", list);
//Steve | Rick | Peter | Abbey
System.out.println(names);
}
}
下面是它的一个构造方法的源码,只是第二个参数是集合类型:
public static String join(CharSequence delimiter,
Iterable<? extends CharSequence> elements) {
Objects.requireNonNull(delimiter);
Objects.requireNonNull(elements);
StringJoiner joiner = new StringJoiner(delimiter);
for (CharSequence cs: elements) {
joiner.add(cs);
}
return joiner.toString();
}
通过这两个方法我们可以看出来,他的内部实现是通过创建StringJoiner
来创建对象,并且循环可变参数列表,访问每一个元素,然后调用joiner的add方法进行追加,最后调用joiner的toString
方法将其转为字符串。【注明:StringJoiner
类是JDK1.8引入的,final修饰的不可变的类,join
方法也是JDK1.8才引入进来的】
public String toLowerCase(Locale locale) {
if (locale == null) {
throw new NullPointerException();
}
int firstUpper;
final int len = value.length;
/* Now check if there are any characters that need to be changed. */
scan: {
for (firstUpper = 0 ; firstUpper < len; ) {
char c = value[firstUpper];
if ((c >= Character.MIN_HIGH_SURROGATE)
&& (c <= Character.MAX_HIGH_SURROGATE)) {
int supplChar = codePointAt(firstUpper);
if (supplChar != Character.toLowerCase(supplChar)) {
break scan;
}
firstUpper += Character.charCount(supplChar);
} else {
if (c != Character.toLowerCase(c)) {
break scan;
}
firstUpper++;
}
}
return this;
}
char[] result = new char[len];
int resultOffset = 0; /* result may grow, so i+resultOffset
* is the write location in result */
/* Just copy the first few lowerCase characters. */
System.arraycopy(value, 0, result, 0, firstUpper);
String lang = locale.getLanguage();
boolean localeDependent =
(lang == "tr" || lang == "az" || lang == "lt");
char[] lowerCharArray;
int lowerChar;
int srcChar;
int srcCount;
for (int i = firstUpper; i < len; i += srcCount) {
srcChar = (int)value[i];
if ((char)srcChar >= Character.MIN_HIGH_SURROGATE
&& (char)srcChar <= Character.MAX_HIGH_SURROGATE) {
srcChar = codePointAt(i);
srcCount = Character.charCount(srcChar);
} else {
srcCount = 1;
}
if (localeDependent ||
srcChar == '\u03A3' || // GREEK CAPITAL LETTER SIGMA
srcChar == '\u0130') { // LATIN CAPITAL LETTER I WITH DOT ABOVE
lowerChar = ConditionalSpecialCasing.toLowerCaseEx(this, i, locale);
} else {
lowerChar = Character.toLowerCase(srcChar);
}
if ((lowerChar == Character.ERROR)
|| (lowerChar >= Character.MIN_SUPPLEMENTARY_CODE_POINT)) {
if (lowerChar == Character.ERROR) {
lowerCharArray =
ConditionalSpecialCasing.toLowerCaseCharArray(this, i, locale);
} else if (srcCount == 2) {
resultOffset += Character.toChars(lowerChar, result, i + resultOffset) - srcCount;
continue;
} else {
lowerCharArray = Character.toChars(lowerChar);
}
/* Grow result if needed */
int mapLen = lowerCharArray.length;
if (mapLen > srcCount) {
char[] result2 = new char[result.length + mapLen - srcCount];
System.arraycopy(result, 0, result2, 0, i + resultOffset);
result = result2;
}
for (int x = 0; x < mapLen; ++x) {
result[i + resultOffset + x] = lowerCharArray[x];
}
resultOffset += (mapLen - srcCount);
} else {
result[i + resultOffset] = (char)lowerChar;
}
}
return new String(result, 0, len + resultOffset);
}
String toLowerCase(Locale locale)
该方法是将字符串中所有的大写字母转为小写字母,源码感觉有点复杂,我只能说一下我看懂的地方,它首先判断Locale
对象是否为空,如果非空则进行下面的操作,它会首先有一个带标签的for循环,来检查是否存在一些字符需要被改变,判断了字符对应的unicode值然后过滤了所有的小写字母,只统计了非小写的字符个数。(后面就没有怎么看懂)。我们通常是会调用无参的方法,源码如下:
public String toLowerCase() {
return toLowerCase(Locale.getDefault());
}
public String toUpperCase(Locale locale) {
if (locale == null) {
throw new NullPointerException();
}
int firstLower;
final int len = value.length;
/* Now check if there are any characters that need to be changed. */
scan: {
for (firstLower = 0 ; firstLower < len; ) {
int c = (int)value[firstLower];
int srcCount;
if ((c >= Character.MIN_HIGH_SURROGATE)
&& (c <= Character.MAX_HIGH_SURROGATE)) {
c = codePointAt(firstLower);
srcCount = Character.charCount(c);
} else {
srcCount = 1;
}
int upperCaseChar = Character.toUpperCaseEx(c);
if ((upperCaseChar == Character.ERROR)
|| (c != upperCaseChar)) {
break scan;
}
firstLower += srcCount;
}
return this;
}
/* result may grow, so i+resultOffset is the write location in result */
int resultOffset = 0;
char[] result = new char[len]; /* may grow */
/* Just copy the first few upperCase characters. */
System.arraycopy(value, 0, result, 0, firstLower);
String lang = locale.getLanguage();
boolean localeDependent =
(lang == "tr" || lang == "az" || lang == "lt");
char[] upperCharArray;
int upperChar;
int srcChar;
int srcCount;
for (int i = firstLower; i < len; i += srcCount) {
srcChar = (int)value[i];
if ((char)srcChar >= Character.MIN_HIGH_SURROGATE &&
(char)srcChar <= Character.MAX_HIGH_SURROGATE) {
srcChar = codePointAt(i);
srcCount = Character.charCount(srcChar);
} else {
srcCount = 1;
}
if (localeDependent) {
upperChar = ConditionalSpecialCasing.toUpperCaseEx(this, i, locale);
} else {
upperChar = Character.toUpperCaseEx(srcChar);
}
if ((upperChar == Character.ERROR)
|| (upperChar >= Character.MIN_SUPPLEMENTARY_CODE_POINT)) {
if (upperChar == Character.ERROR) {
if (localeDependent) {
upperCharArray =
ConditionalSpecialCasing.toUpperCaseCharArray(this, i, locale);
} else {
upperCharArray = Character.toUpperCaseCharArray(srcChar);
}
} else if (srcCount == 2) {
resultOffset += Character.toChars(upperChar, result, i + resultOffset) - srcCount;
continue;
} else {
upperCharArray = Character.toChars(upperChar);
}
/* Grow result if needed */
int mapLen = upperCharArray.length;
if (mapLen > srcCount) {
char[] result2 = new char[result.length + mapLen - srcCount];
System.arraycopy(result, 0, result2, 0, i + resultOffset);
result = result2;
}
for (int x = 0; x < mapLen; ++x) {
result[i + resultOffset + x] = upperCharArray[x];
}
resultOffset += (mapLen - srcCount);
} else {
result[i + resultOffset] = (char)upperChar;
}
}
return new String(result, 0, len + resultOffset);
}
这个是将字母转为大写的方法源码,我们通常也调用无参的形式:
public String toUpperCase() {
return toUpperCase(Locale.getDefault());
}
public String trim() {
int len = value.length;
int st = 0;
char[] val = value; /* avoid getfield opcode */
while ((st < len) && (val[st] <= ' ')) {
st++;
}
while ((st < len) && (val[len - 1] <= ' ')) {
len--;
}
return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}
String trim()
对于非NULL对象String可以调用该方法,用来去除字符串中首尾空格,第一个循环是判断字符串的前面有几个空格,第二个循环是判断原字符串长度减去字符串后面的几个空格的长度,分别用两个变量来标记,最后只需要截取该字符起始索引和长度就可以了。我当时有个需求是只需要去掉字符串最后的空格,所以我读了该源码,进行了改造,可以参考我这篇博文Java去掉字符串的末尾空格当然可以实现只去掉字符串的首部空格,源码如下:
public String trimFirst(char[] value) {
int len = value.length;
int st = 0;
char[] val = value;
while ((st < len) && (val[st] <= ' ')) {
st++;
}
return ((st > 0) || (len < value.length)) ? new String(val).substring(st, len) : new String(val);
}
一个关于去掉空格的demo示例;
public class TestString {
public static void main(String[] args){
String str = " abc de fg ";
//abc de fg
System.out.println(str.trim());
//9
System.out.println(str.trim().length());
//abc de fg
System.out.println(TestString.trimFirst(str.toCharArray()));
//10
System.out.println(TestString.trimFirst(str.toCharArray()).length());
}
public static String trimFirst(char[] value) {
int len = value.length;
int st = 0;
char[] val = value;
while ((st < len) && (val[st] <= ' ')) {
st++;
}
return ((st > 0) || (len < value.length)) ? new String(val).substring(st, len) : new String(val);
}
}
public char[] toCharArray() {
// Cannot use Arrays.copyOf because of class initialization order issues
char result[] = new char[value.length];
System.arraycopy(value, 0, result, 0, value.length);
return result;
}
char[] toCharArray()
将一个字符串转为字符数组。
public static String format(String format, Object... args) {
return new Formatter().format(format, args).toString();
}
static String format(String format, Object... args)
该方法是用来格式化的,是一个静态方法,返回值为String类型,第一个参数是格式化的形式,后面是一个可变参数列表,下面是一个demo示例:
public class TestString {
public static void main(String[] args){
String str = "GeeksforGeeks.";
String gfg1 = String.format("My Company name is %s", str);
String str2 = String.format("My answer is %.8f", 47.65734);
String str3 = String.format("My answer is %15.8f", 47.65734);
//My Company name is GeeksforGeeks.
System.out.println(gfg1);
//My answer is 47.65734000
System.out.println(str2);
//My answer is 47.65734000
System.out.println(str3);
}
}
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
static String valueOf(Object obj)
该方法是将对象转为字符串,是一个静态的方法,参数是一个对象object,返回值类型是String类型,注意:如果该Object对象为NULL,调用该方法就会报NULLPointException异常,所以调用此方法时应该需要做非NULL判断。
到这里为止String类的源码应该算是看完了,我觉得看源码的同时可以学习他的实现思路,来丰富自己的思路,同时还可以了解该类下面有什么方法,这些方法是用来做什么的,这样在开发中才可以选取恰当的方法来实现。