前言
Java 中的 String
类是面试中经常被问到的话题,因为它不仅是最常用的类之一,而且有许多特性和优化值得深入理解。以下是一些常见的 String
类面试问题及解答。
String
的不可变性
问题:为什么 String
类在 Java 中是不可变的?
String
类在 Java 中被设计为不可变的,主要有以下几个原因:
- 安全性:
String
经常用于存储敏感信息如用户名、密码等,不可变性确保这些值不会被意外修改。 - 线程安全:不可变对象天然是线程安全的,可以被多个线程共享使用。
- 字符串池优化:Java 维护一个字符串常量池,可以重用
String
对象,节省内存。 - 哈希缓存:
String
对象被广泛用作HashMap
的键,不可变性确保哈希值不变,提高性能。
String
,StringBuilder
和 StringBuffer
问题:String
,StringBuilder
和 StringBuffer
的区别是什么?
- **
String
**:不可变,线程安全,每次操作都会创建新对象。 - **
StringBuilder
**:可变,非线程安全,适合单线程环境下的字符串操作,性能最好。 - **
StringBuffer
**:可变,线程安全(方法使用synchronized
修饰),适合多线程环境下的字符串操作。
何时使用哪个?
- 如果字符串不会改变,使用
String
。 - 如果字符串会频繁修改且在单线程环境,使用
StringBuilder
。 - 如果字符串会频繁修改且在多线程环境,使用
StringBuffer
。
字符串常量池
问题:解释一下Java中的字符串常量池
字符串常量池是 JVM 中的一个特殊内存区域,用于存储字符串字面量。当创建字符串字面量时,JVM 首先检查池中是否已有相同的字符串:
- 如果存在,则返回对池中对象的引用。
- 如果不存在,则在池中创建新的字符串对象并返回其引用。
1 | String s1 = "hello"; // 创建一个字符串,存储在常量池中 |
问题:new String("abc")
创建了几个对象?
若常量池中无
"abc"
:1 个放入常量池,1 个堆对象 → 共 2 个。若常量池中已有
"abc"
:仅创建 1 个堆对象。
intern()
方法
问题:String
类的 intern()
方法有什么作用?
intern()
方法用于将字符串对象添加到字符串常量池中:
- 如果池中已存在该字符串,则返回池中的字符串对象。
- 如果池中不存在,则添加到池中并返回其引用。
1 | String s1 = new String("hello"); // 在堆内存中创建对象 |
equals()
和 ==
的区别
问题:在比较 String
时,equals()
和 ==
有什么区别?
==
运算符:比较引用是否指向同一对象(比较内存地址)equals()
方法:比较字符串的内容是否相同(String
类重写了Object
的equals
方法)
1 | String s1 = "Hello"; |
性能优化相关问题
问题:在循环中进行大量字符串拼接操作应该使用什么方法?
在循环中使用 +
进行字符串拼接会创建大量临时对象,影响性能。应该使用 StringBuilder
:
1 | // 低效方式 |
问题:String str = "a" + "b" + "c";
在内存中如何操作?
编译期优化:字面量拼接会被编译器直接合并为 "abc"
,只生成一个常量。
运行时拼接(如循环中使用 +
):每次拼接生成新 String
对象,效率低。应改用 StringBuilder
。
String
的内存占用
问题:String
对象在内存中占用多少空间?
在 Java 8 及之前版本中,String 内部使用 char[]
数组存储字符,每个字符占 2 字节。Java 9 之后改用 byte[]
数组加编码标记,可以根据字符内容采用 Latin-1(1字节/字符)或 UTF-16(2字节/字符)编码,以减少内存占用。
常见的 String
方法
问题:列举一些常用的 String
方法及其用途
length()
:返回字符串长度charAt(int index)
:返回指定索引处的字符substring(int beginIndex, int endIndex)
:返回子字符串equals(Object anObject)
:比较字符串内容equalsIgnoreCase(String anotherString)
:忽略大小写比较字符串startsWith(String prefix)/endsWith(String suffix)
:检查前缀/后缀indexOf(String str)
:查找子字符串第一次出现的位置replace(CharSequence target, CharSequence replacement)
:替换所有匹配的子字符串trim()
:去除前导和尾随空白split(String regex)
:按正则表达式分割字符串toLowerCase()/toUpperCase()
:转换为小写/大写
字符串比较和排序
问题:如何比较和排序字符串?
- **
compareTo(String anotherString)
**:按字典顺序比较两个字符串 - **
compareToIgnoreCase(String str)
**:忽略大小写按字典顺序比较
1 | String[] names = {"张三", "李四", "王五"}; |
字符串格式化
问题:如何格式化字符串?
使用 String.format()
方法或 printf()
方法:
1 | String formatted = String.format("姓名: %s, 年龄: %d", "张三", 25); |
String
与其他数据类型的转换
问题:如何在 String
和其他数据类型之间进行转换?
1 | // 基本类型转String |
后记
理解 String
类及其相关类的特性和优化对于 Java 程序员至关重要。面试中,不仅要了解基本用法,还要理解底层原理,如不可变性、字符串常量池、性能优化等方面的知识。