Java String 类及相关工具

Java String 类及相关工具

在 Java 中,String 类是最常用的类之一,用于处理文本数据等。此外,Java 还提供了 StringBufferStringBuilderStringJoiner 等工具类,以满足不同场景下的字符串操作需求。

String 类

String 类的核心特性

  • 不可变性String 对象一旦创建,其内容(底层存储的字符序列)无法修改。任何看似“修改”字符串的操作(如拼接、替换),本质都是创建新的 String 对象。
  • 底层存储String 底层基于 char[] 数组存储字符序列(Java 9 及以上优化为 byte[],根据编码自适应存储,节省内存)。
  • 引用存储String 变量存储的是对 String 对象的引用(内存地址),而非字符串本身;String 对象存储在堆内存或字符串常量池中。

String 对象的创建方式

String 对象有三种常见创建方式,分别适用于不同场景:

方式1:通过字符串直接量创建

Java 将字符串直接量视为 String 对象,可直接赋值给 String 变量,该对象会优先存储在字符串常量池(提高复用性,节约内存)。

// 直接量创建:对象存储在字符串常量池
String message = "welcome to java";

方式2:通过 new 关键字创建

通过 new String(...) 创建对象时,会先在堆内存中创建 String 对象。

// new 关键字创建:堆内存中创建对象,常量池可能同步创建副本
String message1 = new String("welcome to java");

方式3:通过字符数组创建

将字符数组转换为 String 对象,适用于从字符序列构建字符串的场景。

// 字符数组创建
char[] charArray = {'g', 'o', 'o', 'd'};
String message2 = new String(charArray); // 结果:"good"

// 截取字符数组的部分内容创建(参数:字符数组、起始索引、长度)
String message3 = new String(charArray, 1, 2); // 从索引1开始,取2个字符,结果:"oo"

综合示例代码

package com.inherit;

/**
 * @author Jing61
 */
public class StringTest {
    public static void main(String[] args) {
        // 方式1:字符串直接量
        String s1 = "Welcome to java!"; // 结果:"Welcome to java!"
        
        // 方式2:字符数组(完整数组)
        char[] letters = {'H', 'e', 'l', 'l', 'o'};
        String s2 = new String(letters); // 结果:"Hello"
        
        // 方式3:字符数组(部分截取)
        String s3 = new String(letters, 1, 3); // 从索引1取3个字符,结果:"ell"
    }
}

不可变字符串与字符串常量池

不可变性的本质

String 的不可变性源于底层 char[] 数组被 private final 修饰:

  • private:外部无法直接访问数组;
  • final:数组引用无法指向新的数组,且数组长度固定。

示例:看似“修改”实则创建新对象

String s = "java"; // 创建对象1:内容为"java",s指向该对象
s = "HTML";        // 创建对象2:内容为"HTML",s指向新对象
// 此时对象1仍存在于内存中,但无引用指向,后续会被垃圾回收

字符串常量池(String Constant Pool)

为提高效率、节约内存,Java 虚拟机(JVM)维护一个字符串常量池,存储具有相同字符序列的 String 对象(即“限定字符串”),确保相同直接量仅创建一个对象。

常量池的关键特性

  • 直接量自动入池:通过字符串直接量创建的对象,会自动加入常量池(若池中已存在相同序列,则直接复用)。
  • new 对象需手动入池:通过 new 创建的对象默认在堆内存,需调用 intern() 方法手动加入常量池(若池中无相同序列,则将对象引用存入;若已有,则返回池中的引用)。

常量池操作示例与分析

示例1:直接量与 new 对象的对比
String s1 = "a";                  // 常量池创建"a",s1指向池对象
String s2 = "b";                  // 常量池创建"b",s2指向池对象
String s3 = "a" + "b";            // 编译器优化为"ab",常量池创建"ab",s3指向池对象
String s4 = s1 + s2;             // s1、s2是引用变量,编译器不优化,堆内存new String("ab"),s4指向堆对象
String s5 = "ab";                // 常量池已存在"ab",s5指向池对象

内存示意图
image

输出结果

System.out.println(s3 == s4);  // false(s3指向池对象,s4指向堆对象)
System.out.println(s3 == s5);  // true(均指向池中的"ab")
示例2:intern() 调用顺序的影响
// 情况1:先创建new对象,再调用intern(),最后直接量创建
String x2 = new String("c") + new String("d");  // 堆内存new String("cd"),x2指向堆对象
String x1 = "cd";                               // 常量池创建"cd",x1指向池对象
x2.intern();                                    // 池已有"cd",无操作
System.out.println(x1 == x2);                   // false(x1指向池,x2指向堆)

image

// 情况2:先调用intern(),再直接量创建
String x2 = new String("c") + new String("d");  // 堆内存new String("cd"),x2指向堆对象
x2.intern();                                    // 池无"cd",将x2的引用存入池
String x1 = "cd";                               // 池已有x2的引用,x1指向x2(堆对象)
System.out.println(x1 == x2);                   // true(x1、x2均指向堆中的"cd")

image

String 类常用方法

