Java String 字符串核心知识点总结及例题
String 是 Java 中最常用的引用数据类型,用于表示不可变的字符序列,掌握其核心特性和用法是 Java 基础的关键。
一、String 的核心特性
1. 不可变性(Immutable)
- 定义:字符串对象一旦创建,其内容(字符序列)无法被修改。
- 底层原因:String 类的核心存储是
private final char value[](JDK9+ 改为byte[]节省空间),final修饰的数组无法重新赋值,且数组本身的内容也被封装为私有,无对外修改的方法。 - 表现:对字符串的拼接、替换等操作,本质是创建新的 String 对象,原对象不变。
java
运行
String s = "abc"; s += "d"; // 实际创建了新字符串 "abcd",原 "abc" 仍存在 - 优势:线程安全、可缓存(字符串常量池)、哈希值可缓存(hashCode 计算一次后复用)。
2. 字符串常量池(String Pool)
- 定义:JVM 为优化内存,在堆内存中开辟的一块特殊区域,用于存储唯一的字符串字面量。
- 创建规则:
- 直接使用字面量(
String s = "abc"):优先从常量池查找,不存在则创建,存在则复用引用。 new String("abc"):先在常量池创建 "abc",再在堆中创建新对象指向该常量池内容(共创建 1-2 个对象)。
- 直接使用字面量(
- 示例:
java
运行
String s1 = "abc"; // 常量池创建 String s2 = "abc"; // 复用常量池引用,s1 == s2 → true String s3 = new String("abc"); // 堆新对象,s1 == s3 → false String s4 = s3.intern(); // 手动将 s3 入池,s1 == s4 → true
二、String 的创建与比较
1. 创建方式
表格
| 方式 | 示例 | 特点 |
|---|---|---|
| 字面量创建 | String s = "hello" |
优先使用常量池,效率高 |
| new 关键字 | String s = new String("hello") |
堆内存创建新对象,效率低,避免频繁使用 |
| 字符数组转换 | String s = new String(new char[]{'h','e','l','l','o'}) |
从字符数组构建,适合动态生成字符串 |
2. 比较方式
表格
| 方式 | 作用 | 示例 |
|---|---|---|
== |
比较对象引用地址(是否指向同一对象) | s1 == s2(字面量相等则 true,new 创建则 false) |
equals() |
比较字符串内容(String 重写了 Object 的 equals) | s1.equals(s2)(内容相同则 true) |
equalsIgnoreCase() |
忽略大小写比较内容 | "Abc".equalsIgnoreCase("abc") → true |
三、String 常用方法(高频)
1. 基础获取
length():获取字符串长度(字符数),注意与数组length属性区分。charAt(int index):获取指定索引的字符(索引从 0 开始),越界抛IndexOutOfBoundsException。indexOf(String str):查找子串首次出现的索引,无则返回 -1。lastIndexOf(String str):查找子串最后出现的索引,无则返回 -1。
2. 内容判断
isEmpty():判断是否为空字符串(长度为 0),注意null调用会抛空指针。contains(CharSequence s):判断是否包含指定子串。startsWith(String prefix):判断是否以指定前缀开头。endsWith(String suffix):判断是否以指定后缀结尾。
3. 内容修改(返回新字符串)
substring(int beginIndex)/substring(int begin, int end):截取子串(左闭右开)。replace(char old, char new)/replace(CharSequence target, CharSequence replacement):替换字符 / 子串。trim():去除首尾空白字符(空格、制表符、换行等,JDK11+ 可用strip()支持全角空格)。toLowerCase()/toUpperCase():转换大小写。split(String regex):按正则表达式分割字符串,返回字符串数组。
4. 类型转换
- 字符串转字符数组:
toCharArray()(如字符串反转的核心操作)。 - 字符串转字节数组:
getBytes()(指定编码如getBytes("UTF-8")避免乱码)。 - 其他类型转字符串:
String.valueOf(xxx)(推荐,避免 null 转为 "null")。
四、String 拼接的效率对比
表格
| 拼接方式 | 原理 | 效率 | 适用场景 |
|---|---|---|---|
+ / += |
底层自动创建 StringBuilder,拼接后转 String |
低(频繁创建对象) | 少量、简单拼接 |
StringBuilder |
可变字符序列,直接修改底层数组 | 高 | 循环 / 大量拼接 |
StringBuffer |
与 StringBuilder 功能一致,线程安全(方法加 synchronized) |
中(比 StringBuilder 慢) | 多线程环境拼接 |
示例(循环拼接推荐 StringBuilder):
java
运行
// 低效
String s = "";
for (int i = 0; i < 1000; i++) {
s += i; // 每次创建新对象
}
// 高效
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i);
}
String result = sb.toString();
五、String 与其他相关类的区别
表格
| 类 | 可变性 | 线程安全 | 核心用途 |
|---|---|---|---|
| String | 不可变 | 安全 | 存储固定字符串 |
| StringBuilder | 可变 | 不安全 | 单线程字符串拼接 |
| StringBuffer | 可变 | 安全(同步) | 多线程字符串拼接 |
六、常见易错点
- 空指针问题:调用
null字符串的方法(如null.equals("abc"))会抛NullPointerException,建议写成"abc".equals(null)(返回 false)。 - 常量池与 new 的区别:
new String("abc")会创建堆对象,避免不必要的 new 操作。 - split 正则陷阱:
split("\\.")才是分割点号(点号是正则通配符,需转义)。 - JDK 版本差异:JDK9 后 String 底层由
char[]改为byte[],节省内存(Latin 字符占 1 字节,中文等占 2 字节)。
总结
- String 核心特性是不可变性和常量池缓存,决定了其使用方式和性能特点。
- 字符串比较优先用
equals(),拼接优先用StringBuilder(单线程)。 - 高频方法需重点掌握:
charAt()、substring()、replace()、split()、contains()等,注意方法返回新字符串的特性。
例题一:Java 字符串反转编程题解析

