MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Java字符串处理与常用方法

2021-07-264.0k 阅读

Java 字符串基础

在 Java 中,字符串是一个非常重要的数据类型,用于表示文本。Java 中的字符串是不可变的,这意味着一旦创建了一个字符串对象,它的值就不能被修改。Java 提供了 java.lang.String 类来处理字符串。

字符串的创建

  1. 直接赋值创建

    String str1 = "Hello, World!";
    

    这种方式创建字符串时,Java 会首先在字符串常量池中查找是否存在相同内容的字符串。如果存在,则直接返回常量池中的引用;如果不存在,则在常量池中创建该字符串并返回引用。

  2. 使用 new 关键字创建

    String str2 = new String("Hello, World!");
    

    使用 new 关键字创建字符串时,会在堆内存中创建一个新的字符串对象,即使字符串常量池中已经存在相同内容的字符串。这种方式会额外消耗堆内存空间。

字符串的不可变性

字符串的不可变性是 Java 字符串的一个重要特性。例如:

String s = "abc";
s = s + "d";

在上述代码中,s 最初指向字符串 “abc”。当执行 s = s + "d" 时,并不是在原有 “abc” 字符串上直接添加 “d”,而是创建了一个新的字符串 “abcd”,并让 s 指向这个新的字符串。原来的 “abc” 字符串依然存在于字符串常量池中,只是没有任何引用指向它,等待垃圾回收器回收。

这种不可变性带来了很多好处,比如字符串常量池的实现依赖于字符串的不可变性。多个地方引用相同内容的字符串时,实际引用的是常量池中的同一个对象,节省了内存空间。同时,不可变对象是线程安全的,在多线程环境下不需要额外的同步操作。

字符串常用方法

字符串长度

获取字符串长度可以使用 length() 方法。例如:

String str = "Java is fun";
int length = str.length();
System.out.println("字符串长度为: " + length);

上述代码中,str.length() 返回字符串 “Java is fun” 的字符个数,即 11。

字符访问

  1. charAt(int index) 方法 通过 charAt(int index) 方法可以获取字符串中指定位置的字符。索引从 0 开始。例如:
String str = "Hello";
char c = str.charAt(1);
System.out.println("索引 1 处的字符为: " + c);

在上述代码中,str.charAt(1) 获取字符串 “Hello” 中索引为 1 的字符,即 ‘e’。

  1. toCharArray() 方法 toCharArray() 方法将字符串转换为字符数组。例如:
String str = "example";
char[] charArray = str.toCharArray();
for (char c : charArray) {
    System.out.print(c + " ");
}

上述代码将字符串 “example” 转换为字符数组,并遍历打印每个字符。

字符串比较

  1. equals(Object obj) 方法 equals(Object obj) 方法用于比较两个字符串的内容是否相等。例如:
String str1 = "Java";
String str2 = "Java";
String str3 = new String("Java");
System.out.println(str1.equals(str2)); // true
System.out.println(str1.equals(str3)); // true

在上述代码中,str1str2 都是通过直接赋值创建,指向字符串常量池中的同一个对象;str3 虽然是通过 new 关键字创建,但 equals 方法比较的是内容,所以 str1.equals(str2)str1.equals(str3) 都返回 true

  1. equalsIgnoreCase(String anotherString) 方法 equalsIgnoreCase(String anotherString) 方法用于忽略大小写比较两个字符串的内容。例如:
String str1 = "Java";
String str2 = "java";
System.out.println(str1.equalsIgnoreCase(str2)); // true

在上述代码中,虽然 str1str2 大小写不同,但 equalsIgnoreCase 方法返回 true

  1. compareTo(String anotherString) 方法 compareTo(String anotherString) 方法用于按字典顺序比较两个字符串。它返回一个整数值:
    • 如果当前字符串小于参数字符串,返回负整数。
    • 如果当前字符串等于参数字符串,返回 0。
    • 如果当前字符串大于参数字符串,返回正整数。