String 类提供大量方法用于字符串操作,可分为基本属性、拼接与转换、比较、截取、查找、替换与分割等类别,以下为核心方法详解。

基本属性与转换

public int length():获取字符串当中含有的字符个数,返回字符串长度。
public String concat(String str):将当前字符串和参数字符串str连接,返回值新的字符串。
public char charAt(int index):获取指定索引位置的单个字符。(索引从0开始。)
public String toUpperCase():返回所有字母大写的新字符串
public String toLowerCase():返回所有字母小写的新字符串
public String trim(): 返回去掉两边空白字符的新字符串。
//字符串比较
public boolean equals(String str):如果该字符串等于字符串str,返回true
public boolean equalsIgnoreCase(String str):如果该字符串等于字符串str,返回true.不区分大小写
public int compareTo(String str):返回一个大于0、等于0、小于0的整数,表明一个字符串是否大于、等于或者小于str
public int compareToIgnoreCase(String str):返回一个大于0、等于0、小于0的整数,表明一个字符串是否大于、等于或者小于str。不区分大小写
public boolean startsWith(String prefix): 返回字符串是否以前缀prefix开头
public boolean endsWith(String suffix): 返回字符串是否以后缀suffix结束
public boolean contains(String str): 检查字符串中是否包含子串str
//字符串截取
public String substring(int begin):截取字符串,从特定位置begin的字符开始到字符串结尾。
public String substring(int begin,int end):截取字符串,从特定位置begin的字符开始到end-1的字符。(长度为end - begin)
//字符串查找,提供了几个版本的indexOf和lastIndexOf方法
public int indexOf(String str):查找参数字符串在本字符串当中首次出现str的索引位置,如果没有返回-1值。
public int indexOf(String str,int fromIndex):查找参数字符串在本字符串当中fromIndex之后首次出现str的索引位置,如果没有返回-1值。
public int lastIndexOf(String str):查找参数字符串在本字符串当中最后一个出现str的索引位置,如果没有返回-1值。
public int lastIndexOf(String str,int fromIndex):查找参数字符串在本字符串当中fromIndex之前出现最后一个str的索引位置,如果没有返回-1值。

字符串替换与分割

替换方法

方法签名 功能描述
String replace(String oldStr, String newStr) 替换所有匹配的 oldStrnewStr(不支持正则,仅精确匹配)
String replaceFirst(String regex, String newStr) 替换第一个匹配正则 regex 的子串为 newStr
String replaceAll(String regex, String newStr) 替换所有匹配正则 regex 的子串为 newStr(支持正则表达式)

分割方法

方法签名 功能描述
String[] split(String regex) 按正则 regex 分割字符串,返回分割后的字符串数组;若末尾有分隔符,会忽略空串

示例代码

package com.inherit;

/**
 * @author Jing61
 */
public class StringTest {
    public static void main(String[] args) {
        String token = "我认为他是一个好老师,你觉得他是吗?";
        //将所有的"他"替换为"him"
        String info = token.replace("他", "him");
        System.out.println(info);
        //替换第一个找到的"他"
        System.out.println(token.replaceFirst("他", "him"));
        //替换全部,支持模式匹配(正则表达式)
        System.out.println(token.replaceAll("他", "him"));
        //分割
        String[] tokens = "Linux@Java@Spring".split("@");
        for(String t : tokens)
            System.out.println(t);
    }
}

模式匹配(正则表达式)

String 类的 matches() 方法支持正则表达式匹配,用于验证字符串格式(如手机号、邮箱等)。

示例:验证手机号格式

package com.inherit;

/**
 * @author Jing61
 */
public class StringTest {
    public static void main(String[] args) {
        String reg = "^1[358]\\d{9}$"; // 该正则表达式为1开头,第二位为3、5、8,共2 + 9位
        System.out.println("110".matches(reg)); // 输出为false
        System.out.println("1300".matches(reg));// 输出为false
        System.out.println("17300001111".matches(reg));// 输出为false
        System.out.println("15300001121".matches(reg));// 输出为true

    }
}

正则表达式详解:可参考 菜鸟教程 - Java 正则表达式

字符串与数组之间的转换

//字符串和字符数组进行转换
char[] letters = token.toCharArray();
System.out.println(letters.length);
new String(letters);
System.out.println(new String(letters,0,3));

字符串与数值之间的转换

可以使用Double.parseDouble(str)或者Integer.parseInt(str)将一个字符串转为一个double或者int值,也可以使用字符串的连接操作符将字符或者数值转换为字符串。另外一种将数字转为字符串的方法是使用重载的静态valueOf方法。

StringBuffer 与 StringBuilder

String 的不可变性导致频繁修改字符串时(如循环拼接)会创建大量临时对象,效率低下。StringBufferStringBuilder 作为可变字符串类,可直接修改内部字符序列,避免创建新对象,提高效率。

核心区别与适用场景

特性 StringBuffer StringBuilder
线程安全性 线程安全(方法被 synchronized 修饰) 线程不安全(无同步修饰)
效率 较低(同步开销) 较高(无同步开销)
适用场景 多线程环境(如服务器端并发操作) 单线程环境(如客户端、单线程业务逻辑)

