1.String
str1指向的是常量池中的“abc”,str2指向的是堆内的String对象,这个String对象指向的才是常量池中的“abc”。
String str1="abc";
String str2=new String("abc");
System.out.println(str1==str2);//false
String字符串的底层是字符数组
private final char value[];
通过使用指定的 charset 解码指定的 byte 数组,构造一个新的
String
String str3="abc";
byte[] arr = str3.getBytes("utf8");
String str4=new String(arr, "utf8");
System.out.println(str4);//abc
返回指定索引处的
char
值。
//底层源码
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
System.out.println(str1.charAt(1));//b
按字典顺序比较两个字符串,从第一个字符开始比较它们的ASCII码值。
public static void main(String[] args) throws UnsupportedEncodingException {
String str1="abc";
String str2="ABB";
System.out.println(str1.compareTo(str2));//32
String str3="abc";
System.out.println(str1.compareTo(str3));//0
System.out.println(str2.compareTo(str1));//-32
String str4="abcd";
System.out.println(str1.compareTo(str4));//-1
}
//源码分析
public int compareTo(String anotherString) {
//1.获取字符数组或者说字符串的长度
int len1 = value.length;
int len2 = anotherString.value.length;
//2.取两个长度的最小值
int lim = Math.min(len1, len2);
//3.将两个字符数组赋给新的字符数组
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
//4.对相同索引的字符进行比较,如果相等则进入下一个循环,否则返回ASCII差值
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
//5.如果进行比较的那些字符全部相等,那么返回数组长度的差值
return len1 - len2;
}
将指定字符串连接到此字符串的结尾。
public static void main(String[] args) throws UnsupportedEncodingException {
String str1="abc";
String s = str1.concat("def");
System.out.println(s);//abcdef
}
//源码分析
public String concat(String str) {
//1.获取字符串长度
int otherLen = str.length();
//2.如果传入的字符串长度为0,直接返回当前字符串对象
if (otherLen == 0) {
return this;
}
//3.将需要拼接的字符串以及传入的字符串复制到字符数组中
//public static native void arraycopy(Object src, int srcPos,
// Object dest, int destPos,
// int length);
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
//4.将字符数组转化为字符串并返回
return new String(buf, true);
}
当且仅当此字符串包含指定的 char 值序列时,返回 true
public static void main(String[] args) {
String str="abc";
System.out.println(str.contains("a"));//true
}
//源码分析
public boolean contains(CharSequence s) {//CharSequence是一个接口,String类实现了该接口
return indexOf(s.toString()) > -1;//通过返回的索引值来判断是否有该char值序列
}
测试此字符串是否以指定的后缀结束。
public static void main(String[] args) {
String s="abc";
System.out.println(s.endsWith("bc"));//true
}
//源码分析
public boolean endsWith(String suffix) {
//调用startWith方法
return startsWith(suffix, value.length - suffix.value.length);
}
public boolean startsWith(String prefix, int toffset) {
char ta[] = value;
int to = toffset;//toffset表示从什么位置开始比较
char pa[] = prefix.value;
int po = 0;
int pc = prefix.value.length;
// Note: toffset might be near -1>>>1.
//如果当前字符串对象长度小于后缀字符串长度,这里只针对endsWith分析,那么返回false
if ((toffset < 0) || (toffset > value.length - pc)) {
return false;
}
while (--pc >= 0) {//这一步是用于防止越界
if (ta[to++] != pa[po++]) {
return false;
}
}
return true;
}
将此字符串与指定的对象比较。
public static void main(String[] args) {
String s="abc";
System.out.println("abc".equals(s));//true
}
//源码分析
public boolean equals(Object anObject) {
if (this == anObject) {//1.如果它们的地址相同,那么必然是同一个对象
return true;
}
if (anObject instanceof String) {//2.如果参数对象是String的实例
String anotherString = (String)anObject;//强转为String
int n = value.length;
if (n == anotherString.value.length) {//3.判断长度是否一样,不一样就不用比了
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])//只要有一个字符不一样就返回false
return false;
i++;
}
return true;
}
}
return false;
}
当且仅当
length()
为0
时返回true
。
public static void main(String[] args) {
String s="abc";
System.out.println(s.isEmpty());//false
String s1="";
System.out.println(s1.isEmpty());//true
}
返回指定字符在此字符串中最后一次出现处的索引。
public static void main(String[] args) {
String s1="abccb";
System.out.println(s1.lastIndexOf('b'));//4
}
返回一个新的字符串,它是通过用
newChar
替换此字符串中出现的所有oldChar
得到的。
public static void main(String[] args) {
String s1="abcda";
System.out.println(s1.replace('a', 'e'));//ebcde
}
//源码分析
public String replace(char oldChar, char newChar) {
//如果新字符不同于旧字符
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value; /* avoid getfield opcode */
//如果没有一个字符和oldchar一样,那就不用替换了
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];
//等于oldChar则替换
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
return new String(buf, true);
}
}
return this;
}
根据给定正则表达式的匹配拆分此字符串。
public static void main(String[] args) {
String s="a,b,c,d,e";
String[] split = s.split(",");
for (String s1 : split) {
System.out.println(s1);
}
}
返回一个新的字符串,它是此字符串的一个子字符串。
public static void main(String[] args) {
String s1="abcde";
System.out.println(s1.substring(1, 2));//b
}
//源码分析
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 String(char value[], int offset, int count) {
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
if (count <= 0) {
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
if (offset <= value.length) {
this.value = "".value;
return;
}
}
// Note: offset or count might be near -1>>>1.
if (offset > value.length - count) {
throw new StringIndexOutOfBoundsException(offset + count);
}
//将第一参数的数组中的从offset-offset+count复制到当前创建的String对象的value(字符数组)中
this.value = Arrays.copyOfRange(value, offset, offset+count);
}
将此字符串转换为一个新的字符数组。
public static void main(String[] args) {
String s1="abcde";
char[] arr = s1.toCharArray();
for (char c : arr) {
System.out.println(c);//a b c d e
}
}
//源码分析
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);//将当前对象的value复制到另一个字符数组并返回
return result;
}
使用默认语言环境的规则将此
String
中的所有字符都转换为小写。
public static void main(String[] args) {
String str="ABC";
System.out.println(str.toLowerCase());//abc
}
返回字符串的副本,忽略前导空白和尾部空白。
public static void main(String[] args) {
String str=" ab c d ";
String trim = str.trim();
System.out.println(trim);//ab c d
System.out.println(trim.length());//6
}
正则表达式的简单使用
private static void method10() {
String str=" ";
String regex="\\W";//非单词字符
System.out.println(str.matches(regex));//true
}
private static void method09() {
String str="_";
String regex="\\w";//单词字符:[a-zA-Z_0-9]
System.out.println(str.matches(regex));//true
}
private static void method08() {
String str="a";
String regex="\\S";//非空白字符
System.out.println(str.matches(regex));//true
}
//空格、制表符、换行符、回车、换页垂直制表符和换行符称为 “空白来字符
private static void method07() {
String str=" ";
String regex="\\s";//空白字符
System.out.println(str.matches(regex));//true
}
private static void method06() {
String str="a";
String regex="\\D";//非数字[^0-9]
System.out.println(str.matches(regex));//true
}
private static void method05() {
String str="1";
String regex="\\d";//数字:[0-9]
System.out.println(str.matches(regex));//true
}
private static void method04() {
String str="d";
String regex=".";//任何字符(与行结束符可能匹配也可能不匹配)
System.out.println(str.matches(regex));//true
}
private static void method03() {
String str="a";
String regex="[a-zA-Z]";//a 到 z 或 A 到 Z,两头的字母包括在内(范围)
System.out.println(str.matches(regex));//true
}
private static void method02(String d, String s) {
String str = d;
String regex = s;//任何字符,除了 a、b 或 c(否定)
System.out.println(str.matches(regex));
}
private static void method01() {
method02("a", "[abc]");
}
2.StringBuffer
一个带有缓冲区的可变字符串,实现了Charsequence接口,线程安全(内部方法加锁)。
/**
* A cache of the last value returned by toString. Cleared
* whenever the StringBuffer is modified.
*/
private transient char[] toStringCache;//缓冲区,每次修改StringBuffer都会清理缓存。
-------------------------------------------------------
//构造
public StringBuffer(int capacity) {
super(capacity);//调用父类有参构造
}
//父类构造
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
往StringBuffer追加
StringBuffer sb=new StringBuffer("abc");
StringBuffer def = sb.append("def");
System.out.println(sb);//abcdef
System.out.println(def);//abcdef
System.out.println(sb==def);//true
//源码分析
@Override
public synchronized/*线程安全*/ StringBuffer append(String str) {
toStringCache = null;//缓存区清空
super.append(str);//调用父类append
return this;
}
//父类append
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);//count是目前缓冲区内已有字符串的长度
str.getChars(0, len, value, count);//将str加到value中
count += len;//已有的字符串长度改变
return this;
}
/**
* The count is the number of characters used.
*/
int count;
//父类ensureCapacityInternal
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {//如果已有字符串长度+即将添加字符串长度大于字符数组value长度
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));//value指向新数组
}
}
//父类newCapacity
private int newCapacity(int minCapacity) {
// overflow-conscious code
int newCapacity = (value.length << 1) + 2;//<<左移运算符,相当于乘以2
if (newCapacity - minCapacity < 0) {//如果新的容量还是小于minCapacity,那就直接把minCapacity赋给它
newCapacity = minCapacity;
}
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)//返回新容量
? hugeCapacity(minCapacity)
: newCapacity;
}
返回当前容量
System.out.println(sb.capacity());//19 返回当前容量。
移除此序列的子字符串中的字符。
sb.delete(1, 2);
System.out.println(sb);//acdef
//源码分析
@Override
public synchronized StringBuffer delete(int start, int end) {
toStringCache = null;//缓存清空
super.delete(start, end);//调用父类delete
return this;
}
//父类delete
public AbstractStringBuilder delete(int start, int end) {
if (start < 0)
throw new StringIndexOutOfBoundsException(start);
if (end > count)
end = count;
if (start > end)
throw new StringIndexOutOfBoundsException();
int len = end - start;
if (len > 0) {
System.arraycopy(value, start+len, value, start, count-end);
count -= len;
}
return this;
}
前面很多地方的底层源码都涉及到了System.arrayCopy()这个方法,这里就来使用一下。
char[] arr1={'a','b','c','d','e'};
char[] arr2=new char[10];
/*
第一个参数是源数组,第二个参数是从源数组的哪个下标开始复制;第三个参数是目标数组,第四个参数是复制到目标数组的起始位置,第五个参数则是复制长度。
*/
System.arraycopy(arr1, 0, arr2, 1, 5);
for (char c : arr2) {
System.out.println(c);
}
将字符串插入此字符序列中。
StringBuffer sb=new StringBuffer("abc");
sb.insert(1, "de");
System.out.println(sb);//adebc
//源码分析
@Override
public synchronized StringBuffer insert(int offset, String str) {
toStringCache = null;//缓存区清空
super.insert(offset, str);
return this;
}
使用给定
String
中的字符替换此序列的子字符串中的字符。
StringBuffer sb=new StringBuffer("abc");
sb.replace(1, 2, "de");
System.out.println(sb);//adec
将此字符序列用其反转形式取代。
StringBuffer sb=new StringBuffer("abc");
sb.reverse();
System.out.println(sb);//cba
toStringCache,缓冲区,修改StringBuffer会被清空,只有在打印输出的时候有用
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
StringBuilder和StringBuffer差不多,区别在于:StringBuilder没有缓冲区,并且StringBuilder是线程不安全的。
为什么String是不可变的,而StringBuilder和StringBuffer是可变的?
//String类的concat方法
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字符串,而不是返回this
}
//StringBuilder和StringBuffer共同父类AbstractStringBuilder的append方法
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
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);
}
//只是将追加的字符串的value字符数组拷贝到StringBuilder或者StringBuffer的value字符数组中,并没有创建新的StringBuilder或者StringBuffer
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}