例如:

String str1 = "apple";
String str2 = "banana";
System.out.println(str1.compareTo(str2)); // 负数,因为 'a' 在 'b' 之前

字符串查找

  1. indexOf(int ch) 方法 indexOf(int ch) 方法用于查找指定字符在字符串中第一次出现的索引位置。如果找不到,则返回 -1。例如:
String str = "Hello, World!";
int index = str.indexOf('o');
System.out.println("字符 'o' 第一次出现的索引为: " + index);

在上述代码中,str.indexOf('o') 返回字符 ‘o’ 在字符串 “Hello, World!” 中第一次出现的索引位置,即 4。

  1. indexOf(String str) 方法 indexOf(String str) 方法用于查找指定子字符串在字符串中第一次出现的索引位置。例如:
String str = "Hello, World! Hello, Java!";
int index = str.indexOf("Hello");
System.out.println("子字符串 'Hello' 第一次出现的索引为: " + index);

在上述代码中,str.indexOf("Hello") 返回子字符串 “Hello” 在字符串中第一次出现的索引位置,即 0。

  1. lastIndexOf(int ch) 方法 lastIndexOf(int ch) 方法用于查找指定字符在字符串中最后一次出现的索引位置。例如:
String str = "Hello, World!";
int index = str.lastIndexOf('o');
System.out.println("字符 'o' 最后一次出现的索引为: " + index);

在上述代码中,str.lastIndexOf('o') 返回字符 ‘o’ 在字符串 “Hello, World!” 中最后一次出现的索引位置,即 8。

  1. lastIndexOf(String str) 方法 lastIndexOf(String str) 方法用于查找指定子字符串在字符串中最后一次出现的索引位置。例如:
String str = "Hello, World! Hello, Java!";
int index = str.lastIndexOf("Hello");
System.out.println("子字符串 'Hello' 最后一次出现的索引为: " + index);

在上述代码中,str.lastIndexOf("Hello") 返回子字符串 “Hello” 在字符串中最后一次出现的索引位置,即 13。

字符串截取

  1. substring(int beginIndex) 方法 substring(int beginIndex) 方法用于截取从指定索引开始到字符串末尾的子字符串。例如:
String str = "Hello, World!";
String subStr = str.substring(7);
System.out.println("截取的子字符串为: " + subStr);

在上述代码中,str.substring(7) 从索引 7 开始截取,返回 “World!”。

  1. substring(int beginIndex, int endIndex) 方法 substring(int beginIndex, int endIndex) 方法用于截取从指定开始索引(包括)到指定结束索引(不包括)的子字符串。例如:
String str = "Hello, World!";
String subStr = str.substring(0, 5);
System.out.println("截取的子字符串为: " + subStr);

在上述代码中,str.substring(0, 5) 从索引 0 开始截取到索引 4(不包括索引 5),返回 “Hello”。

字符串替换

  1. replace(char oldChar, char newChar) 方法 replace(char oldChar, char newChar) 方法用于将字符串中所有的指定旧字符替换为新字符。例如:
String str = "Hello, World!";
String newStr = str.replace('o', '0');
System.out.println("替换后的字符串为: " + newStr);

在上述代码中,str.replace('o', '0') 将字符串 “Hello, World!” 中的所有 ‘o’ 替换为 ‘0’,返回 “Hell0, W0rld!”。

  1. replace(CharSequence target, CharSequence replacement) 方法 replace(CharSequence target, CharSequence replacement) 方法用于将字符串中所有的指定旧子字符串替换为新子字符串。例如:
String str = "Hello, World! Hello, Java!";
String newStr = str.replace("Hello", "Hi");
System.out.println("替换后的字符串为: " + newStr);

在上述代码中,str.replace("Hello", "Hi") 将字符串中的所有 “Hello” 替换为 “Hi”,返回 “Hi, World! Hi, Java!”。

