摘要

本文主要介绍 Java String 相关的 API 和用法,字符串的操作。

String 核心知识点

Java 中字符串是不可变的,一经创建就无法再修改。所有对原有字符串的删除替换拼接等操作,都不会修改原有字符串,而是产生一个新的字符串。

并且为了减少内存占用,用字面量直接创建时会在字符串常量池中查找,如果存在相同的,直接返回引用。如下例所示。

// 用字面量创建的字符串,会先在字符串常量池中查找,如果有相同的,就返回引用  
String s1 = "Hello World";  
String s2 = "Hello World";  
System.out.println("s1 == s2 ? " + (s1 == s2));// true  
  
// 用 new String() 创建的字符串会创建在堆内存上  
String s3 = new String("Hello World");  
System.out.println("s1 == s3 ? " + (s1 == s3));// false

对字符串的操作

对字符串的操作可以分为以下几类:

  1. 创建:

    String s1 = "Hello World";//字面量直接创建
    String s2 = new String("Hello World");//new 一个 String,放在堆上
    String oneStr = String.valueOf(1);// 用其他数据类型转换创建
    
  2. 拼接:

    // 用 + 号拼接
    String s1 = "Hello" + " World";
    
  3. 拆分:

    // 按指定字符串切分
    String str = "apple,banana,cherry";  
    String[] parts = str.split(",");  
    for (String part : parts) {  
        System.out.println(part);  
    }
    
  4. 子字符串:

    substring 函数,传入开始和结束位置,截取位置左闭右开原则,即包含开始位,不包含结束位置

    String str = "Hello, world!";  
    String subStr = str.substring(7);  
    System.out.println(subStr);  // 输出:world!
    String subStr = str.substring(7, 12);
    System.out.println(subStr); // 输出:world
    
  5. 查找:

    String str = "Hello";  
    System.out.println(str.contains("ll"));//true  
    System.out.println(str.startsWith("H"));//true  
    System.out.println(str.endsWith("o"));//true  
    // 定位字符位置  
    System.out.println(str.indexOf("l"));//2  
    System.out.println(str.lastIndexOf("l"));//3  
    // 从位置获取  
    System.out.println(str.charAt(0));//H  
    System.out.println(str.charAt(str.length() - 1));//o
    
  6. 替换修改:

    String str = "Hello";  
    System.out.println(str.replace("H","h"));//hello  
    System.out.println(str.replace("llo",""));//He,用来删除
    System.out.println(str.replaceAll("[a-z]", ""));//H,删除所有小写字母
    
  7. 格式化:

    String formattedString = String.format("Integer: %d", 2);
    System.out.println(formattedString);  // 输出:Integer: 42
    
  8. 大小写转换:

    String str = "Hello";  
    System.out.println(str.toLowerCase());//hello  
    System.out.println(str.toUpperCase());//HELLO
    
  9. 去除指定字符:

    System.out.println("  hello  ".trim());//hello
    

StringBuilder 和 StringBuffer 的用法

因为 String 一经创建就无法修改,对于需要频繁修改字符串时,应当使用 StringBuilder 和 StringBuffer。

  • StringBuilder 线程不安全,但是效率高,适合单线程情况下使用
  • StringBuffer 线程安全,适合多线程修改同一个字符串的情况

具体的实现原理可以查看后文的源码分析部分。

StringsUtils 工具库

StringsUtils 是 Apache 的字符串处理工具库,封装常见的字符串复杂处理。并且 null 安全(即传入的字符串为 null,也不会导致 Null Point Exception)。

  • IsEmpty 判断字符串是否为空字符串,即""
  • IsBlank - 检查字符串是否为空白字符串,例如"\n\t"这种空白字符串返回 true
  • Trim/Strip - 删除前导和尾随空格
  • Equals/Compare - 以一种空安全的方式比较两个字符串
  • startsWith - 以一种 null 安全的方式检查字符串是否以前缀开头
  • endsWith - 以一种 null 安全的方式检查字符串是否以后缀结尾
  • IndexOf/LastIndexOf/Contains - null 安全的索引检查
  • IndexOfAny/LastIndexOfAny/IndexOfAnyBut/LastIndexOfAnyBut - 检索一组字符串中的任何一个的索引
  • ContainsOnly/ContainsNone/ContainsAny - 检查字符串是否仅包含/不含/包含任何这些字符
  • Substring/Left/Right/Mid - 空安全的子串提取
  • SubstringBefore/SubstringAfter/SubstringBetween - 相对于其他字符串的子串提取
  • Split/Join - 将字符串拆分为子字符串数组,反之亦然
  • Remove/Delete - 删除字符串的一部分
  • Replace/Overlay - 在字符串中搜索并用另一个字符串替换
  • Chomp/Chop - 删除字符串的最后一部分
  • AppendIfMissing - 如果不存在,则在字符串末尾附加后缀
  • PrependIfMissing - 如果不存在,则在字符串开头添加前缀
  • LeftPad/RightPad/Center/Repeat - 填充字符串
  • UpperCase/LowerCase/SwapCase/Capitalize/Uncapitalize - 更改字符串的大小写
  • CountMatches - 计算一个字符串在另一个字符串中出现的次数
  • IsAlpha/IsNumeric/IsWhitespace/IsAsciiPrintable - 检查字符串中的字符
  • DefaultString - 防止空输入字符串
  • Rotate - 旋转(循环移位)字符串
  • Reverse/ReverseDelimited - 反转字符串
  • Abbreviate - 使用省略号或另一个给定的字符串缩写字符串
  • Difference - 比较字符串并报告它们的差异
  • LevenshteinDistance - 将一个字符串变为另一个字符串所需的改变次数

源码分析

String

String 类 是 final 的,是不可变的

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    }

StringBuilder 和 StringBuffer

StringBuilder 和 StringBuffer 类,主要方法就是 添加字符串的 append,两者都继承自 AbstractStringBuilder。append 的主要实现在父类中。StringBuilder 和 StringBuffer 中的 append 都是调用父类的方法,只是 StringBuffer 为了线程安全,每个方法都使用了 synchronized 加锁。

StringBuilder 线程不安全

@Override
public StringBuilder append(CharSequence s) {
    super.append(s);
    return this;
}

StringBuffer 是线程安全的,可以看到所有的 append 方法中都使用了 synchronized 关键字

@Override
public synchronized StringBuffer append(CharSequence s) {
    toStringCache = null;
    super.append(s);
    return this;
}

参考资料