Day 05:字符串 - 文本处理专家
🎯 今日学习目标
- 深入理解
String类,明白它为什么是“不可变”的。 - 掌握创建
String对象的多种方式。 - 熟练使用字符串的常用方法:获取长度、比较、查找、截取、替换、大小写转换等。
- 理解字符串拼接的不同方式及其效率差异。
- 能够使用字符串解决实际问题,如文本分析、数据格式化。
一、初识字符串:不只是“一段文字”
在之前的课程中,我们已经多次使用过字符串:
String name = "大雄";
System.out.println("Hello, World!");
但 String在Java中远不止是“用双引号包起来的文字”。它是一个类,是Java标准库中用来表示和操作字符序列的核心类。
1. 字符串无处不在
想想你使用的软件:
- 用户名、密码是
String。 - 聊天消息、文章内容是
String。 - 文件路径、网址是
String。 - 甚至程序读取的配置文件、网络传输的数据,也常常是
String格式。
可以说,String是编程中处理“文本”信息的基石。 掌握好它,你才能让程序“读懂”和“说出”人话。
二、String 的创建:多种方式,殊途同归
创建 String对象(也就是得到一个字符串)主要有以下几种方式:
1. 直接赋值(最常用、最推荐)
String str1 = "Hello, Java!";
String str2 = "你好,Java!";
String emptyStr = ""; // 空字符串,长度为0
这种方式最为简洁直观。Java会使用一种叫做“字符串常量池”的机制来管理这些字符串,可以提高效率。你现在可以简单理解为:用这种方式创建,Java会尽量复用已有的相同字符串。