一、解题步骤分析
步骤 1:明确需求与约束
- 功能需求:将输入字符串按字符顺序完全反转(如
HelloWorld→dlroWolleH)。 - 约束条件:禁止直接使用
StringBuilder.reverse()等现成反转方法。 - 边界要求:需处理空字符串、纯空格字符串、单字符字符串等特殊情况。
步骤 2:选择核心实现思路
由于不能使用现成反转方法,我们需要手动操作字符序列,双指针交换法是最高效且直观的选择:
- 将字符串转换为可修改的字符数组(
String本身不可变,必须通过数组操作)。 - 用两个指针分别指向数组的头部(left)和尾部(right)。
- 交换两个指针指向的字符,然后将
left右移、right左移,直到两指针相遇或交叉。 - 将修改后的字符数组重新封装为
String并返回。
步骤 3:边界情况处理
在核心逻辑执行前,先处理特殊情况以避免无效操作:
- 若输入为
null:直接返回null(或根据需求返回空字符串)。 - 若字符串长度 ≤ 1(空字符串、单字符):直接返回原字符串(无需反转)。
- 纯空格字符串:交换后仍为纯空格,核心逻辑可正常处理,无需额外判断。
二、完整代码示例
package com.sy;
/**
* 描述:字符串反转
*/
public class Demo01 {
/**
* 编写一个 Java 程序,实现字符串反转功能(禁止直接使用 `StringBuilder/reverse()` 等现成的反转方法)。
* 输入:一个任意字符串(例如 "HelloWorld")
* 输出:反转后的字符串(例如 "dlroWolleH")
* 要求:需处理空字符串、纯空格字符串等边界情况。
* 输出结果
* 原字符串:HelloWorld,反转后:dlroWolleH
* 原字符串:Java编程,反转后:程编avaJ
*/
public static void main(String[] args) {
String s = "HelloWorld";
System.out.println("原字符串:" + s + ",反转后:" + reverse(s));
String s1 = "Java编程";
System.out.println("原字符串:" + s1 + ",反转后:" + reverse(s1));
}
public static String reverse(String s) {
char[] chars = s.toCharArray();
int left = 0;
int right = chars.length - 1;
while (left < right) {
char temp = chars[left];
chars[left] = chars[right];
chars[right] = temp;
left++;
right--;
}
return new String(chars);
}
}
三、解题总结
核心思路总结
本题的核心是手动操作字符序列实现反转,通过双指针交换法在 O (n) 时间复杂度、O (n) 空间复杂度下完成高效反转,同时通过边界判断保证代码健壮性。
关键知识点
- String 不可变性:必须通过
toCharArray()转换为字符数组才能修改字符顺序。 - 双指针思想:通过首尾指针向中间移动交换,避免了多次创建新字符串的开销。
- 边界处理:空字符串、单字符、纯空格等特殊情况是代码健壮性的关键。
- 字符数组与 String 转换:
new String(char[])是将修改后的数组还原为字符串的标准方式。
优化与拓展
- 若需进一步优化空间,可考虑递归反转,但递归会增加栈开销,不推荐用于长字符串。
- 若允许使用
StringBuilder,可通过append逆序遍历字符实现反转,但本题明确禁止使用reverse()方法。
题目二:Java 字符串字符类型频次统计题解析

