面试题答案
一键面试- 内存管理方面
- String:
String
类是不可变的,一旦创建,其值就不能被修改。当对String
进行修改操作(如拼接、替换等)时,实际上会创建一个新的String
对象。每次操作都会在堆内存中开辟新的空间来存储新的字符串,原有的字符串对象并不会被修改,只是不再被引用,等待垃圾回收器回收。例如:
- String:
String str = "Hello";
str = str + " World";
在上述代码中,首先创建了 Hello
字符串对象,然后在执行 str = str + " World"
时,会在堆内存中创建一个新的包含 Hello World
的字符串对象,并将 str
指向这个新对象,而原来的 Hello
对象在没有其他引用时会等待垃圾回收。这种频繁创建新对象的操作会增加内存开销。
- StringBuilder:
StringBuilder
类是可变的。它内部维护了一个字符数组来存储字符串内容,初始容量默认为16。当进行字符串修改操作(如append
等方法)时,如果当前字符数组的容量足够,就直接在数组中进行修改;如果容量不足,会自动扩容。扩容时会创建一个新的更大的字符数组,并将原数组内容复制到新数组中。例如:
StringBuilder sb = new StringBuilder("Hello");
sb.append(" World");
在上述代码中,sb
指向的 StringBuilder
对象内部的字符数组会根据需要进行调整,不会像 String
那样每次操作都创建全新的对象,从而减少了内存的频繁分配和垃圾回收,提高了内存使用效率。
2. 操作原理方面
- String:
- 对
String
进行修改操作时,会调用String
类的各种方法(如concat
方法等),这些方法都会返回一个新的String
对象。例如concat
方法的实现大致如下:
- 对
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
char buf[] = new char[count + otherLen];
getChars(0, count, buf, 0);
str.getChars(0, otherLen, buf, count);
return new String(0, count + otherLen, buf);
}
可以看到,每次 concat
操作都会创建新的字符数组和新的 String
对象。
- StringBuilder:
StringBuilder
的修改方法(如append
方法)直接在内部字符数组上进行操作。例如append
方法实现大致如下:
public StringBuilder 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;
}
它首先检查容量是否足够,不足则扩容,然后直接将新的字符串内容追加到内部字符数组中,不会创建大量中间的字符串对象,所以在频繁修改操作时效率更高。
综上所述,在Java中,由于 StringBuilder
在内存管理上避免了频繁创建新对象,以及在操作原理上直接在内部数组操作,所以比直接使用 String
进行频繁字符串修改操作更高效。