2. 通过构造方法 new String()
String是一个类,自然可以用 new关键字来创建它的对象。
// 方式1:根据字符数组创建
char[] charArray = {'J', 'a', 'v', 'a'};
String str3 = new String(charArray); // str3 的内容是 "Java"
// 方式2:根据字节数组创建(常用于网络传输、文件读写)
byte[] byteArray = {65, 66, 67}; // ASCII码,A=65, B=66, C=67
String str4 = new String(byteArray); // str4 的内容是 "ABC"
// 方式3:直接 new
String str5 = new String("Hello"); // 创建一个内容为"Hello"的新String对象
注意:new String(“Hello”)和 String str = “Hello”;这两种写法在内存处理上有细微差别。对于初学者,记住绝大多数情况下,使用第一种“直接赋值”的方式就够了,它更简洁高效。new String()通常在有特定需求(如从字节数组转换)时使用。
3. 一个重要的概念:字符串不可变性
这是理解 String所有行为的关键!String对象一旦被创建,其内容(字符序列)就永远不能被改变。
String s = "Hello";
String sCopy = s; // 将s赋值给sCopy
s = s + " World"; // 这看起来像是修改了s
System.out.println(s); // 输出 "Hello World"
System.out.println(sCopy); // 输出的还是 "Hello"
❌ 误解:上面代码好像把 "Hello"改成了 "Hello World"。
✅ 真相:实际上,s + “ World”这个操作创建了一个全新的字符串对象 “Hello World”,然后让变量 s指向这个新对象。原来的 “Hello”这个字符串对象依然存在于内存中,并且内容 “Hello”没有任何变化。
形象的比喻:
把 String对象想象成一个已经写上字并且用玻璃封装好的相框。
- 你无法擦掉玻璃相框里已经写好的字(不可修改内容)。
- 但你可以换一个新的、写了不同字的相框,然后把标签(变量名
s)贴到这个新相框上(让变量引用新对象)。
为什么这样设计?
主要是为了安全和效率。因为不可变,所以多个变量可以安全地共享同一个字符串对象,而不必担心其中一个修改会影响其他。这也是“字符串常量池”能够存在的基础。
三、字符串的常用方法(上):信息获取与比较
String类提供了几十个方法供我们操作字符串。我们按功能分类学习。今天先学习最常用的一部分。
如何调用方法:字符串是对象,所以调用其方法的语法是:字符串变量名.方法名(参数)。
1. 获取字符串信息
String message = "Hello, Java Programmers!";
// 1. length(): 返回字符串的长度(字符个数)
int len = message.length();
System.out.println("字符串长度: " + len); // 输出: 24 (注意空格和标点都算一个字符)
// 2. charAt(int index): 返回指定索引处的字符
char firstChar = message.charAt(0);
char lastChar = message.charAt(message.length() - 1);
System.out.println("第一个字符: " + firstChar); // H
System.out.println("最后一个字符: " + lastChar); // !
// 3. isEmpty(): 判断字符串是否为空(长度为0)
String emptyStr = "";
System.out.println("emptyStr是否为空? " + emptyStr.isEmpty()); // true
System.out.println("message是否为空? " + message.isEmpty()); // false
// 注意:null 和 空字符串 "" 是两回事。一个没有引用的变量(null)调用方法会报错。
2. 字符串的比较
⚠️ 超级重点:比较字符串内容是否相等,永远不要用 ==!
String s1 = "Java";
String s2 = "Java";
String s3 = new String("Java");
String s4 = "java";
// ❌ 错误的方式:使用 ==
// == 比较的是两个变量是否指向内存中的同一个对象(地址),而不是内容是否相同。
System.out.println("s1 == s2: " + (s1 == s2)); // 可能为 true (因为常量池复用)
System.out.println("s1 == s3: " + (s1 == s3)); // false! 因为s3是new出来的新对象
// ✅ 正确的方式:使用 equals() 方法比较内容
System.out.println("s1.equals(s2): " + s1.equals(s2)); // true
System.out.println("s1.equals(s3): " + s1.equals(s3)); // true!内容相同
System.out.println("s1.equals(s4): " + s1.equals(s4)); // false,大小写不同
// ✅ 忽略大小写比较:equalsIgnoreCase()
System.out.println("s1.equalsIgnoreCase(s4): " + s1.equalsIgnoreCase(s4)); // true
// 比较字符串在字典中的顺序:compareTo()
// 返回0表示相等,负数表示调用者在前,正数表示参数在前(按Unicode值比较)
System.out.println("\"apple\".compareTo(\"banana\"): " + "apple".compareTo("banana")); // 负数
System.out.println("\"cat\".compareTo(\"cat\"): " + "cat".compareTo("cat")); // 0
System.out.println("\"dog\".compareTo(\"cat\"): " + "dog".compareTo("cat")); // 正数
为什么 ==有时也对?
因为Java的“字符串常量池”优化,String s1 = “Java”;和 String s2 = “Java”;可能让 s1和 s2指向内存中同一个 “Java”对象,所以 ==比较为真。但这是一种不稳定的优化,绝非比较内容的正确方法。牢记:比较字符串内容,请无条件使用 equals()方法。
四、字符串的常用方法(中):查找与子串
1. 查找字符或子串
String text = "I love Java programming. Java is fun!";
// 1. indexOf(...): 返回指定字符或子串第一次出现的索引,找不到返回-1
int indexOfLove = text.indexOf("love");
int indexOfJava = text.indexOf("Java");
int indexOfPython = text.indexOf("Python");
System.out.println("'love'首次出现在: " + indexOfLove); // 2
System.out.println("'Java'首次出现在: " + indexOfJava); // 7
System.out.println("'Python'首次出现: " + indexOfPython); // -1 (没找到)
// 可以从指定位置开始查找
int secondJavaIndex = text.indexOf("Java", indexOfJava + 1); // 从第一个Java后面开始找
System.out.println("第二个'Java'在: " + secondJavaIndex); // 25
// 2. lastIndexOf(...): 返回指定字符或子串最后一次出现的索引
int lastIndexOfJava = text.lastIndexOf("Java");
System.out.println("'Java'最后一次出现在: " + lastIndexOfJava); // 25
// 3. contains(CharSequence s): 判断是否包含某个子串 (返回boolean)
System.out.println("是否包含'fun'? " + text.contains("fun")); // true
System.out.println("是否包含'Python'? " + text.contains("Python")); // false
2. 截取子串
String url = "https://www.example.com/page/index.html";
// substring(int beginIndex): 从beginIndex开始截取到字符串末尾
String domain = url.substring(8); // 从索引8('w')开始截取
System.out.println("域名及之后: " + domain); // www.example.com/page/index.html
// substring(int beginIndex, int endIndex):
// 截取 [beginIndex, endIndex) 范围的子串 (包含头,不包含尾)
String protocol = url.substring(0, 5); // 索引0到4
String host = url.substring(8, 22); // 索引8到21
System.out.println("协议: " + protocol); // https
System.out.println("主机: " + host); // www.example.com
五、字符串的常用方法(下):修改与转换
再次强调:由于字符串不可变,所有这些“修改”方法都不会改变原字符串,而是返回一个全新的字符串对象。
1. 替换
String original = "Hello World, World is big.";
// replace(char oldChar, char newChar): 替换所有指定字符
String replaced1 = original.replace('o', '0');
System.out.println(replaced1); // Hell0 W0rld, W0rld is big.
// replace(CharSequence target, CharSequence replacement): 替换所有子串
String replaced2 = original.replace("World", "Java");
System.out.println(replaced2); // Hello Java, Java is big.
// 注意:原字符串不变!
System.out.println("原字符串: " + original); // 仍然是 "Hello World, World is big."
2. 去除首尾空白
String userInput = " Hello, how are you? \n";
String trimmed = userInput.trim(); // 去除首尾的空白字符(空格、制表符、换行符)
System.out.println("修剪前: '" + userInput + "'");
System.out.println("修剪后: '" + trimmed + "'");
// 中间的空格不会被去除
3. 大小写转换
String mixedCase = "Hello Java";
String upper = mixedCase.toUpperCase();
String lower = mixedCase.toLowerCase();
System.out.println("原串: " + mixedCase);
System.out.println("大写: " + upper); // HELLO JAVA
System.out.println("小写: " + lower); // hello java
六、字符串拼接
将多个字符串连接成一个,是最常见的操作之一。
1. 使用 +运算符
最简单直观的方式。
String firstName = "张";
String lastName = "三";
int age = 25;
String fullName = firstName + lastName; // "张三"
String info = "姓名: " + fullName + ", 年龄: " + age; // 字符串与数字拼接,数字会自动转为字符串
System.out.println(info);
2. 使用 concat()方法
功能和 +类似。
String s1 = "Hello";
String s2 = "World";
String s3 = s1.concat(" ").concat(s2); // 链式调用
System.out.println(s3); // Hello World
3. 性能考虑与 StringBuilder
+和 concat()在循环中大量拼接字符串时效率很低,因为每次都会创建新的String对象。
// ❌ 低效的写法(在循环内用 + 拼接)
String result = "";
for (int i = 0; i < 1000; i++) {
result += i; // 每次循环都产生一个新字符串,旧对象成为垃圾
}
高效的做法:使用 StringBuilder
StringBuilder是一个可变的字符序列,专门用于高效拼接字符串。
// ✅ 高效的写法
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i); // 在同一个StringBuilder对象上修改,不创建新对象
}
String finalResult = sb.toString(); // 最后一次性转换为String
System.out.println(finalResult);
简单规则:在循环内拼接字符串,或者需要频繁修改字符串内容时,使用 StringBuilder。简单的单次拼接用 +即可。
七、综合实战:文本分析工具
让我们运用所学的字符串方法,制作一个简单的文本分析工具。
import java.util.Scanner;
public class TextAnalyzer {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("=== 简易文本分析工具 ===");
System.out.println("请输入一段英文文本(按回车结束):");
String inputText = scanner.nextLine(); // 读取一整行文本
if (inputText == null || inputText.trim().isEmpty()) {
System.out.println("输入不能为空!");
scanner.close();
return;
}
// 1. 基本统计
int totalChars = inputText.length();
String trimmedText = inputText.trim();
int nonSpaceChars = trimmedText.replace(" ", "").length(); // 去除空格后的长度
int wordCount = countWords(trimmedText);
// 2. 查找关键词
System.out.print("请输入一个你想在文中查找的单词: ");
String keyword = scanner.next().toLowerCase(); // 读取一个单词,并转为小写
int keywordOccurrences = countOccurrences(trimmedText.toLowerCase(), keyword);
// 3. 输出报告
printAnalysisReport(trimmedText, totalChars, nonSpaceChars, wordCount, keyword, keywordOccurrences);
scanner.close();
}
/**
* 统计单词数(简易版,按空格分割)
*/
public static int countWords(String text) {
if (text.isEmpty()) {
return 0;
}
// 使用 split 方法按空白字符分割字符串,返回单词数组
String[] words = text.split("\\s+"); // 正则表达式 \s+ 表示一个或多个空白字符
return words.length;
}
/**
* 统计某个关键词出现的次数
*/
public static int countOccurrences(String text, String keyword) {
int count = 0;
int index = 0;
// 循环查找,每次从上一次找到的位置之后开始
while ((index = text.indexOf(keyword, index)) != -1) {
count++;
index += keyword.length(); // 跳过已找到的关键词,继续向后查找
}
return count;
}
/**
* 打印分析报告
*/
public static void printAnalysisReport(String text, int totalChars, int nonSpaceChars,
int wordCount, String keyword, int occurrences) {
System.out.println("\n========== 文本分析报告 ==========");
System.out.println("原文摘要: \"" + getTextPreview(text, 50) + "\"");
System.out.println("----------------------------------");
System.out.println("1. 字符统计:");
System.out.println(" 总字符数 (含空格): " + totalChars);
System.out.println(" 非空格字符数: " + nonSpaceChars);
System.out.println(" 单词数 (近似): " + wordCount);
System.out.println("\n2. 关键词查找:");
System.out.printf(" 关键词 \"%s\" 出现了 %d 次。\n", keyword, occurrences);
System.out.println("\n3. 其他信息:");
System.out.println(" 全部大写: " + text.toUpperCase());
System.out.println(" 全部小写: " + text.toLowerCase());
System.out.println("==================================");
}
/**
* 获取文本预览(前n个字符,超出部分用...表示)
*/
public static String getTextPreview(String text, int maxLength) {
if (text.length() <= maxLength) {
return text;
} else {
return text.substring(0, maxLength) + "...";
}
}
}
📚 今日巩固练习
练习1:验证回文串
回文串是指正着读和反着读都一样的字符串,忽略空格、标点、大小写。例如:“A man, a plan, a canal: Panama”。
- 编写一个方法
boolean isPalindrome(String s),判断一个字符串是否是回文串。 - 在
main方法中测试。
提示:可以先使用 toLowerCase()、replaceAll(“[^a-z0-9]”, “”)(正则表达式,去掉非字母数字字符)处理字符串,再用双指针法或反转字符串比较。
练习2:密码强度检查器
编写一个程序,检查用户输入的密码强度。规则如下(可自定义):
- 长度 >= 8
- 包含至少一个大写字母
- 包含至少一个小写字母
- 包含至少一个数字
- 包含至少一个特殊字符(如!@#$%) 根据满足的条件数量,输出密码强度:弱、中、强。
提示:使用循环和 charAt()遍历密码字符,或用 contains()配合条件判断。
练习3:字符串翻转
编写一个方法 String reverseString(String s),实现字符串的翻转。例如,输入 “hello”,返回 “olleh”。
- 使用循环和
charAt()实现。 - 尝试使用
StringBuilder的reverse()方法实现。
挑战题:简单的命令行解析
模拟解析一个命令行指令,如 “copy file.txt /home/user backup_”。
- 将指令按空格分割成数组(
split(” “))。 - 第一个元素是命令(
copy)。 - 后续的是参数。尝试识别参数中的选项(以
-开头的)和值。
💡 常见问题解答 (Q&A)
Q1:String是基本数据类型吗?
A:不是。 String是Java标准库提供的一个类(java.lang.String)。它是引用类型。只是因为太常用了,Java允许用双引号 ""这种字面量形式创建其对象,让人感觉像基本类型。
Q2:==和 equals()的区别,再说一次?
A:这是必考重点!
==:比较两个变量存储的地址值是否相同,即是否指向内存中同一个对象。equals():比较两个对象的内容是否相同(对于String就是字符序列是否相同)。 黄金法则:只要你想比较两个字符串的内容是否一样,永远、无条件、必须使用equals()方法。
Q3:null和空字符串 ""一样吗?
A:完全不一样!
null:表示一个引用(变量)没有指向任何对象。在null上调用任何方法(如length())都会导致NullPointerException。"":这是一个有效的String对象,内容是空的,长度为0。你可以在它上面安全地调用所有String方法。
String s1 = null;
String s2 = "";
System.out.println(s1 == null); // true
System.out.println(s2 == null); // false
System.out.println(s2.isEmpty()); // true
// System.out.println(s1.isEmpty()); // 运行时错误: NullPointerException
Q4:字符串的“不可变性”到底有什么用?
A:主要好处:
- 安全:作为参数传递时,不用担心方法内部会修改它。例如,数据库用户名、密码作为字符串传递是安全的。
- 可以缓存哈希值:
String的hashCode()经常被使用(如在HashMap中),因为内容不变,哈希值只需计算一次,提高了效率。 - 实现字符串常量池,共享相同内容的字符串,节省内存。
Q5:什么时候用 StringBuilder?
A:当你在循环体内进行字符串拼接,或者需要频繁、多次修改一个字符串时,使用 StringBuilder。对于简单的、次数固定的拼接(如 String s = a + b + c;),用 +即可,代码更清晰。
🎓 今日核心总结
今天,你深入学习了Java中处理文本的核心——String类。
- ✅ 理解本质:明白了
String是类而非基本类型,掌握了其不可变性这一核心特性。 - ✅ 创建对象:学会了用直接赋值和构造方法创建字符串。
- ✅ 熟练使用API:掌握了获取信息(
length,charAt)、比较(equals,compareTo)、查找(indexOf,contains)、截取(substring)、修改(replace,trim,toUpperCase)等常用方法。牢记比较内容用equals()! - ✅ 字符串拼接:了解了
+、concat()和StringBuilder的区别与适用场景。 - ✅ 实战应用:通过构建文本分析工具,综合运用了字符串的各种操作来解决实际问题。
🔧 能力提升:你现在可以让程序自如地处理用户输入、分析文件内容、格式化输出信息了。字符串操作是日常编程的“基本功”,今天学的方法将伴随你的整个编程生涯。
📮 作业提交与明日预告
你的作业
-
必做:完成“综合实战”中的 《文本分析工具》,并尝试添加一个新功能(如统计某个字母的出现频率)。
-
选做:从“巩固练习”中至少完成2个。
-
代码位置:请将你的作业代码文件,创建在以下目录中:
module01_basic_syntax/src/day05_strings/
提交要求
- 在
📝 每日学习打卡话题下,搜索《Day 05 作业》完成每日打卡吧。
明日预告
Day 06:第一阶段复习与综合练习
明天是我们第一阶段“基础语法”的复习日。我们将:
- 系统回顾前5天(变量、流程控制、数组、方法、字符串)的核心知识点。
- 通过一系列精心设计的综合练习题,巩固你的学习成果。
- 讲解常见的错误模式与调试技巧。
- 为Day 07的迷你项目“控制台计算器”做准备。
✨ 提前准备:建议你花点时间复习一下前5天的笔记和代码,尝试自己总结每个知识点的关键。明天是检验和提升的好机会!
⚠️ 学习提醒
-
代码位置:请务必把今天的练习代码保存到正确的包路径下:
module01_basic_syntax/src/day05_strings/。 -
多敲多试:字符串的方法很多,最好的记忆方式是在IDEA里多敲代码,利用代码补全(输入
”abc”.然后按Ctrl+Space)查看所有可用方法,并尝试它们。 -
善用Git:记得提交你的代码。
git add . git commit -m "feat: Day 05: 掌握String类的核心方法及不可变性" -
理解本质:“不可变性”和“
equals()vs==”是初学者的两大拦路虎。如果不甚明白,把今天的例子多运行几次,在 ❓ 问答与答疑 区提出你的困惑。
恭喜你完成了字符串的学习!你手中的工具又多了强大的一件。明天我们将停下脚步,回顾来时路,让知识沉淀得更牢固。复习是进步的阶梯,我们明天见!📖
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)