面试题答案
一键面试String
- 内存占用
String
是不可变类,每次进行字符串拼接操作,都会创建一个新的String
对象。例如,执行String s = "a" + "b" + "c";
,在编译期会优化为String s = "abc";
,但如果是运行时拼接,如String s1 = "a"; String s2 = "b"; String result = s1 + s2;
,就会创建新的String
对象。这会导致大量中间对象在内存中被创建,占用较多内存。- 由于
String
对象不可变,其内容存储在字符串常量池中(JDK 7及之前)或堆中(JDK 7之后)。常量池中的字符串对象在程序运行期间一直存在,不会轻易被回收,除非整个类被卸载。
- 内存回收机制
- 当不再有引用指向这些新创建的
String
对象时,它们会在垃圾回收(GC)过程中被回收。然而,由于常量池中的字符串可能被其他地方引用,即使拼接产生的中间字符串不再被使用,也可能无法及时回收。例如,如果有一个静态String
变量引用了某个拼接产生的字符串,那么这个字符串对象在整个应用程序生命周期内都不会被回收。
- 当不再有引用指向这些新创建的
StringBuffer
- 内存占用
StringBuffer
是可变类,它内部维护一个字符数组。在进行字符串拼接操作时,它会在原有数组的基础上进行扩展。例如,StringBuffer sb = new StringBuffer("a"); sb.append("b");
,它会在已有的字符数组上追加字符'b',而不是创建新的对象(除非数组容量不足需要扩容)。- 初始创建
StringBuffer
时,会分配一定大小的字符数组(默认16个字符的容量)。如果拼接的字符串长度超过当前容量,会进行扩容操作,新的容量通常是原容量的2倍加2。扩容会涉及到数组的复制,这会有一定的性能开销,但总体上相比String
频繁创建新对象,内存占用会少很多。
- 内存回收机制
StringBuffer
对象本身在不再被引用时,会被垃圾回收。由于其内部字符数组的内容是可变的,不会像String
那样在常量池中留下大量中间对象。只要StringBuffer
对象的引用被释放,相关内存就可以被回收。
StringBuilder
- 内存占用
StringBuilder
和StringBuffer
类似,也是可变类,同样通过维护一个字符数组来进行字符串操作。在进行字符串拼接时,也是在原有数组基础上进行扩展,原理与StringBuffer
基本相同。- 初始容量和扩容机制也和
StringBuffer
类似,默认容量为16,容量不足时会进行扩容。它在内存占用方面和StringBuffer
表现相近,相比String
进行大量字符串拼接时,内存占用显著减少。
- 内存回收机制
- 与
StringBuffer
一样,当StringBuilder
对象不再被引用时,会被垃圾回收。其内部字符数组随着对象的回收,相关内存也会被释放,不会像String
那样因常量池引用等问题导致内存难以回收。
- 与
总结来说,在大量字符串拼接操作中,String
会因频繁创建新对象而占用较多内存且回收相对复杂;StringBuffer
和StringBuilder
通过可变的字符数组减少了对象创建,内存占用相对较少,且回收较为直接,StringBuffer
由于线程安全机制会有一些性能开销,StringBuilder
则更适合单线程环境下的字符串拼接。