一、解题步骤与代码分析
步骤 1:明确需求与约束
- 功能需求:统计输入字符串中大写字母、小写字母、数字、空格、其他字符五类字符的出现次数。
- 技术约束:必须使用
charAt()或toCharArray()遍历字符,通过ASCII 范围判断字符类型,不能依赖Character工具类(如isUpperCase())。 - 输出要求:清晰展示每类字符的数量,格式友好易读。
步骤 2:核心实现思路
- 初始化计数器:为五类字符分别定义一个
int类型的计数器,初始值为 0。 - 遍历字符:将字符串转换为字符数组(
toCharArray()),或通过charAt()逐个获取字符,遍历每一个字符。 - 类型判断:根据字符的 ASCII 值范围进行分类:
- 大写字母:
'A'(65) ~'Z'(90) - 小写字母:
'a'(97) ~'z'(122) - 数字:
'0'(48) ~'9'(57) - 空格:
' '(32) - 其他字符:不属于以上范围的字符
- 大写字母:
- 累加计数:根据判断结果,对对应类别的计数器执行
++操作。 - 结果输出:按要求格式打印原字符串和各类字符的统计结果。
二、完整代码示例
package com.sy;
/**
* 统计字符串字符类型及频次
*/
public class Demo02 {
public static void main(String[] args) {
// 测试用例
String testStr = "Hello 123 World!@#";
Char(testStr);
}
public static void Char(String str) {
// 初始化各类字符计数器
int upperCase = 0;
int lowerCase = 0;
int digit = 0;
int space = 0;
int other = 0;
// 将字符串转换为字符数组,遍历每个字符
char[] chars = str.toCharArray();
for (char c : chars) {
if (c >= 'A' && c <= 'Z') {
upperCase++;
} else if (c >= 'a' && c <= 'z') {
lowerCase++;
} else if (c >= '0' && c <= '9') {
digit++;
} else if (c == ' ') {
space++;
} else {
other++;
}
}
// 输出统计结果
System.out.println("字符串: " + str);
System.out.println("大写字母: " + upperCase + " 个");
System.out.println("小写字母: " + lowerCase + " 个");
System.out.println("数字: " + digit + " 个");
System.out.println("空格: " + space + " 个");
System.out.println("其他字符: " + other + " 个");
}
}
三、解题总结
核心思路总结
本题的核心是遍历字符串 + 基于 ASCII 范围的字符分类 + 计数器累加,通过手动判断字符类型,加深对 ASCII 编码和字符串底层结构的理解。
关键知识点
- 字符串遍历:
toCharArray()和charAt()是遍历字符串字符的两种基础方式,本题推荐使用toCharArray(),代码更简洁。 - ASCII 范围判断:掌握常见字符的 ASCII 区间是核心考点,避免依赖封装好的工具类,体现底层实现能力。
- 计数器设计:多计数器模式是统计类问题的通用解法,清晰分离不同类别的数据。
- 边界处理:代码天然支持空字符串、纯空格等边界情况,无需额外判断,保证了程序健壮性。
优化与拓展
- 若需支持更多语言字符(如中文),可拓展判断逻辑,但本题仅要求 ASCII 范围内的分类。
- 可将统计结果封装为
Map或自定义对象,便于后续数据处理和复用。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)