String
String 对象是不可变的。String 类中每一个看起来会修改 String 值的方法,实际上都是创建了一个全新的 String 对象,以包含修改后的字符串内容。而最初的 String 对象则丝毫未动。
String 真正不可变原因:
- 保存字符串的数组被
final修饰且为私有的,并且String类没有提供/暴露修改这个字符串的方法。 String类被final修饰导致其不能被继承,进而避免了子类破坏String不可变。
// strings/Immutable.java
public class Immutable {
public static String upcase(String s) {
return s.toUpperCase();
}
public static void main(String[] args) {
String q = "howdy";
System.out.println(q); // howdy
String qq = upcase(q);
System.out.println(qq); // HOWDY
System.out.println(q); // howdy
}
}
/* Output:
howdy
HOWDY
howdy
*/
当把 q 传递给 upcase() 方法时,实际传递的是引用的一个拷贝。其实,每当把 String 对象作为方法的参数时,都会复制一份引用,而该引用所指向的对象其实一直待在单一的物理位置上,从未动过。
回到 upcase() 的定义,传入其中的引用有了名字 s,只有 upcase() 运行的时候,局部引用 s 才存在。一旦 upcase() 运行结束,s 就消失了。当然了,upcase() 的返回值,其实是最终结果的引用。这足以说明,upcase() 返回的引用已经指向了一个新的对象,而 q 仍然在原来的位置。
StringBuilder与StringBuffer
StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串,不过没有使用 final 和 private 关键字修饰, AbstractStringBuilder 类还提供了很多修改字符串的方法比如 append 方法。
StringBuilder 提供了丰富而全面的方法,包括 insert()、replace()、substring(),甚至还有reverse(),但是最常用的还是 append() 和 toString()。
StringBuilder 是 Java SE5 引入的,在这之前用的是 StringBuffer。后者是线程安全的(参见并发编程),因此开销也会大些。使用 StringBuilder 进行字符串操作更快一点。
+与StringBuilder
Java 语言本身并不支持运算符重载,“+”和“+=”是专门为 String 类重载过的运算符,也是 Java 中仅有的两个重载过的运算符。
// strings/Concatenation.java
public class Concatenation {
public static void main(String[] args) {
String mango = "mango";
String s = "abc" + mango + "def" + 47;
System.out.println(s);
}
}
/* Output:
abcmangodef47
*/
在这里,编译器创建了一个 StringBuilder 对象,用于构建最终的 String,并对每个字符串调用了一次 append() 方法,最后调用 toString() 生成结果。
在循环内使用“+”进行字符串的拼接的话,存在比较明显的缺陷:编译器不会创建单个 StringBuilder 以复用,会导致创建过多的 StringBuilder 对象。
对于三者使用的总结:
- 操作少量的数据: 适用
String - 单线程操作字符串缓冲区下操作大量数据: 适用
StringBuilder - 多线程操作字符串缓冲区下操作大量数据: 适用
StringBuffer
字符串操作
以下是 String 对象具备的一些基本方法。重载的方法归纳在同一行中:
| 方法 | 参数,重载版本 | 作用 |
|---|---|---|
| 构造方法 | 默认版本,String,StringBuilder,StringBuffer,char数组,byte数组 | 创建String对象 |
length() | String中字符的个数 | |
charAt() | int索引 | 获取String中索引位置上的char |
getChars(),getBytes() | 待复制部分的开始和结束索引,复制的目标数组,目标数组的开始索引 | 复制char或byte到一个目标数组中 |
toCharArray() | 生成一个char[],包含String中的所有字符 | |
equals(),equalsIgnoreCase() | 与之进行比较的String | 比较两个String的内容是否相同。如果相同,结果为true |
compareTo(),compareToIgnoreCase() | 与之进行比较的String | 按词典顺序比较String的内容,比较结果为负数、零或正数。注意,大小写不等价 |
contains() | 要搜索的CharSequence | 如果该String对象包含参数的内容,则返回true |
contentEquals() | 与之进行比较的CharSequence或StringBuffer | 如果该String对象与参数的内容完全一致,则返回true |
isEmpty() | 返回boolean结果,以表明String对象的长度是否为0 | |
regionMatches() | 该String的索引偏移量,另一个String及其索引偏移量,要比较的长度。重载版本增加了“忽略大小写”功能 | 返回boolean结果,以表明所比较区域是否相等 |
startsWith() | 可能的起始String。重载版本在参数中增加了偏移量 | 返回boolean结果,以表明该String是否以传入参数开始 |
endsWith() | 该String可能的后缀String | 返回boolean结果,以表明此参数是否是该字符串的后缀 |
indexOf(),lastIndexOf() | 重载版本包括:char,char与起始索引,String,String与起始索引 | 如果该String并不包含此参数,就返回-1;否则返回此参数在String中的起始索引。lastIndexOf()是从后往前搜索 |
matches() | 一个正则表达式 | 返回boolean结果,以表明该String和给出的正则表达式是否匹配 |
split() | 一个正则表达式。可选参数为需要拆分的最大数量 | 按照正则表达式拆分String,返回一个结果数组 |
join()(Java8引入的) | 分隔符,待拼字符序列。用分隔符将字符序列拼接成一个新的String | 用分隔符拼接字符片段,产生一个新的String |
substring()(即subSequence()) | 重载版本:起始索引;起始索引+终止索引 | 返回一个新的String对象,以包含参数指定的子串 |
concat() | 要连接的String | 返回一个新的String对象,内容为原始String连接上参数String |
replace() | 要替换的字符,用来进行替换的新字符。也可以用一个CharSequence替换另一个CharSequence | 返回替换字符后的新String对象。如果没有替换发生,则返回原始的String对象 |
replaceFirst() | 要替换的正则表达式,用来进行替换的String | 返回替换首个目标字符串后的String对象 |
replaceAll() | 要替换的正则表达式,用来进行替换的String | 返回替换所有目标字符串后的String对象 |
toLowerCase(),toUpperCase() | 将字符的大小写改变后,返回一个新的String对象。如果没有任何改变,则返回原始的String对象 | |
trim() | 将String两端的空白符删除后,返回一个新的String对象。如果没有任何改变,则返回原始的String对象 | |
valueOf()(static) | 重载版本:Object;char[];char[],偏移量,与字符个数;boolean;char;int;long;float;double | 返回一个表示参数内容的String |
intern() | 为每个唯一的字符序列生成一个且仅生成一个String引用 | |
format() | 要格式化的字符串,要替换到格式化字符串的参数 | 返回格式化结果String |
参考资料
深入探究可参考如下内容
文章探讨了Java中String对象的不可变性,解释了其背后的原因,包括final修饰的字符数组和方法不提供修改功能。同时提到了在需要频繁修改字符串时,可以使用StringBuilder和StringBuffer,后者在多线程环境下更安全。另外,文章还列举并对比了String类的一些常用方法。
319

被折叠的 条评论
为什么被折叠?



