JavaSE5️⃣核心类 - String 相关类
1、String
String:字符串
Java 最常用的引用类型之一。
1.1、简介
底层实现:不可变、私有的字符数组(Java 9+ 改成字节数组)
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[];
}
不可变性
- 现象:
- 字符串对象创建后无法改变。
- 任何对字符串的修改,底层都会创建新的字符串对象并返回引用。
- 原因:
- value 数组:final 修饰、私有、没有提供修改方法。
- String 类:final 修饰,无法被子类继承。
- 特点:
- 优点:线程安全、节省空间(字符串常量池)
- 缺点:修改时频繁创建新的 String 对象
1.1.1、创建方式
字面量 | new | |
---|---|---|
含义 | 双引号 "" 包围 |
new String(参数) |
存储位置 | 常量池 | 堆 |
创建过程 | 先检查常量池中是否存在,是则返回引用,否则创建并放在常量池中 | 每次创建一个全新的字符串对象 |
说明 | 允许 JVM 优化内存分配,节省空间 | 参数可以是字符串、字符数组、字节数组 |
使用
String.intern()
可主动将字符串对象放入常量池。若常量池中已有该对象,直接返回对象引用。
否则:
- JDK 1.6-:将对象副本放入串池,返回副本对象引用。
- JDK 1.7+:将对象放入串池,返回对象引用。
1.1.2、特殊值(空)
特殊的字符串值
-
空值(
null
):任何引用对象都可指向 null,表示不存在。 -
空串(
""
):值为空的有效字符串对象。 -
空格串(
" "
):值为若干个空格。String str1 = "ABC"; String str2 = null; String str3 = "";
1.1.3、转义字符
转义字符:
\
将字符串中的特殊符号进行转义,避免程序识别错误。
-
常用转义字符:
特殊符号 对应转义字符 "
\"
'
\'
\
\\
换行符 \n
回车符 \r
缩进符 \t
Unicode 字符 \u####
-
示例:
// 三个字符:A 换行符 好 String str = "A\n\u597D";
1.1.4、多行字符串
两种书写形式
-
拼接:使用
+
拼接多个字符串。 -
文本块:(Java 13)使用一对
"""
包围字符串内容。String str1 = "SELECT * " + "FROM tb_user" + "WHERE id = 1;"; String str2 = """ SELECT * FROM tb_user WHERE id = 1; """;
1.2、基础操作
1.2.1、比较
字符串比较
- 比较地址:
==
- 比较内容:
equals()
- 忽略大小写:
euqalsIgnoreCase()
示例:对于内容相同的字符串,分析
==
的结果。
-
字面量:s1 和 s2 是对常量池中同一个字符串对象的引用。
String s1 = "hello"; String s2 = "hello"; System.out.println(s1.equals(s2)); // true System.out.println(s1 == s2); // true
-
字面量 & new:s1 和 s2 分别在常量池和堆中,是不同的对象。
String s1 = "hello"; String s2 = new String("hello"); System.out.println(s1.equals(s2)); // true System.out.println(s1 == s2); // false
-
new:s1 和 s2 均创建了新的字符串对象,是不同的对象。
String s1 = new String("hello"); String s2 = new String("hello"); System.out.println(s1.equals(s2)); // true System.out.println(s1 == s2); // false
1.2.2、判空
-
isEmpty():(Java 6)判断字符串长度是否为 0,即空串(
""
)。"".isEmpty(); // true " ".isEmpty(); // false "\n".isEmpty(); // false
-
isBlank():(Java 11)判断字符串是否空白。
-
空串(
""
)、空格串(" "
)、缩进符\t
,回车符\r
,换行符\n
。 -
可理解为是否包含有意义字符。
"".isBlank(); // true " ".isEmpty(); // true "\n".isBlank(); // true
-
1.2.3、分割
分割字符串
-
方法签名:传入正则表达式,返回分割后的字符串数组。
public String[] split(String regex) {}
-
示例:
String s = "A,B,C"; String[] result = s.split(","); // ["A", "B", "C"]
2、类型转换
2.1、其它 → String (valueOf)
String.valueOf()
:重载的静态方法引用类型、基本类型、字符数组 → String
2.1.1、引用类型
实现逻辑:
对象.toString()
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
2.1.2、基本类型
-
数值:实现逻辑是
包装类型.toString(xxx)
。public static String valueOf(int i) {} public static String valueOf(long l) {} public static String valueOf(float f) {} public static String valueOf(double d) {}
-
布尔:
public static String valueOf(boolean b) { return b ? "true" : "false"; }
-
字符:实现逻辑是
new String()
。public static String valueOf(char c) { char data[] = {c}; return new String(data, true); }
2.1.3、字符数组
本质:
new String()
的重载方法。
// 整个数组
public static String valueOf(char data[]) {
return new String(data);
}
// 指定起始索引和字符个数
public static String valueOf(char data[], int offset, int count) {
return new String(data, offset, count);
}
2.2、String → 包装类 (parseXxx)
各包装类中定义了
parseXxx()
静态方法。(
Character
类除外)
-
整数型:
-
Byte、Short、Integer、Long 类中定义。
-
重载方法:默认十进制、指定进制。
-
Integer 和 Long 支持无符号转换。
// 有符号转换:四种类型 public static 基本类型 parseXxx(String s) throws NumberFormatException {} public static 基本类型 parseXxx(String s, int radix) throws NumberFormatException {} // 无符号转换:Integer、Long public static 基本类型 parseUnsignedXxx(String s) throws NumberFormatException {} public static 基本类型 parseUnsignedXxx(String s, int radix) throws NumberFormatException {}
-
-
浮点型:Float 和 Double 类中定义。
public static 基本类型 parseXxx(String s) throws NumberFormatException {}
-
布尔型:Boolean 类中定义。
public static boolean parseBoolean(String s) {}
2.3、字符数组
① new String(char[])
char[] → String:String 的构造方法。
-
方法签名:
-
说明:对 char[] 的修改不会影响 String。
-
原因:不会直接引用传入的 char[],而是拷贝一份相同内容的数组,使用其副本。
public String(char value[]) {} public String(char value[], int offset, int count) {}
-
-
示例:
char[] chs = {'a', 'b', 'c', 'd'}; String s1 = new String(chs); // abcd String s2 = new String(chs, 0, 2); // ab
② toCharArray()
String → char[]:String 定义的实例方法。
-
方法签名:
public char[] toCharArray() {}
-
示例:
String str = "Hello"; str.toCharArray(); // ['H', 'e', 'l', 'l', 'o']
3、子串操作
Hint:索引从 0 开始
3.1、搜索
3.1.1、索引位置
-
方法签名:
// 首次出现 public int indexOf(String str) {} // 最后一次出现 public int lastIndexOf(String str) {}
-
示例:
"Hello".indexOf("l"); // 2 "Hello".lastIndexOf("l"); // 3
3.1.2、判断包含
-
方法签名:形参是
CharSequence
接口,String 是其实现类。public boolean contains(CharSequence s) {}
-
示例:
"Hello".contains("lo"); // true "Hello".contains("ol"); // false
3.1.3、判断前后缀
-
方法签名:
// 是否以prefix开头 public boolean startsWith(String prefix) {} // 是否以suffix结尾 public boolean endsWith(String suffix) {}
-
示例:
"Hello".startsWith("He"); // true "Hello".endsWith("lo"); // true
3.2、提取
3.2.1、子串
-
方法签名:
// 指定索引之后 public String substring(int beginIndex) {} // 指定索引范围(前闭后开) public String substring(int beginIndex, int endIndex){}
-
示例:
"Hello".substring(2); // "llo" "Hello".substring(2, 4); // "ll"
3.2.2、移除首尾空白字符
两种方式
-
trim()
:包含英文空格\t
,回车符\r
,换行符\n
。" \tHello\r\n ".trim(); // "Hello"
-
strip()
:(Java 11+)在trim()
的基础上,移除中文空格字符\u3000
。"\u3000Hello\u3000".strip(); // "Hello" " Hello ".stripLeading(); // "Hello " " Hello ".stripTrailing(); // " Hello"
3.3、替换子串
3.3.1、字符/字符串
-
方法签名:
// 替换掉所有oldChar public String replace(char oldChar, char newChar) {} // 替换掉所有target public String replace(CharSequence target, CharSequence replacement) {}
-
示例:
String s = "hello"; s.replace('l', 'x'); // hexxo s.replace("l", "xx"); // hexxxxo
3.3.2、正则表达式
-
方法签名:
// 替换掉首次匹配的 regex public String replaceFirst(String regex, String replacement) {} // 替换掉所有匹配的 regex public String replaceAll(String regex, String replacement) {}
-
示例:
String s2 = "A,,B;C ,D"; s2.replaceFirst("[,;\\s]+", ","); // "A,B;C ,D" s2.replaceAll("[,;\\s]+", ","); // "A,B,C,D"
4、字符串拼接
4.1、运算符 +
使用
+
连接多个任意类型的变量。连接之前,其它数据类型自动转型为
String
。
-
拼接原理:
常量拼接 变量拼接 含义 常量(含 final 变量)之间的拼接 涉及变量的拼接 原理 编译期优化 StringBuilder.append() 具体过程 编译器拼接所有常量作为一个对象,检查常量池中是否已有该对象,是则返回引用,否则放入 -
示例:
String str = "A" + "B" +"C"; // 一个对象: ABC
4.2、concat()
String 实例方法
-
方法源码:创建一个新的 String 对象。
public String concat(String str) { int otherLen = str.length(); if (otherLen == 0) { return this; } int len = value.length; char buf[] = Arrays.copyOf(value, len + otherLen); // 将str复制到buf数组之后 str.getChars(buf, len); return new String(buf, true); }
-
示例:
String str1 = "ABC"; String str2 = str1.concat("DEF");
4.3、Buffer/Builder
4.3.1、使用场景
在频繁拼接字符串的场景下,
使用
+
或concat()
有以下缺点。
- 每次拼接都创建新的对象,丢弃旧的对象。
- 大量临时对象,浪费内存,降低 GC 效率。
4.3.2、API
① 共同点
StringBuffer & StringBuilder 共同点
抽象类 AbstractStringBuilder 的子类
-
可变对象:预分配缓存区,长度不足时自动扩容。
// 存储元素 char[] value; // 记录数组已使用字符个数 int count;
-
链式编程:方法返回值是
this
,可链式调用。
② 对比
String | StringBuffer | StringBuilder | |
---|---|---|---|
版本 | Java 1.0 | Java 1.0 | Java 1.5 |
可变性 | ❌ | ✔ | ✔ |
特点 | 任何操作都创建新对象,效率低 | 线程安全,效率低 ( synchronized 方法) |
线程不安全,效率高 |
存储位置 | 字符串常量池 | 堆 | 堆 |
4.4、Joiner
4.4.1、使用场景
StringJoiner(Java 8+)
场景:使用分隔符连接数组元素。
-
作用:用于构造字符序列。
- 由分隔符分割。
- 可指定前后缀。
-
示例:
String[] nameList = {"Alice", "Beta", "Cindy"}; StringJoiner joiner = new StringJoiner(", ", "---", "!---"); for (String name : nameList) { joiner.add(name); } System.out.println(joiner.toString()); // ---Alice, Beta, Cindy!---
4.4.2、StringJoiner 源码
① 成员变量
-
前缀、后缀、分隔符(String)
-
字符串值(StringBuilder)
-
空值(String,默认是前后缀直接拼接)
private final String prefix; private final String delimiter; private final String suffix; private StringBuilder value; private String emptyValue;
② 构造方法
-
指定分隔符、前后缀:
public StringJoiner(CharSequence delimiter, CharSequence prefix, CharSequence suffix) { // 判空 Objects.requireNonNull(prefix, "The prefix must not be null"); Objects.requireNonNull(delimiter, "The delimiter must not be null"); Objects.requireNonNull(suffix, "The suffix must not be null"); this.prefix = prefix.toString(); this.delimiter = delimiter.toString(); this.suffix = suffix.toString(); this.emptyValue = this.prefix + this.suffix; }
-
指定分隔符:前后缀默认为空串。
public StringJoiner(CharSequence delimiter) { this(delimiter, "", ""); }
③ 拼接
prepareBuilder():add() 和 merge() 的预处理,
保证存储元素的 StringBuilder 是正确的。
-
value 非空:直接拼接分隔符。
-
value 空:创建 StringBuilder 对象,拼接前缀。
private StringBuilder prepareBuilder() { if (value != null) { value.append(delimiter); } else { value = new StringBuilder().append(prefix); } return value; }
add():连接一个 CharSequence 变量。
-
预处理
-
拼接内容
public StringJoiner add(CharSequence newElement) { prepareBuilder().append(newElement); return this; }
merge():连接一个 Joiner 内容,不包含前后缀。
-
判空
-
保存 other 的长度,预处理 builder
-
连接 other 的内容。
public StringJoiner merge(StringJoiner other) { Objects.requireNonNull(other); if (other.value != null) { final int length = other.value.length(); StringBuilder builder = prepareBuilder(); // 追加内容, 起始索引, 长度 builder.append(other.value, other.prefix.length(), length); } return this; }
④ toString()
返回当前 value 值
-
value 为空:直接返回 emptyValue。
-
value 非空:
-
后缀为空:直接返回 value。
-
后缀非空:拼接后缀,设置 value 的正确长度。
@Override public String toString() { if (value == null) { return emptyValue; } else { if (suffix.equals("")) { return value.toString(); } else { int initialLength = value.length(); String result = value.append(suffix).toString(); // reset value to pre-append initialLength value.setLength(initialLength); return result; } } }
-
⑤ 其它
-
setEmptyValue():设置空值,取代默认的前后缀拼接。
public StringJoiner setEmptyValue(CharSequence emptyValue) { this.emptyValue = Objects.requireNonNull(emptyValue, "The empty value must not be null").toString(); return this; }
-
length():返回字符串长度,等价于
toString().length()
。public int length() { return (value != null ? value.length() + suffix.length() : emptyValue.length()); }
4.4.3、String.join()
本质:仅指定分割符的 StringJoiner。
若无需指定前后缀,使用
String.join()
更方便。
-
方法源码:实例化
StringJoiner
,仅指定分隔符。public static String join(CharSequence delimiter, CharSequence... elements) { // 判空 Objects.requireNonNull(delimiter); Objects.requireNonNull(elements); // 字符串连接对象 StringJoiner joiner = new StringJoiner(delimiter); for (CharSequence cs: elements) { joiner.add(cs); } return joiner.toString(); }
-
示例:
String[] arr = {"A", "B", "C"}; String s = String.join("***", arr); // "A***B***C"
4.5、场景小结
- 无需拼接,使用
String
。 - 非循环体拼接,使用
+
或concat()
。 - 循环体拼接/频繁拼接,使用
StringBuilder
(并发场景则使用StringBuffer
)。 - 分隔符连接,使用
StringJoiner
。