摘要
本文主要介绍 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
对字符串的操作
对字符串的操作可以分为以下几类:
-
创建:
String s1 = "Hello World";//字面量直接创建 String s2 = new String("Hello World");//new 一个 String,放在堆上 String oneStr = String.valueOf(1);// 用其他数据类型转换创建
-
拼接:
// 用 + 号拼接 String s1 = "Hello" + " World";
-
拆分:
// 按指定字符串切分 String str = "apple,banana,cherry"; String[] parts = str.split(","); for (String part : parts) { System.out.println(part); }
-
子字符串:
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
-
查找:
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
-
替换修改:
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,删除所有小写字母
-
格式化:
String formattedString = String.format("Integer: %d", 2); System.out.println(formattedString); // 输出:Integer: 42
-
大小写转换:
String str = "Hello"; System.out.println(str.toLowerCase());//hello System.out.println(str.toUpperCase());//HELLO
-
去除指定字符:
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;
}