字符串分割

  1. split(String regex) 方法 split(String regex) 方法用于根据指定的正则表达式将字符串分割为字符串数组。例如:
String str = "apple,banana,orange";
String[] parts = str.split(",");
for (String part : parts) {
    System.out.println(part);
}

在上述代码中,str.split(",") 根据逗号 “,” 将字符串 “apple,banana,orange” 分割为字符串数组,数组元素为 “apple”、“banana” 和 “orange”。

  1. split(String regex, int limit) 方法 split(String regex, int limit) 方法用于根据指定的正则表达式将字符串分割为字符串数组,并限制分割的次数。例如:
String str = "apple,banana,orange";
String[] parts = str.split(",", 2);
for (String part : parts) {
    System.out.println(part);
}

在上述代码中,str.split(",", 2) 最多分割一次,返回的字符串数组包含两个元素:“apple” 和 “banana,orange”。

字符串转换

  1. toLowerCase() 方法 toLowerCase() 方法用于将字符串中的所有字符转换为小写。例如:
String str = "HELLO";
String lowerStr = str.toLowerCase();
System.out.println("转换为小写后的字符串为: " + lowerStr);

在上述代码中,str.toLowerCase() 将字符串 “HELLO” 转换为 “hello”。

  1. toUpperCase() 方法 toUpperCase() 方法用于将字符串中的所有字符转换为大写。例如:
String str = "hello";
String upperStr = str.toUpperCase();
System.out.println("转换为大写后的字符串为: " + upperStr);

在上述代码中,str.toUpperCase() 将字符串 “hello” 转换为 “HELLO”。

  1. trim() 方法 trim() 方法用于去除字符串两端的空白字符(包括空格、制表符等)。例如:
String str = "   Hello, World!   ";
String trimmedStr = str.trim();
System.out.println("去除两端空白后的字符串为: " + trimmedStr);

在上述代码中,str.trim() 去除字符串 “ Hello, World! ” 两端的空白字符,返回 “Hello, World!”。

字符串格式化

Java 提供了 String.format() 方法用于格式化字符串,类似于 C 语言中的 printf 函数。String.format() 方法使用格式字符串和参数列表来创建格式化后的字符串。

基本格式化

  1. 格式化整数
int num = 123;
String formattedStr = String.format("整数: %d", num);
System.out.println(formattedStr);

在上述代码中,%d 是格式说明符,表示整数类型。String.format("整数: %d", num) 将整数 num 格式化为字符串 “整数: 123”。

  1. 格式化浮点数
double pi = 3.14159;
String formattedStr = String.format("圆周率: %.2f", pi);
System.out.println(formattedStr);

在上述代码中,%.2f 表示将浮点数格式化为保留两位小数的形式。String.format("圆周率: %.2f", pi) 将浮点数 pi 格式化为字符串 “圆周率: 3.14”。

  1. 格式化字符串
String name = "Alice";
String formattedStr = String.format("名字: %s", name);
System.out.println(formattedStr);

在上述代码中,%s 是字符串格式说明符。String.format("名字: %s", name) 将字符串 name 格式化为字符串 “名字: Alice”。

格式化日期和时间

Java 8 引入了新的日期和时间 API(java.time 包),可以更方便地进行日期和时间的格式化。例如:

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class DateTimeFormatExample {
    public static void main(String[] args) {
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String formattedDateTime = now.format(formatter);
        System.out.println("格式化后的日期时间: " + formattedDateTime);
    }
}

在上述代码中,LocalDateTime.now() 获取当前日期和时间。DateTimeFormatter.ofPattern("yyyy - MM - dd HH:mm:ss") 创建一个日期时间格式化模式,now.format(formatter) 将当前日期和时间格式化为指定的字符串形式。

字符串性能优化

在处理字符串时,性能是一个需要考虑的重要因素。由于字符串的不可变性,一些操作可能会导致大量的临时字符串对象创建,从而影响性能。

