🎯 学习目标
- 理解String类的不可变性特征
- 掌握字符串的创建和初始化方法
- 学会使用String类的常用方法
- 了解字符串池(String Pool)的工作原理
- 掌握StringBuilder和StringBuffer的使用场景
- 学习字符串操作的最佳实践
📖 String字符串概述
String是Java中最常用的类之一,用于表示字符序列。String类具有不可变性(immutable)的特征,这意味着一旦创建了String对象,就不能修改其内容。
🔑 核心特性
- 不可变性:String对象一旦创建,内容不可修改
- 字符串池:JVM维护一个字符串常量池来优化内存使用
- 线程安全:由于不可变性,String天然线程安全
- 丰富的API:提供大量字符串操作方法
🛠️ 字符串创建方式
// 使用字面量创建(推荐)
String str1 = "Hello World";
String str2 = "Hello World"; // 指向同一个对象
// 验证是否为同一对象
System.out.println(str1 == str2); // true
// 使用构造方法创建
String str3 = new String("Hello World");
String str4 = new String("Hello World");
// 每次都创建新对象
System.out.println(str3 == str4); // false
System.out.println(str3.equals(str4)); // true
// 从字符数组创建
char[] chars = {'H', 'e', 'l', 'l', 'o'};
String str5 = new String(chars);
System.out.println(str5); // "Hello"
🔧 常用方法详解
📏 长度和判空
String text = "Hello World";
// 获取长度
int length = text.length(); // 11
// 判断是否为空
boolean isEmpty = text.isEmpty(); // false
boolean isBlank = text.isBlank(); // false (Java 11+)
// 安全的判空方法
if (text != null && !text.isEmpty()) {
System.out.println("字符串不为空");
}
🔍 查找和比较
String text = "Hello World";
// 查找字符或子字符串
int index1 = text.indexOf('o'); // 4
int index2 = text.lastIndexOf('o'); // 7
int index3 = text.indexOf("World"); // 6
// 字符串比较
boolean equals = text.equals("Hello World"); // true
boolean equalsIgnoreCase = text.equalsIgnoreCase("hello world"); // true
int compareResult = text.compareTo("Hello"); // 正数
// 包含判断
boolean contains = text.contains("World"); // true
boolean startsWith = text.startsWith("Hello"); // true
boolean endsWith = text.endsWith("World"); // true
✂️ 截取和替换
String text = "Hello World";
// 字符串截取
String sub1 = text.substring(6); // "World"
String sub2 = text.substring(0, 5); // "Hello"
// 字符串替换
String replaced1 = text.replace('o', 'a'); // "Hella Warld"
String replaced2 = text.replace("World", "Java"); // "Hello Java"
String replaced3 = text.replaceAll("[aeiou]", "*"); // "H*ll* W*rld"
// 去除空白字符
String trimmed = " Hello World ".trim(); // "Hello World"
String stripped = " Hello World ".strip(); // "Hello World" (Java 11+)
🔄 大小写转换
String text = "Hello World";
// 大小写转换
String upper = text.toUpperCase(); // "HELLO WORLD"
String lower = text.toLowerCase(); // "hello world"
// 首字母大写(自定义方法)
public static String capitalize(String str) {
if (str == null || str.isEmpty()) {
return str;
}
return str.substring(0, 1).toUpperCase() + str.substring(1).toLowerCase();
}
📝 分割和连接
// 字符串分割
String csv = "apple,banana,orange";
String[] fruits = csv.split(","); // ["apple", "banana", "orange"]
// 字符串连接
String joined = String.join(", ", fruits); // "apple, banana, orange"
// 使用StringBuilder进行高效连接
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" ");
sb.append("World");
String result = sb.toString(); // "Hello World"
🏊 字符串池详解
字符串池(String Pool)是JVM在堆内存中维护的一个特殊区域,用于存储字符串字面量,避免重复创建相同的字符串对象。
// 字面量会进入字符串池
String s1 = "Hello";
String s2 = "Hello";
System.out.println(s1 == s2); // true,指向同一个对象
// new创建的对象不在字符串池中
String s3 = new String("Hello");
System.out.println(s1 == s3); // false,不同对象
System.out.println(s1.equals(s3)); // true,内容相同
// intern()方法可以将字符串加入池中
String s4 = s3.intern();
System.out.println(s1 == s4); // true,现在指向池中的对象
⚡ StringBuilder vs StringBuffer
当需要频繁修改字符串时,应该使用StringBuilder或StringBuffer,而不是String。
| 特性 | String | StringBuilder | StringBuffer |
|---|---|---|---|
| 可变性 | 不可变 | 可变 | 可变 |
| 线程安全 | 安全 | 不安全 | 安全 |
| 性能 | 频繁修改时较慢 | 快 | 较快 |
| 使用场景 | 字符串不变 | 单线程频繁修改 | 多线程频繁修改 |
// 低效的字符串拼接(避免)
String result = "";
for (int i = 0; i < 1000; i++) {
result += "Hello "; // 每次都创建新对象
}
// 高效的字符串拼接(推荐)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append("Hello "); // 在原对象上修改
}
String result = sb.toString();
💻 在线IDE体验
点击下面的链接,在在线IDE中实践String字符串的各种操作:
🚀 打开在线IDE - String字符串实践
💡 在IDE中你可以运行示例代码,观察字符串的创建、操作和性能差异。
💡 最佳实践
✅ 推荐做法使用字面量创建字符串:优先使用"Hello"而不是new String("Hello")使用equals()比较内容:避免使用==比较字符串内容频繁修改时使用StringBuilder:避免字符串拼接的性能问题及时进行空值检查:防止NullPointerException使用String.format()格式化:提高代码可读性
❌ 避免的做法避免不必要的String对象创建:减少内存开销避免在循环中进行字符串拼接:使用StringBuilder代替避免使用==比较字符串内容:可能导致逻辑错误避免忽略大小写敏感性:根据需求选择合适的比较方法
// ✅ 好的做法
public class StringBestPractices {
// 安全的字符串比较
public static boolean safeEquals(String str1, String str2) {
return Objects.equals(str1, str2);
}
// 高效的字符串构建
public static String buildMessage(String[] parts) {
if (parts == null || parts.length == 0) {
return "";
}
StringBuilder sb = new StringBuilder();
for (String part : parts) {
if (part != null && !part.isEmpty()) {
sb.append(part).append(" ");
}
}
return sb.toString().trim();
}
// 格式化字符串
public static String formatUserInfo(String name, int age) {
return String.format("用户:%s,年龄:%d岁", name, age);
}
}
❓ 常见问题
Q: 为什么String是不可变的?
A: String的不可变性带来了多个好处:
- 线程安全:多个线程可以安全地访问同一个String对象
- 字符串池优化:相同内容的字符串可以共享同一个对象
- 哈希码缓存:String的hashCode()只需计算一次
- 安全性:防止字符串内容被意外修改
Q: 什么时候使用StringBuilder?
A: 在以下情况下应该使用StringBuilder:
- 需要频繁修改字符串内容
- 在循环中进行字符串拼接
- 构建大型字符串
- 单线程环境下的字符串操作
Q: String.intern()方法的作用是什么?
A: intern()方法的作用:
- 将字符串对象加入到字符串池中
- 如果池中已存在相同内容的字符串,返回池中的引用
- 如果池中不存在,将当前字符串加入池中并返回引用
- 可以节省内存,但要谨慎使用,避免内存泄漏
⚠️ 注意事项
空值处理:始终检查字符串是否为null,避免NullPointerException性能考虑:频繁的字符串操作应使用StringBuilder或StringBuffer内存使用:谨慎使用intern()方法,避免字符串池过大字符编码:处理文件或网络数据时注意字符编码问题正则表达式:使用split()和replaceAll()时注意正则表达式的特殊字符