抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

前言

Java 中的 String 类是面试中经常被问到的话题,因为它不仅是最常用的类之一,而且有许多特性和优化值得深入理解。以下是一些常见的 String 类面试问题及解答。

String 的不可变性

问题:为什么 String 类在 Java 中是不可变的?

String 类在 Java 中被设计为不可变的,主要有以下几个原因:

  1. 安全性String 经常用于存储敏感信息如用户名、密码等,不可变性确保这些值不会被意外修改。
  2. 线程安全:不可变对象天然是线程安全的,可以被多个线程共享使用。
  3. 字符串池优化:Java 维护一个字符串常量池,可以重用 String 对象,节省内存。
  4. 哈希缓存String 对象被广泛用作 HashMap 的键,不可变性确保哈希值不变,提高性能。

StringStringBuilderStringBuffer

问题:StringStringBuilderStringBuffer 的区别是什么?

  • **String**:不可变,线程安全,每次操作都会创建新对象。
  • **StringBuilder**:可变,非线程安全,适合单线程环境下的字符串操作,性能最好。
  • **StringBuffer**:可变,线程安全(方法使用 synchronized 修饰),适合多线程环境下的字符串操作。

何时使用哪个?

  • 如果字符串不会改变,使用 String
  • 如果字符串会频繁修改且在单线程环境,使用 StringBuilder
  • 如果字符串会频繁修改且在多线程环境,使用 StringBuffer

字符串常量池

问题:解释一下Java中的字符串常量池

字符串常量池是 JVM 中的一个特殊内存区域,用于存储字符串字面量。当创建字符串字面量时,JVM 首先检查池中是否已有相同的字符串:

  • 如果存在,则返回对池中对象的引用。
  • 如果不存在,则在池中创建新的字符串对象并返回其引用。
1
2
3
4
5
6
7
String s1 = "hello";  // 创建一个字符串,存储在常量池中
String s2 = "hello"; // 复用常量池中的对象
String s3 = new String("hello"); // 在堆内存中创建新对象

System.out.println(s1 == s2); // 输出:true
System.out.println(s1 == s3); // 输出:false
System.out.println(s1.equals(s3)); // 输出:true

问题:new String("abc") 创建了几个对象?

  • 若常量池中无 "abc":1 个放入常量池,1 个堆对象 → 共 2 个。

  • 若常量池中已有 "abc":仅创建 1 个堆对象。

intern() 方法

问题:String 类的 intern() 方法有什么作用?

intern() 方法用于将字符串对象添加到字符串常量池中:

  • 如果池中已存在该字符串,则返回池中的字符串对象。
  • 如果池中不存在,则添加到池中并返回其引用。
1
2
3
4
5
6
String s1 = new String("hello");  // 在堆内存中创建对象
String s2 = s1.intern(); // 在常量池中查找或添加"hello"
String s3 = "hello"; // 直接从常量池获取

System.out.println(s1 == s2); // 输出:false
System.out.println(s2 == s3); // 输出:true

equals()== 的区别

问题:在比较 String 时,equals()== 有什么区别?

  • == 运算符:比较引用是否指向同一对象(比较内存地址)
  • equals() 方法:比较字符串的内容是否相同(String 类重写了 Objectequals 方法)
1
2
3
4
5
6
7
String s1 = "Hello";
String s2 = "Hello";
String s3 = new String("Hello");

System.out.println(s1 == s2); // 输出:true,指向同一常量池对象
System.out.println(s1 == s3); // 输出:false,引用不同对象
System.out.println(s1.equals(s3)); // 输出:true,内容相同

性能优化相关问题

问题:在循环中进行大量字符串拼接操作应该使用什么方法?

在循环中使用 + 进行字符串拼接会创建大量临时对象,影响性能。应该使用 StringBuilder

1
2
3
4
5
6
7
8
9
10
11
12
// 低效方式
String result = "";
for (int i = 0; i < 10000; i++) {
result += i; // 每次循环创建新String对象
}

// 高效方式
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.append(i); // 在同一对象上操作
}
String result = sb.toString();

问题: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
2
String[] names = {"张三", "李四", "王五"};
Arrays.sort(names); // 按字典顺序排序

字符串格式化

问题:如何格式化字符串?

使用 String.format() 方法或 printf() 方法:

1
2
String formatted = String.format("姓名: %s, 年龄: %d", "张三", 25);
System.out.printf("价格: %.2f 元\n", 99.9);

String 与其他数据类型的转换

问题:如何在 String 和其他数据类型之间进行转换?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 基本类型转String
String s1 = String.valueOf(123);
String s2 = Integer.toString(123);
String s3 = "" + 123;

// String转基本类型
int i = Integer.parseInt("123");
double d = Double.parseDouble("123.45");
boolean b = Boolean.parseBoolean("true");

// String与字符数组转换
char[] chars = "Hello".toCharArray();
String str = new String(chars);

// String与字节数组转换
byte[] bytes = "Hello".getBytes();
String str2 = new String(bytes);

后记

理解 String 类及其相关类的特性和优化对于 Java 程序员至关重要。面试中,不仅要了解基本用法,还要理解底层原理,如不可变性、字符串常量池、性能优化等方面的知识。

评论