常用方法(以 StringBuilder 为例)

方法签名 功能描述
StringBuilder append(任意类型) 追加内容到字符串末尾,返回对象本身(支持链式调用)
StringBuilder insert(int index, 任意类型) 在索引 index 处插入内容,返回对象本身
StringBuilder delete(int start, int end) 删除索引 startend-1 的内容,返回对象本身
StringBuilder reverse() 反转字符串内容,返回对象本身
String toString() StringBuilder 转换为 String 对象

代码示例

StringBuilder 示例

package com.inherit;

/**
 * @author Jing61
 */
public class StringBuilderTest {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        
        // 链式追加:append返回对象本身,可连续调用
        sb.append("hello").append(" ").append("world");
        System.out.println(sb); // 输出:"hello world"
        
        // 插入内容:在索引5处插入":"
        sb.insert(5, ":");
        System.out.println(sb); // 输出:"hello: world"
        
        // 删除内容:删除索引5~6(左闭右开)的字符(即":")
        sb.delete(5, 6);
        System.out.println(sb); // 输出:"hello world"
        
        // 反转字符串
        sb.reverse();
        System.out.println(sb); // 输出:"dlrow olleh"
        
        // 转换为String
        String result = sb.toString();
        System.out.println(result); // 输出:"dlrow olleh"
    }
}

StringBuffer 示例

package com.inherit;

/**
 * @author Jing61
 */
public class StringBufferTest {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer();
        
        // 追加内容
        sb.append("hello").append(" ").append("world");
        System.out.println(sb); // 输出:"hello world"
        
        // 插入内容
        sb.insert(5, ":");
        System.out.println(sb); // 输出:"hello: world"
        
        // 删除内容
        sb.delete(5, 6);
        System.out.println(sb); // 输出:"hello world"
    }
}

实战:判断回文字符串(忽略非字母数字)

package com.inherit;

/**
 * 判断回文字符串时忽略既非字母又非数字的字符
 *    1、过滤出原始字符串中的字母,组成一个新的字符串
 *    2、将新的字符串进行反转既非字母又非数字
 *    3、将反转后的字符串和过滤后字符串进行比较,如果相等则为回文串
 * @author Jing61
 */
public class Plalindrome {
    public static String filter(String s) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (Character.isLetterOrDigit(c)) sb.append(c);
        }
        return sb.toString(); // new String()
    }

    public static String reverse(String s) {
        StringBuilder sb = new StringBuilder(s);
        return sb.reverse().toString();
    }

    public static boolean isPalindrome(String s) {
        return filter(s).equals(reverse(filter(s)));
    }

    public static void main(String[] args) {
        System.out.println(isPalindrome("A man, a plan, a canal: Panama")); // false
        System.out.println(isPalindrome("race a car"));// false
        System.out.println(isPalindrome(" "));// true
        System.out.println(isPalindrome("Ava  232 avA"));// true
    }
}

StringJoiner 与 String.join()

在需要“分隔符拼接数组/集合”的场景(如将 {"a", "b", "c"} 拼接为 "[a, b, c]"),StringJoinerString.join() 可简化代码,避免手动处理多余分隔符。

StringJoiner

StringJoiner 支持指定分隔符前缀后缀,自动处理分隔符拼接,无需手动删除末尾多余字符。

构造器与核心方法

构造器/方法 功能描述
StringJoiner(CharSequence delimiter) 仅指定分隔符(前缀、后缀默认为空)
StringJoiner(CharSequence delimiter, CharSequence prefix, CharSequence suffix) 指定分隔符、前缀、后缀
StringJoiner add(CharSequence str) 向拼接器中添加字符串,返回对象本身(支持链式调用)
String toString() 返回拼接后的完整字符串

代码示例

package com.inherit;

import java.util.StringJoiner;

/**
 * @author Jing61
 */
public class StringJoinerTest {
    public static void main(String[] args) {
        String[] names = {"Jing61", "Jing6", "Jing1"};
        System.out.println(toString(names));
    }

    public static String toString(String[] names) {
        StringJoiner sj = new StringJoiner(",", "[", "]");// 创建StringJoiner对象, 分割符, 前缀, 后缀
        for (String name : names) sj.add(name);
        return sj.toString();
    }
}

String.join()

String.join()String 类的静态方法,内部基于 StringJoiner 实现,适用于无需前缀/后缀的简单拼接场景,代码更简洁。

代码示例

public static void printArray(String[] names) {
    System.out.println(String.join(", ",names));
}

总结

类/方法 核心特性 适用场景
String 不可变、线程安全、常量池复用 字符串内容固定,无需频繁修改
StringBuilder 可变、线程不安全、效率高 单线程环境下频繁修改字符串
StringBuffer 可变、线程安全、效率较低 多线程环境下频繁修改字符串
StringJoiner 支持分隔符、前缀、后缀,自动处理拼接 复杂格式的数组/集合拼接(如 "[a, b, c]"
String.join() 简化无前后缀的拼接,代码简洁 简单的数组/集合拼接(如 "a, b, c"
posted @ 2025-11-05 10:58  Jing61  阅读(12)  评论(0)    收藏  举报