Java String 类(一文讲透)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观

在 Java 开发中,String 类 是一个高频使用且功能强大的核心类。无论是处理用户输入、解析配置文件,还是构建网络请求,开发者几乎每天都会与字符串打交道。对于编程初学者而言,理解 String 类 的特性与方法是掌握 Java 的关键一步;而对中级开发者来说,深入其底层实现原理和优化技巧,能显著提升代码质量与性能。本文将以通俗易懂的方式,结合实际案例,系统讲解 String 类 的核心知识点,并提供实用的编码建议。


String 类的基本特性

1.1 创建与初始化

String 类 是 Java 中唯一一个可以直接用字面量(literal)创建的对象。例如:

String greeting = "Hello, World!";  

这里 "Hello, World!" 是字符串字面量,Java 会自动将其包装为 String 对象。此外,也可以通过 new 关键字显式创建:

String message = new String("Java String 类");  

虽然两种方式都能创建对象,但它们的内存管理机制不同,这将在后续章节详细分析。

1.2 不可变性(Immutability)

String 类 的核心特性之一是不可变性。这意味着一旦创建了字符串对象,其内容就无法被修改。例如:

String s = "Hello";  
s += " World"; // 实际上会生成新对象  

上述代码中,s 最终指向的是一个全新的 String 对象,原始 "Hello" 对象仍然存在于内存中。这种设计保证了字符串的安全性和线程安全性,但也要求开发者注意内存管理。

比喻说明
可以将 String 类 比作写在石板上的文字。如果需要修改内容,只能雕刻新的石板,而旧石板依然保留,无法擦除或覆盖。


String 类的内存管理

2.1 字符串常量池(String Pool)

Java 虚拟机(JVM)维护了一个特殊的内存区域——字符串常量池。当使用字面量创建字符串时,JVM 首先检查池中是否存在相同值的字符串。若存在,则直接返回该对象的引用;若不存在,则新建对象并存入池中。例如:

String s1 = "Java";  
String s2 = "Java";  
System.out.println(s1 == s2); // 输出 true  

此时 s1s2 指向同一个对象。但若通过 new 创建:

String s3 = new String("Java");  
System.out.println(s1 == s3); // 输出 false  

因为 new 的对象直接分配在堆内存,不会进入常量池。

2.2 内存优化的实践

由于字符串不可变,JVM 可以通过常量池机制避免重复存储相同值的字符串。这对于减少内存占用和提升性能至关重要。例如:

// 低效写法:每次拼接生成新对象  
String result = "";  
for (int i = 0; i < 1000; i++) {  
    result += "Item " + i;  
}  

上述代码会创建 1000 个中间对象,导致性能下降。更优的解决方案是使用 StringBuilder

StringBuilder sb = new StringBuilder();  
for (int i = 0; i < 1000; i++) {  
    sb.append("Item ").append(i);  
}  
String result = sb.toString();  

StringBuilder 的可变性使其能高效修改内容,最终生成一个 String 对象。


String 类的常见方法与操作

3.1 字符串拼接

拼接字符串是日常开发中的高频操作。除了使用 + 运算符,还可以通过 concat() 方法:

String a = "Java";  
String b = " String 类";  
String c = a.concat(b); // 等同于 a + b  

但需要注意,+ 运算符在编译时会被自动转换为 StringBuilderappend 操作,因此在简单场景下两者性能差异不大。

3.2 字符串比较

比较字符串内容时,必须使用 equals() 方法,而非 == 运算符。例如:

String str1 = new String("Test");  
String str2 = "Test";  
System.out.println(str1 == str2); // false(引用不同)  
System.out.println(str1.equals(str2)); // true(内容相同)  

== 比较的是对象引用,而 equals() 比较的是内容。

3.3 子字符串与搜索

String 类 提供了丰富的搜索方法,例如:

String text = "Java String 类";  
int index = text.indexOf("String"); // 返回 5  
String substring = text.substring(5, 11); // 提取 "String"  

此外,contains()startsWith()endsWith() 等方法也能简化条件判断。

3.4 转换与格式化

将字符串转换为其他类型时,常用以下方法:

int number = Integer.parseInt("123"); // 转换为整数  
double price = Double.parseDouble("99.99"); // 转换为浮点数  

格式化字符串则可通过 String.format() 实现:

String formatted = String.format("总价:%.2f 元", 99.99); // 输出 "总价:99.99 元"  

String 类的不可变性与内存优化

4.1 不可变性的实现原理

String 类 的不可变性通过以下设计实现:

  1. 字符数组 char[] value 被声明为 final,确保其引用不可变;
  2. 所有修改字符串的操作(如 substring())均返回新对象。

例如:

String s = "Hello World";  
String sub = s.substring(6); // "World"  

此时 subvalue 数组与 svalue 共享原始内存区域,但通过索引和长度控制可见范围。

4.2 内存泄漏的潜在风险

由于不可变性,旧对象可能长期占用内存。例如:

String largeStr = new String(new char[10000]); // 创建大字符串  
String smallStr = largeStr.substring(0, 10); // 共享原始数组  

在旧版本 Java(如 Java 7 及之前),smallStr 仍持有整个原始数组的引用,导致内存浪费。而 Java 9 对 substring() 进行了优化,通过 byte[] 替代 char[] 并避免共享,解决了这一问题。


String 类与其他数据类型的交互

5.1 转换为字符数组

通过 toCharArray() 可将字符串转换为可修改的字符数组:

char[] chars = "Java String 类".toCharArray();  
chars[0] = 'J'; // 合法操作  

但需注意,原始 String 对象不受此影响。

5.2 分割与连接

split() 方法可根据正则表达式分割字符串:

String[] parts = "apple,banana,cherry".split(","); // ["apple", "banana", "cherry"]  

join() 方法可以高效连接字符串数组:

String combined = String.join("-", parts); // "apple-banana-cherry"  

常见问题与最佳实践

6.1 String vs StringBuilder/ StringBuffer

  • String:适合少量、静态的字符串操作。
  • StringBuilder:线程不安全,但性能更高,适用于单线程环境。
  • StringBuffer:线程安全,但因同步开销较大,仅在多线程时使用。

6.2 避免不必要的对象创建

频繁拼接字符串时,应优先使用 StringBuilder

// 低效写法  
String result = "";  
for (int i = 0; i < 1000; i++) {  
    result += i; // 每次循环生成新对象  
}  

// 高效写法  
StringBuilder sb = new StringBuilder();  
for (int i = 0; i < 1000; i++) {  
    sb.append(i); // 直接修改内部缓冲区  
}  

6.3 空字符串与 null 的处理

判断字符串是否为空时,需区分 null 和空字符串 ""

if (str == null || str.isEmpty()) {  
    // 处理空值或空字符串  
}  

结论

String 类 是 Java 开发中不可或缺的基础工具,其不可变性、内存池机制和丰富的 API 设计,既保证了安全性,又提供了灵活的操作方式。掌握 String 类 的核心原理与最佳实践,不仅能提升代码的可读性和性能,还能为开发者深入理解 Java 内存管理打下坚实基础。建议读者通过实际项目不断练习,例如尝试用 StringBuilder 优化日志拼接或数据处理流程,以巩固对 String 类 的理解。

通过本文的学习,希望读者能对 Java String 类 的功能与优化技巧有全面的认知,并在实践中灵活应用这些知识,写出更高效、更健壮的代码。

最新发布