【码道】系列博客致力于为广大Java学习者提供清晰、系统的学习路径。从基础语法到高级特性,从理论讲解到实战应用,我们将用简洁易懂的语言,带您循序渐进地掌握Java编程精髓。无论您是初学者还是希望巩固基础的开发者,都能在这里找到成长为Java高手的捷径。让我们一起探索编程之道,体验Java的无限魅力!
一、字符串:程序中的文本处理核心
在编程世界中,文本数据无处不在。从用户名、密码到文章内容,从配置文件到网页内容,我们需要不断地处理各种文本信息。在Java中,字符串(String)是处理文本的基础工具,也是使用频率最高的引用类型之一。
字符串本质上是字符的有序序列。例如,"Hello"是由’H’、‘e’、‘l’、‘l’、'o’五个字符组成的字符串。今天,我们将深入学习Java中字符串的各种操作方法。
二、String类基础
在Java中,字符串由String
类表示,它是Java标准库中的核心类,位于java.lang
包中(无需显式导入)。
1. 字符串的特点
- 不可变性:一旦创建,字符串的内容不能修改
- 字符序列:本质上是一系列Unicode字符的集合
- 线程安全:因为不可变,所以天然线程安全
- 特殊地位:Java对字符串提供了特殊支持,如字符串字面量
三、创建字符串
Java中创建字符串有多种方式:
1. 字符串字面量
最常见的方式是使用字符串字面量(双引号括起的文本):
String name = "张三";
String message = "你好,世界!";
2. 使用new关键字
可以通过new
关键字显式创建字符串对象:
String city = new String("北京");
char[] chars = {'上', '海'};
String city2 = new String(chars);
3. 字符串常量池
为了提高效率,Java维护了一个字符串常量池。使用字面量创建的字符串会被放入池中重用:
String s1 = "hello";
String s2 = "hello"; // 不会创建新对象,而是复用s1引用的对象
String s3 = new String("hello"); // 强制创建新对象,不使用常量池
四、字符串的不可变性
String对象一旦创建,它的值就不能改变。这看起来与我们的直觉相反:
String name = "张";
name = name + "三"; // 看起来修改了字符串,但实际上...
实际发生的是:
- 创建了字符串"张"
- 创建了新字符串"三"
- 创建了新字符串"张三"
- 变量name的引用改为指向新创建的"张三"
- 原来的"张"字符串对象依然存在,只是没有引用指向它了
这种不可变性虽然看起来低效,但带来了很多好处:
- 安全性:不会被意外修改
- 线程安全:多线程可以共享访问
- 哈希操作优化:可以缓存哈希码
五、常用字符串操作方法
1. 获取字符串信息
String text = "Hello, Java!";
// 获取长度
int length = text.length(); // 12
// 检查是否为空
boolean isEmpty = text.isEmpty(); // false
// 获取特定位置的字符(索引从0开始)
char firstChar = text.charAt(0); // 'H'
2. 查找和子串操作
String sentence = "Java是世界上最流行的编程语言之一";
// 查找子串位置
int index = sentence.indexOf("流行"); // 8
int lastIndex = sentence.lastIndexOf("语言"); // 12
// 检查前缀和后缀
boolean startsWithJava = sentence.startsWith("Java"); // true
boolean endsWithOne = sentence.endsWith("之一"); // true
// 提取子串
String sub1 = sentence.substring(8); // "流行的编程语言之一"
String sub2 = sentence.substring(8, 10); // "流行"
3. 字符串修改操作
由于不可变性,这些操作实际上都是创建新的字符串:
String original = "Hello, World!";
// 转换大小写
String upper = original.toUpperCase(); // "HELLO, WORLD!"
String lower = original.toLowerCase(); // "hello, world!"
// 替换字符或子串
String replaced = original.replace('l', 'L'); // "HeLLo, WorLd!"
String replacedStr = original.replace("World", "Java"); // "Hello, Java!"
// 去除首尾空白
String withSpaces = " trim example ";
String trimmed = withSpaces.trim(); // "trim example"
// 连接字符串
String firstName = "张";
String lastName = "三";
String fullName = firstName.concat(lastName); // "张三"
4. 字符串分割和连接
// 分割字符串
String csvLine = "苹果,香蕉,葡萄,橙子";
String[] fruits = csvLine.split(","); // ["苹果", "香蕉", "葡萄", "橙子"]
// 使用静态方法连接
String[] words = {"This", "is", "a", "test"};
String joined = String.join(" ", words); // "This is a test"
六、字符串比较
1. equals方法:比较内容
String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");
System.out.println(s1.equals(s2)); // true(内容相同)
System.out.println(s1.equals(s3)); // true(内容相同)
System.out.println(s1 == s2); // true(同一对象,都来自常量池)
System.out.println(s1 == s3); // false(不同对象)
2. 忽略大小写比较
String name1 = "Java";
String name2 = "java";
System.out.println(name1.equalsIgnoreCase(name2)); // true
3. 比较字符串大小(字典顺序)
String word1 = "apple";
String word2 = "banana";
int result = word1.compareTo(word2); // 负数,表示word1<word2
七、字符串格式化
Java提供了类似C语言printf的格式化功能:
String name = "张三";
int age = 25;
double height = 175.5;
// 格式化字符串
String info = String.format("姓名:%s,年龄:%d岁,身高:%.1f厘米", name, age, height);
// 输出: 姓名:张三,年龄:25岁,身高:175.5厘米
// 常用格式说明符
// %s - 字符串
// %d - 整数
// %f - 浮点数(%.2f 表示保留2位小数)
// %c - 字符
// %b - 布尔值
// %n - 换行符
// 直接使用printf方法输出格式化字符串
System.out.printf("欢迎%s光临,您是今天第%d位顾客%n", name, 5);
八、StringBuilder和StringBuffer:高效字符串构建
由于String的不可变性,大量拼接字符串会产生大量临时对象,效率低下:
String result = "";
for (int i = 0; i < 1000; i++) {
result += i; // 低效,每次都创建新字符串
}
Java提供了两个可变字符序列类用于高效构建字符串:
1. StringBuilder(非线程安全,但更高效)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i); // 高效,不创建临时对象
}
String result = sb.toString();
2. StringBuffer(线程安全,略微慢一些)
StringBuffer buffer = new StringBuffer();
buffer.append("Hello");
buffer.append(" ");
buffer.append("World");
String result = buffer.toString(); // "Hello World"
3. 常用方法
StringBuilder builder = new StringBuilder("Hello");
// 添加内容
builder.append(" World"); // "Hello World"
// 插入内容
builder.insert(5, ","); // "Hello, World"
// 删除内容
builder.delete(5, 7); // "Hello World"
// 替换内容
builder.replace(6, 11, "Java"); // "Hello Java"
// 反转
builder.reverse(); // "avaJ olleH"
4. 性能对比
// 测量String拼接和StringBuilder的性能差异
long startTime = System.currentTimeMillis();
// 使用String拼接
String s = "";
for (int i = 0; i < 100000; i++) {
s += "a";
}
long endTime = System.currentTimeMillis();
System.out.println("String拼接耗时:" + (endTime - startTime) + "毫秒");
// 使用StringBuilder
startTime = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100000; i++) {
sb.append("a");
}
String result = sb.toString();
endTime = System.currentTimeMillis();
System.out.println("StringBuilder耗时:" + (endTime - startTime) + "毫秒");
九、正则表达式与字符串
Java String类提供了与正则表达式配合使用的方法:
1. 匹配
String email = "user@example.com";
boolean isEmail = email.matches("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}");
System.out.println("是否是合法邮箱:" + isEmail); // true
2. 替换
String text = "我的电话是123-456-7890和987-654-3210";
// 将所有电话号码替换为"***-***-****"
String anonymized = text.replaceAll("\\d{3}-\\d{3}-\\d{4}", "***-***-****");
// 输出:我的电话是***-***-****和***-***-****
3. 分割
// 按照多种分隔符分割字符串
String data = "apple,banana;cherry|grape";
String[] fruits = data.split("[,;|]"); // ["apple", "banana", "cherry", "grape"]
十、实用示例:文本处理应用
我们来看一个简单的短信统计应用,综合使用字符串操作:
public class SMSAnalyzer {
public static void main(String[] args) {
String message = "亲爱的用户,您的订单#12345已发货,预计3天内送达。如有问题,请联系客服电话400-123-4567。祝您生活愉快!【某某商城】";
// 1. 基本信息统计
int length = message.length();
int charCount = message.replaceAll("\\s", "").length(); // 去除空白后的字符数
// 2. 提取订单号
String orderPattern = "#(\\d+)";
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(orderPattern);
java.util.regex.Matcher matcher = pattern.matcher(message);
String orderNumber = matcher.find() ? matcher.group(1) : "未找到订单号";
// 3. 提取联系电话
String phonePattern = "(\\d{3}-\\d{3}-\\d{4})";
pattern = java.util.regex.Pattern.compile(phonePattern);
matcher = pattern.matcher(message);
String contactPhone = matcher.find() ? matcher.group(1) : "未找到联系电话";
// 4. 匿名化处理
String anonymizedMessage = message.replaceAll(phonePattern, "***-***-****");
// 5. 分割成句子
String[] sentences = message.split("[。!?]");
// 6. 检查是否包含特定内容
boolean isOrderMessage = message.contains("订单");
boolean isPromotional = message.endsWith("】") && message.contains("【");
// 7. 使用StringBuilder构建报告
StringBuilder report = new StringBuilder();
report.append("短信分析报告\n");
report.append("========================\n");
report.append(String.format("总字符数: %d\n", length));
report.append(String.format("实际内容字符数: %d\n", charCount));
report.append(String.format("订单号: %s\n", orderNumber));
report.append(String.format("联系电话: %s\n", contactPhone));
report.append(String.format("是否是订单消息: %b\n", isOrderMessage));
report.append(String.format("是否是营销短信: %b\n", isPromotional));
report.append("\n句子分析:\n");
for (int i = 0; i < sentences.length; i++) {
String sentence = sentences[i].trim();
if (!sentence.isEmpty()) {
report.append(String.format("第%d句: %s\n", i+1, sentence));
}
}
report.append("\n匿名化后的短信:\n");
report.append(anonymizedMessage);
System.out.println(report.toString());
}
}
十一、字符串操作的性能建议
-
合理使用字符串拼接
- 少量拼接:可以使用+运算符
- 大量拼接:使用StringBuilder
-
减少临时字符串对象
- 避免在循环中使用+拼接字符串
- 考虑使用字符数组替代字符串进行频繁操作
-
正确使用字符串比较
- 比较内容用equals(),不要用==
- 大小写不敏感比较用equalsIgnoreCase()
-
利用字符串池
- 对于常量字符串,优先使用字面量而不是new
-
合理使用正则表达式
- 复杂正则表达式可以预编译,避免重复编译
- 简单匹配考虑使用indexOf()等方法代替正则
十二、总结
Java中的字符串操作涵盖了从基本的字符获取、子串提取到高级的正则表达式匹配等多种功能。虽然String对象本身是不可变的,但通过各种方法和辅助类,我们可以高效地进行各种文本处理任务。
掌握字符串操作是Java编程的基础技能,几乎所有Java应用都会涉及到文本处理。从用户输入验证到数据分析,从文件处理到网络通信,字符串操作无处不在。
在实际开发中,请记住:
- 合理选择String、StringBuilder或StringBuffer,根据场景优化性能
- 理解String的不可变性及其影响
- 熟练使用正则表达式进行复杂的文本匹配和处理