Java 字符串是用于表示文本数据的对象,核心实现类为 java.lang.String,其最关键特性是不可变性——对象创建后,字符序列无法被修改,任何看似“修改”的操作都会生成新的 String 对象。
一、核心特性
-
不可变性(Immutability)
字符串创建后,其内部字符数组(char[],JDK 9+ 改为byte[])的内容和长度无法修改。例如str = str + "abc"并非修改原对象,而是创建新的字符串对象并指向新地址。- 优势:线程安全、可缓存(如字符串常量池)、哈希值固定(利于作为
HashMap键)。 - 注意:频繁修改字符串(如循环拼接)会产生大量临时对象,建议用
StringBuilder(非线程安全)或StringBuffer(线程安全)。
- 优势:线程安全、可缓存(如字符串常量池)、哈希值固定(利于作为
-
字符串常量池(String Constant Pool)
JVM 为优化内存,在方法区维护一个“常量池”,存储字面量创建的字符串(如"abc")。若创建相同字面量的字符串,会直接复用池中对象,而非新建。- 示例:
String s1 = "abc"; // 先查常量池,无则创建并放入,s1指向池对象 String s2 = "abc"; // 复用常量池对象,s1 == s2(地址相同) String s3 = new String("abc"); // 新建堆对象,s3指向堆,s3 != s1(地址不同) String s4 = s3.intern(); // 将堆对象的引用放入常量池,s4 == s1
- 示例:
二、字符串的创建方式
1. 字面量创建(推荐)
直接用双引号定义,优先使用常量池,内存更高效。
String str1 = "Hello Java"; // 字面量,存储于常量池
2. new 关键字创建
通过 new 实例化,会在堆内存创建对象(即使字面量已在常量池存在),再将对象地址赋值给变量。
String str2 = new String("Hello Java"); // 堆中新建对象,str2指向堆
String str3 = new String(new char[]{'H','e','l','l','o'}); // 通过字符数组创建
三、常用核心方法(重点)
String 类提供大量工具方法,以下为高频使用的核心方法:
| 方法分类 | 方法示例 | 功能说明 |
|---|---|---|
| 获取长度 | int length() | 返回字符串的字符个数(注意:中文、字母均算1个)。 |
| 判断内容 | boolean equals(Object obj) | 比较两个字符串的内容是否相等(区分大小写),需替代 ==(== 比地址)。 |
boolean equalsIgnoreCase(String s) | 忽略大小写比较内容(如 "ABC".equalsIgnoreCase("abc") 为 true)。 | |
boolean contains(CharSequence s) | 判断当前字符串是否包含子串 s(如 "abcde".contains("bc") 为 true)。 | |
boolean startsWith(String prefix) | 判断是否以 prefix 开头(如 "http://a.com".startsWith("http") 为 true)。 | |
boolean endsWith(String suffix) | 判断是否以 suffix 结尾(如 "file.txt".endsWith(".txt") 为 true)。 | |
| 查找位置 | int indexOf(String s) | 从左开始查找子串 s 第一次出现的索引,未找到返回 -1(如 "abcabc".indexOf("ab") 为 0)。 |
int lastIndexOf(String s) | 从右开始查找子串 s 第一次出现的索引,未找到返回 -1(如 "abcabc".lastIndexOf("ab") 为 3)。 | |
| 截取子串 | String substring(int beginIndex) | 从 beginIndex(含)开始截取到末尾(如 "abcde".substring(2) 返回 "cde")。 |
String substring(int begin, int end) | 从 begin(含)截取到 end(不含),注意 end <= length()(如 "abcde".substring(1,3) 返回 "bc")。 | |
| 替换内容 | String replace(CharSequence old, CharSequence new) | 替换所有匹配的子串(如 "aaa".replace("a","b") 返回 "bbb")。 |
String replaceFirst(String regex, String replacement) | 仅替换第一个匹配的子串(支持正则,如 "aabbaa".replaceFirst("aa","xx") 返回 "xxbbaa")。 | |
| 分割与合并 | String[] split(String regex) | 按正则 regex 分割字符串为数组(如 "a,b,c".split(",") 返回 {"a","b","c"})。 |
static String join(CharSequence delimiter, CharSequence... elements) | 用 delimiter 连接多个元素为字符串(如 String.join("-","2024","05","20") 返回 "2024-05-20")。 | |
| 转换操作 | char[] toCharArray() | 将字符串转为字符数组(如 "abc".toCharArray() 返回 {'a','b','c'})。 |
String toLowerCase() / toUpperCase() | 转为全小写/全大写(如 "AbC".toLowerCase() 返回 "abc")。 | |
String trim() | 去除字符串两端的空白字符(空格、制表符 \t、换行符 \n 等,中间空白保留,如 " a b ".trim() 返回 "a b")。 | |
static String valueOf(Object obj) | 将任意类型(如 int、Object)转为字符串(推荐,避免 null 转成 "null" 字符串)。 |
四、常见误区与注意事项
-
==与equals()的区别==:比较变量的内存地址(是否指向同一对象),仅字面量创建的相同字符串会返回true。equals():String重写了该方法,比较的是字符内容是否完全一致,是字符串比较的正确方式。- 错误示例:
"abc" == new String("abc")→false(地址不同);正确示例:"abc".equals(new String("abc"))→true(内容相同)。
-
空字符串与
null的区别- 空字符串(
""):是一个有效对象,长度为0,内存中存在(常量池或堆),可调用方法(如"" .length()返回0)。 null:表示变量未指向任何对象,是“无地址”状态,直接调用方法会抛出NullPointerException(空指针异常)。- 判空建议:先判
null再判空字符串,如if (str != null && !str.isEmpty())。
- 空字符串(
-
字符串拼接的性能问题
- 用
+拼接字符串(如str += "a"),编译后会转为StringBuilder的append()方法,但循环中频繁拼接会重复创建StringBuilder,性能差。 - 优化方案:循环拼接时,手动创建
StringBuilder对象,调用append()后用toString()收尾,示例:// 低效 String s = ""; for (int i = 0; i < 1000; i++) { s += i; // 每次循环新建 StringBuilder 和 String } // 高效 StringBuilder sb = new StringBuilder(); for (int i = 0; i < 1000; i++) { sb.append(i); // 仅操作一个 StringBuilder 对象 } String result = sb.toString();
- 用
五、String、StringBuilder、StringBuffer 对比
实际开发中需根据场景选择三者,核心区别如下:
| 类名 | 可变性 | 线程安全 | 性能 | 适用场景 |
|---|---|---|---|---|
String | 不可变 | 安全(无修改操作) | 低(修改频繁时) | 存储固定文本、常量、哈希键等 |
StringBuilder | 可变 | 不安全(无同步锁) | 高 | 单线程下频繁修改字符串(如循环拼接) |
StringBuffer | 可变 | 安全(方法加同步锁 synchronized) | 中(锁开销) | 多线程下频繁修改字符串(如并发日志) |
综上,String 是 Java 中最基础的文本类型,理解其不可变性和常量池机制是核心,熟练掌握常用方法并根据场景选择 String/StringBuilder/StringBuffer,能有效提升代码效率和安全性。
8488

被折叠的 条评论
为什么被折叠?