使用 StringBuilderStringBuffer

  1. StringBuilder StringBuilder 类用于创建可变的字符串。它提供了一系列方法来高效地构建字符串,避免了频繁创建临时字符串对象。例如:
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10; i++) {
    sb.append(i);
}
String result = sb.toString();
System.out.println(result);

在上述代码中,StringBuilderappend 方法将数字追加到 StringBuilder 对象中,最后通过 toString 方法将 StringBuilder 对象转换为字符串。这种方式比使用 + 运算符连接字符串更高效,因为 + 运算符会在每次连接时创建新的字符串对象。

  1. StringBuffer StringBuffer 类与 StringBuilder 类似,也是用于创建可变字符串。但 StringBuffer 是线程安全的,它的方法都使用了 synchronized 关键字进行同步。由于线程同步会带来一定的性能开销,所以在单线程环境下,StringBuilder 的性能更好;而在多线程环境下,需要使用 StringBuffer 来保证线程安全。例如:
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 10; i++) {
    sb.append(i);
}
String result = sb.toString();
System.out.println(result);

在上述代码中,StringBuffer 的使用方式与 StringBuilder 类似,但适用于多线程环境。

避免不必要的字符串操作

  1. 减少字符串拼接次数 尽量减少在循环中使用 + 运算符进行字符串拼接。例如:
// 不推荐的方式
String result = "";
for (int i = 0; i < 1000; i++) {
    result = result + i;
}

// 推荐的方式
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append(i);
}
String result2 = sb.toString();

在上述代码中,第一种方式在每次循环中都创建新的字符串对象,性能较差;而第二种方式使用 StringBuilder 避免了频繁创建临时字符串对象,性能更好。

  1. 合理使用字符串常量 尽量使用字符串常量进行操作,因为字符串常量在编译时就确定了,并且会被放入字符串常量池中,可以提高性能和节省内存。例如:
String str1 = "Hello" + "World"; // 编译时优化,等同于 "HelloWorld"
String str2 = "Hello";
String str3 = str2 + "World"; // 运行时拼接,会创建新的字符串对象

在上述代码中,str1 的拼接在编译时就完成了,不会在运行时创建额外的字符串对象;而 str3 的拼接在运行时进行,会创建新的字符串对象。

总结字符串处理要点

在 Java 中处理字符串时,要充分理解字符串的不可变性以及各种常用方法的特性。合理使用字符串的创建方式、比较方法、查找和截取等操作,能够高效地实现字符串相关的功能。同时,在涉及大量字符串操作时,要注意性能优化,优先使用 StringBuilderStringBuffer 来避免频繁创建临时字符串对象。在格式化字符串时,掌握 String.format() 方法以及日期时间格式化的技巧,可以满足不同的格式化需求。通过对这些字符串处理知识的深入理解和灵活运用,能够编写出更高效、健壮的 Java 程序。

在实际项目开发中,字符串处理无处不在。例如在 Web 开发中,接收和处理用户输入的数据、处理 JSON 或 XML 格式的字符串;在数据处理领域,对文本文件中的数据进行提取、转换等操作都离不开字符串处理。因此,熟练掌握 Java 字符串处理的方法和技巧是非常重要的。

在多线程环境下使用字符串相关类时,要注意线程安全问题。如果需要在多线程中共享字符串操作对象,应该使用 StringBuffer 而不是 StringBuilder。同时,在处理大量字符串数据时,也要考虑内存的使用情况,避免因创建过多的字符串对象而导致内存溢出。

总之,深入理解和熟练运用 Java 字符串处理的各种方法和技术,对于编写高质量的 Java 程序至关重要。无论是小型的命令行工具,还是大型的企业级应用,字符串处理都是不可或缺的一部分。通过不断实践和优化,能够在字符串处理方面做到游刃有余,提升程序的整体性能和稳定性。