我们为什么需要String、StringBuilder与StringBuffer?
String,字符串类型,是一种引用数据类型。在Java编程过程中,我们广泛应用String类来创建和操作字符串。
由于String类是不可变字符串,每次变更时都会产生新的对象。当变更此时较多时,就有产生对象太多,占用空间大,效率低下等问题。这时候,就需要Java提供的StringBuilder与StringBuffer这两个可变字符串类型。
String类
String类又称作不可变字符序列,通过查看String类的定义,可以看到这条代码:
private final char value[];
String类是通过数组实现的,并且是final修饰的,所以String是不可变字符串,如果String定义后,内容改变,其实是新建了一个String对象,地址也会跟着改变。来个实例:
public class Test {
public static void main(String[] args) {
String str = "11aa";
System.out.println(Integer.toHexString(str.hashCode()));
str = "22bb";
System.out.println(Integer.toHexString(str.hashCode()));
}
}
--------------------------------------------
输出结果:
170a40
178280
String类位于java.lang包中,java是默认导入了java.lang包下所有类的,所以不需要单独额外引用。
内容相同的String对象,其实是同一个对象,除了用new关键字创建外。而且,在定义时,如果有拼接,编译器会先做拼接再赋值。来个实例:
public class Test {
public static void main(String[] args) {
String str1 = "abc";
String str2 = "abc";
String str3 = "ab"+"c";
String str4 = new String("abc");
System.out.println(str1 == str2); // 相同内容的String对象地址相等,证明是同一个对象
System.out.println(str1 == str3); // 有拼接是,会先拼接,此时依旧是同一对象
System.out.println(str1 == str4); // new关键字一定会新创建对象的,所以内容相同也不是同一对象
}
}
------------------------------------------------
输出结果:
true
true
false
所以再进行String字符串比较时,我们需要使用equals方法。实例:
public class Test {
public static void main(String[] args) {
String str1 = "abc";
String str2 = "ab";
String str3 = "c";
String str4 = str2 + str3;
System.out.println(str1);
System.out.println(str4);
System.out.println(str1 == str4);
System.out.println(str1.equals(str4));
}
}
------------------------------------------
输出结果:
abc
abc
false
true
String类的常用方法:
方法 | 说明 |
---|---|
char charAt(int index) | 返回指定索引处的 char 值。 |
boolean equals(Object anObject) | 将此字符串与指定的对象比较。 |
boolean equalsIgnoreCase(String anotherString) | 将此 String 与另一个 String 比较,不考虑大小写。 |
int indexOf(int ch) | 返回指定字符在此字符串中第一次出现处的索引。 |
int lastIndexOf(int ch) | 返回指定字符在此字符串中最后一次出现处的索引。 |
int length() | 返回此字符串的长度。 |
String replace(char oldChar, char newChar) | 返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。 |
boolean startsWith(String prefix) | 测试此字符串是否以指定的前缀开始。 |
boolean endsWith(String suffix) | 测试此字符串是否以指定的后缀结束。 |
String substring(int beginIndex, int endIndex) | 返回一个新字符串,它是此字符串的一个子字符串。 |
String toLowerCase() | 使用默认语言环境的规则将此 String 中的所有字符都转换为小写。 |
String toUpperCase() | 使用默认语言环境的规则将此 String 中的所有字符都转换为大写。 |
String trim() | 返回字符串的副本,忽略前导空白和尾部空白。 |
StringBuilder与StringBuffer
和 String 类不同,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
StringBuilder 类和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。
由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。
来个使用实例:
public class Test {
public static void main(String[] args) {
StringBuilder sBuilder = new StringBuilder("a");
sBuilder.append("b"); // 追加字符
sBuilder.append("d");
sBuilder.insert(2, "c"); // 插入到指定序列
sBuilder.reverse(); // 字符顺序反转
System.out.println(sBuilder);
}
}
StringBuffer 和 StringBuilder拥有许多与Sting类相同或相似方法,我在这里就只列出特别的方法:
方法 | 说明 |
---|---|
public StringBuffer append(String s) | 将指定的字符串追加到此字符序列。 |
public StringBuffer reverse() | 将此字符序列用其反转形式取代。 |
public delete(int start, int end) | 移除此序列的子字符串中的字符。 |
public insert(int offset, int i) | 将 int 参数的字符串表示形式插入此序列中。 |
replace(int start, int end, String str) | 使用给定 String 中的字符替换此序列的子字符串中的字符。 |
效率对比测试
测试代码:
public class Test {
public static void main(String[] args) {
//String
String str = "";
long num1 = Runtime.getRuntime().freeMemory();
long time1 = System.currentTimeMillis();
for(int i = 0;i < 10000;i++) {
str = str + i;
}
long num2 = Runtime.getRuntime().freeMemory();
long time2 = System.currentTimeMillis();
System.out.println("String占用内存:" + ( num1 - num2) );
System.out.println("String占用时间:" + ( time2 - time1) );
//StringBuilder
StringBuilder sBuilder = new StringBuilder("");
long num3 = Runtime.getRuntime().freeMemory();
long time3 = System.currentTimeMillis();
for(int i = 0;i < 10000;i++) {
sBuilder.append(i);
}
long num4 = Runtime.getRuntime().freeMemory();
long time4 = System.currentTimeMillis();
System.out.println("StringBuilder占用内存:" + ( num3 - num4) );
System.out.println("StringBuilder占用时间:" + ( time4 - time3) );
}
}
-------------------------------------------
输出结果:
String占用内存:46640
String占用时间:127
StringBuilder占用内存:0
StringBuilder占用时间:1
StringBuilder对比String类型,在多次修改时,占用内存更少,效率更高。