当前位置: 首页 > news >正文

【Java常用API】----- 字符串 与 链式编程

1. String

1.1 String 概述

  • String 是 Java 定义好的一个类,位于 java.lang 包中,使用时无需手动导入。
  • Java 程序中的所有字符串文字(如 "abcdefg")都是 String 类的对象。
  • String 对象是不可变的,一旦创建,其内容不能被修改。

示例:

  • String name = "尼古拉斯·阿玮"; 创建一个字符串对象;
  • name = "三连加投币·阿玮"; 并非修改原对象,而是将 name 引用指向新的字符串对象;
  • 因此,每次赋值都可能产生新对象,旧对象若无引用则可被垃圾回收。

注意:
字符串拼接、替换等操作看似“修改”,实则返回新字符串,原字符串保持不变

1.2 String 构造方法

1.2.1 代码格式

创建 String 对象主要有两种方式:

  • 直接赋值

    String s1 = "abc";
    
  • 使用 new 关键字调用构造方法

    String s2 = new String("abc");
    

常用构造方法如下:

构造方法说明
public String()创建一个空字符串对象
public String(String original)根据传入的字符串内容创建新对象
public String(char[] chs)根据字符数组创建字符串对象
public String(byte[] chs)根据字节数组创建字符串对象(默认 UTF-8 编码)

1.2.2 内存分析

在这里插入图片描述


1.2.2.1 直接赋值(字符串常量池机制)
public class demo6 {public static void main(String[] args) {String s1 = "abc";String s2 = "abc";}
}

在这里插入图片描述

  • JVM 会检查字符串常量池(StringTable)中是否已存在 "abc"
  • 若存在,则复用该对象,s1s2 指向同一地址;
  • 若不存在,则在常量池中创建新对象。

1.2.2.2 使用 new 创建对象
public class demo6 {public static void main(String[] args) {char[] chs = {'a', 'b', 'c'};String s1 = new String(chs);String s2 = new String(chs);}
}

在这里插入图片描述

  • 每次调用 new String(...) 都会在堆内存中创建一个新的 String 对象;
  • 即使内容相同,也会生成两个独立对象;
  • 但内部字符数组共享(若未复制);

1.3 String 之间的比较

1.3.1 == 比较的本质

在 Java 中,== 运算符的比较行为取决于操作数的数据类型

在这里插入图片描述

关键:
== 对于字符串来说,比较的是“地址是否相同”,而不是“内容是否相等”。


1.3.2 字符串比较

由于 == 只能判断引用是否相同,不能直接比较字符串内容,因此 Java 提供了专门的方法来比较字符串内容。

方法说明
boolean equals(Object obj)比较两个字符串的内容是否完全一致(大小写敏感
boolean equalsIgnoreCase(String str)忽略大小写比较两个字符串的内容

1.3.3 示例分析

// 示例1:使用字面量创建字符串
String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2); // true —— 同一个常量池对象,地址相同
System.out.println(s1.equals(s2)); // true —— 内容相同
// 示例2:使用 new 创建字符串
String s3 = new String("abc");
String s4 = "abc";
System.out.println(s3 == s4); // false —— 不同对象,地址不同
System.out.println(s3.equals(s4)); // true —— 内容相同
// 示例3:用户输入 vs 字面量
Scanner sc = new Scanner(System.in);
String str1 = sc.next(); // 输入 "abc"
String str2 = "abc";
System.out.println(str1 == str2); // false —— 不同对象
System.out.println(str1.equals(str2)); // true —— 内容相同

为什么示例3中 str1 == str2 返回 false

  • str1 是通过 Scanner 读取的字符串,存储在堆中,是一个新对象;
  • str2 是字面量,JVM 会在字符串常量池中查找或创建 "abc"
  • 虽然内容相同,但它们是两个不同的对象,内存地址不同;
  • 所以 == 返回 false

1.3.4 正确的比较做法

使用 equals()equalsIgnoreCase()

// 安全比较字符串内容
if (str1.equals(str2)) {System.out.println("内容相同!");
}// 忽略大小写比较
if (str1.equalsIgnoreCase("ABC")) {System.out.println("忽略大小写后相同!");
}

注意:避免使用 == 比较字符串内容,除非你明确想判断是否为同一个对象。


1.4 String基本操作

1.4.1 遍历

字符串可以通过 charAt(int index) 方法逐个访问字符,实现遍历。

常用方法:
  • public char charAt(int index):返回指定索引位置的字符(从 0 开始)。
  • public int length():返回字符串长度。

注意:

  • 字符串长度通过 .length() 获取,而数组长度是 .length(无括号);
  • 索引越界会抛出 StringIndexOutOfBoundsException
示例:
import java.util.Scanner;public class demo1 {public static void main(String[] args) {Scanner sc = new Scanner(System.in);System.out.println("请输入一个字符串:");String str = sc.next();for (int i = 0; i < str.length(); i++) {char c = str.charAt(i);System.out.println(c);}}
}

输出效果:
在这里插入图片描述


1.4.2 拼接

字符串拼接是常见需求,Java 提供多种方式实现。

常见方式:
  • 使用 + 运算符;
  • 使用 StringBuilder(高效,适用于大量拼接)。
示例:将整型数组按格式拼接为字符串 [1, 2, 3]
public class demo2 {public static void main(String[] args) {int[] arr = {1, 2, 3, 4, 5};System.out.println(subStr(arr));}public static String subStr(int[] arr) {if (arr.length == 0) {return "[]";}String str = "[";for (int i = 0; i < arr.length; i++) {str += arr[i];if (i < arr.length - 1) {str += ",";}}str += "]";return str;}
}

输出结果:在这里插入图片描述

注意:频繁使用 + 拼接字符串效率较低,建议在循环中使用 StringBuilder


1.4.3 截取

substring() 是字符串截取的核心方法。

方法说明:
方法说明
substring(int beginIndex)从指定索引开始,截取到末尾
substring(int beginIndex, int endIndex)截取从 beginIndexendIndex-1 的子串

关键点:包头不包尾,即“左闭右开”

示例:手机号脱敏处理
import java.util.Scanner;public class demo5 {public static void main(String[] args) {Scanner sc = new Scanner(System.in);String phoneNumber;while (true) {System.out.println("请输入手机号码:");phoneNumber = sc.next();if (phoneNumber.length() <= 11) {break;} else {System.out.println("手机号码长度有误,请重新输入:");}}System.out.println(change(phoneNumber));}public static String change(String phoneNumber) {return phoneNumber.substring(0, 3) + "****" + phoneNumber.substring(7, 11);}
}

输入:13812345678 → 输出:138****5678
在这里插入图片描述


1.4.4 替换

replace() 方法用于将字符串中的某一部分内容替换为新内容。

方法说明
  • String replace(String oldChar, String newChar)全局替换所有匹配项。
  • 返回值是新的字符串对象,原字符串不变。

注意:只有返回值才是替换后的结果,必须赋值给变量或直接使用。

示例:敏感词过滤
public class demo5 {public static void main(String[] args) {String talk = "你玩你妈呢,你个傻逼,光送人头!!";String[] arr = {"你妈", "傻逼", "人头"};for (int i = 0; i < arr.length; i++) {talk = talk.replace(arr[i], "***");}System.out.println(talk);}
}

输出结果:在这里插入图片描述

1.4.5 大小写转换

String 提供了便捷的方法用于统一字母的大小写,常用于输入标准化或忽略大小写的匹配场景。

常用方法:
  • public String toLowerCase():将字符串中所有大写字母转为小写;
  • public String toUpperCase():将字符串中所有小写字母转为大写。

注意:

  • 这些方法不会修改原字符串,而是返回一个新字符串;
  • 转换遵循当前系统的语言环境(如需指定 Locale,可使用带参数的重载版本)。
示例:
public class demo9 {public static void main(String[] args) {String str = "Hello World";System.out.println(str.toLowerCase()); System.out.println(str.toUpperCase()); }
}

输出:
在这里插入图片描述


1.4.6 类型转换

在实际开发中,经常需要在字符串与其他数据类型之间相互转换。

常见方式:
场景方法
其他类型 → StringString.valueOf(任意类型) 或 使用 + "" 拼接
String → 基本类型Integer.parseInt(str)Double.parseDouble(str)Boolean.parseBoolean(str)

注意:

  • String.valueOf() 可安全处理 null,返回 "null" 字符串;
  • 将字符串转为数字时,若内容不符合格式(如 "abc"int),会抛出 NumberFormatException
示例:
public class demo10 {public static void main(String[] args) {// 其他类型转 StringString s1 = String.valueOf(123);     // "123"String s2 = 3.14 + "";               // "3.14"// String 转其他类型int num = Integer.parseInt("100");           // 100double d = Double.parseDouble("3.14");       // 3.14boolean b = Boolean.parseBoolean("true");    // trueSystem.out.println(s1);System.out.println(num);}
}

输出:
在这里插入图片描述

1.4.7 案例 —— 金额转换

场景背景

在这里插入图片描述

将用户输入的整数金额(如 123)转换为中文大写格式。

实现思路

在这里插入图片描述

  1. 将数字每一位转为对应中文字符;
  2. 补齐前导零,确保位数统一;
  3. 按照单位(百万、十万、万、千、百、十、元)进行组合;
完整代码
import java.util.Scanner;public class demo3 {public static void main(String[] args) {// 最高七位的金额转换成大写的金额Scanner sc = new Scanner(System.in);int money;// 输入金额,循环直到输入合法(0 ~ 9999999)while (true) {System.out.println("请输入金额:");money = sc.nextInt();if (0 <= money && money <= 9999999) {break;} else {System.out.println("输入金额有误,请重新输入:");}}// 调用 change 方法将金额转换为中文大写,并输出结果System.out.println(change(money));}/*** 将整数金额转换为中文大写形式(按位处理)*/public static String change(int money) {// 定义中文数字字符数组,索引对应0~9String[] arr = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};// 临时变量,用于提取每一位数字int count = money;String Money = "";  // 存储数字对应的中文字符,从低位到高位拼接// 将金额每一位转换为中文数字(从个位开始)while (count > 0) {int ge = count % 10;  // 获取个位数字Money = arr[ge] + Money;  // 将对应中文字符加到前面(实现逆序拼接)count /= 10;  // 去掉个位,继续处理高位}// 补齐前导零,使字符串长度为7(对应最高七位数)// 例如:123 → "零零零零壹贰叁"int size = 7 - Money.length();for (int i = 0; i < size; i++) {Money = "零" + Money;}// 定义单位数组,按位置对应:百、十、万、千、百、十、元String[] unit = {"佰", "拾", "万", "仟", "佰", "拾", "元"};// 按位拼接每个数字和对应的单位String result = "";for (int i = 0; i < Money.length(); i++) {result = result + Money.charAt(i) + unit[i];}return result;  // 返回最终的大写金额字符串}
}

输入:123 → 输出:在这里插入图片描述


2. StringBuilder

在 Java 中,String不可变的(immutable),每次拼接都会创建新的字符串对象,导致内存频繁分配与回收,效率低下。

为了解决这一问题,Java 提供了 StringBuilder 类——一个可变的字符串容器,专为高效字符串操作而设计。


2.1 StringBuilder 的使用场景

  • 需要进行大量字符串拼接的操作(如循环中拼接日志、SQL 字符串等);
  • 字符串频繁修改(插入、删除、反转等);

比如:将数组 [1,2,3] 转换为字符串 [1, 2, 3] —— 使用 StringBuilder+ 快得多!


2.2 StringBuilder 概述

  • StringBuilder 可以看作是一个可变的字符串容器
  • 创建后,其内部内容可以被修改(添加、删除、替换、反转等);
  • 所有操作都直接作用于同一个对象,不会生成新对象
  • 主要作用是:提高字符串操作的性能与效率

2.3 StringBuilder 构造方法

方法说明
public StringBuilder()创建一个空白的可变字符串对象,初始容量为 16 字符
public StringBuilder(String str)根据指定字符串内容创建可变字符串对象
// 初始化为 "abc"
StringBuilder sb2 = new StringBuilder("abc");      

在这里插入图片描述

注意:StringBuilder 是引用类型,必须通过 new 创建对象。


2.4 StringBuilder基本操作

2.4.1 添加(Append)

append() 方法用于将数据追加到当前字符串末尾。

常用方法:
  • public StringBuilder append(任意类型):支持 StringcharintbooleanObject 等几乎所有类型;
  • 返回当前 StringBuilder 对象,支持链式调用。

注意:

  • 所有 append() 调用都直接修改原对象
  • 自动将非字符串类型转换为字符串形式。
示例:
public class DemoAppend {public static void main(String[] args) {StringBuilder sb = new StringBuilder();sb.append("Hello").append(' ').append(123).append(true);System.out.println(sb); }
}

输出 :
在这里插入图片描述


2.4.2 删除(Delete)

delete()deleteCharAt() 用于移除部分或单个字符。

常用方法:
  • public StringBuilder delete(int start, int end):删除 [start, end) 区间的字符(左闭右开);
  • public StringBuilder deleteCharAt(int index):删除指定位置的单个字符。

注意:

  • 索引越界会抛出 StringIndexOutOfBoundsException
  • 删除后后续字符自动前移。
示例:
public class DemoDelete {public static void main(String[] args) {StringBuilder sb = new StringBuilder("abcdef");sb.delete(2, 4);       // 删除 "cd"sb.deleteCharAt(0);    // 删除首字符 'a'System.out.println(sb);}
}

输出:
在这里插入图片描述


2.4.3 插入(Insert)

insert() 方法可在指定位置插入数据。

常用方法:
  • public StringBuilder insert(int offset, 任意类型):在索引 offset 处插入内容;
  • 支持所有基本类型和对象。

注意:

  • 插入位置不能超过当前长度(但可等于长度,即在末尾插入);
  • 原有字符会向后移动。
示例:
public class DemoInsert {public static void main(String[] args) {StringBuilder sb = new StringBuilder("ac");sb.insert(1, "b");     // 在索引1插入 "b"sb.insert(0, 100);     // 在开头插入数字System.out.println(sb);}
}

输出:
在这里插入图片描述


2.4.4 替换(Replace)

replace() 用于将一段子串替换为新内容。

常用方法:
  • public StringBuilder replace(int start, int end, String str):将 [start, end) 替换为 str

注意:

  • 新字符串长度可以与原区间不同;
  • 替换后整体长度会相应变化。
示例:
public class DemoReplace {public static void main(String[] args) {StringBuilder sb = new StringBuilder("Hello World");sb.replace(6, 11, "Java");System.out.println(sb); }
}

输出:
在这里插入图片描述


2.4.5 翻转(Reverse)

reverse() 可将整个字符串内容反转。

常用方法:
  • public StringBuilder reverse():反转所有字符顺序。
示例:
public class DemoReverse {public static void main(String[] args) {StringBuilder sb = new StringBuilder("abc");sb.reverse();System.out.println(sb); }
}

输出:
在这里插入图片描述


2.4.6 查询与获取

用于获取长度、字符、子串位置等信息。

常用方法:
  • public int length():返回当前字符个数;
  • public char charAt(int index):获取指定位置的字符;
  • public int indexOf(String str):查找子串首次出现位置;
  • public String substring(int start, int end):截取子串(返回 String)。

注意:

  • length() 返回的是实际字符数,不是内部缓冲区容量;
  • substring() 返回的是 String,不是 StringBuilder
示例:
public class DemoQuery {public static void main(String[] args) {StringBuilder sb = new StringBuilder("Hello");System.out.println("长度:" + sb.length());        System.out.println("第1位:" + sb.charAt(1));      System.out.println("子串位置:" + sb.indexOf("ll")); System.out.println("截取:" + sb.substring(1, 4)); }
}

输出:
在这里插入图片描述


2.4.7 转换为 String

最终需将 StringBuilder 转为不可变字符串。

常用方法:
  • public String toString():返回对应的 String 对象。

注意:

  • 必须调用此方法才能用于需要 String 类型的场景(如输出、存储、比较等)。
示例:
public class DemoToString {public static void main(String[] args) {StringBuilder sb = new StringBuilder("OK");String result = sb.toString();System.out.println(result); 
}

输出:
在这里插入图片描述


2.4.8 清空

StringBuilder 没有 clear() 方法,但可通过设置长度实现清空。

常用方式:
  • sb.setLength(0):将长度设为 0,保留内部缓冲区,效率高;
  • sb.delete(0, sb.length()):也可清空,但略低效。
示例:
public class DemoClear {public static void main(String[] args) {StringBuilder sb = new StringBuilder("Hello");sb.setLength(0); System.out.println("清空后长度:" + sb.length()); sb.append("New");System.out.println(sb);}
}

输出:
在这里插入图片描述


2.5 案例----- 数组拼接问题优化

需求:

定义一个方法,把 int[] arr = {1,2,3,4,5} 按格式 [1, 2, 3, 4, 5] 拼接成字符串。

优化方案:

使用StringBuilder 进行拼接操作,无需频繁创建临时字符串,性能极高。

完整代码:

public class demo2 {public static void main(String[] args) {int[] arr={1,2,3,4,5};System.out.println(arrToString(arr));}public static String arrToString(int[] arr) {StringBuilder sb = new StringBuilder();sb.append("["); // 开始符号for (int i = 0; i < arr.length; i++) {if (i == arr.length - 1) {sb.append(arr[i]); // 最后一个元素不加逗号} else {sb.append(arr[i]).append(", "); // 加逗号和空格}}sb.append("]"); // 结束符号return sb.toString();}
}

输出:
在这里插入图片描述


3. StringJoiner

StringJoiner 是 Java 8 引入的一个实用工具类,用于高效、便捷地拼接带分隔符的字符串。相比手动拼接或使用 StringBuilder 拼接分隔符,它能自动处理前缀、后缀和中间分隔逻辑,避免多余的逗号或空格问题。

特别适合拼接集合、数组等场景,如生成 [a, b, c]a@b@c 格式的字符串。


3.1 基本用法

构造方法:

构造器说明
StringJoiner(CharSequence delimiter)指定分隔符(如 ,
StringJoiner(CharSequence delimiter, CharSequence prefix, CharSequence suffix)指定分隔符、前缀和后缀(如 []

注意:

  • 所有参数均为 CharSequence 类型,支持 StringStringBuilder 等;
  • 若未添加任何元素,调用 toString() 会返回 prefix + suffix(如 "[]")。

3.2 常用方法

方法说明
add(CharSequence element)添加一个元素,自动处理分隔(返回 this,支持链式调用)
toString()返回最终拼接结果
merge(StringJoiner other)合并另一个 StringJoiner 的内容(保留分隔规则)
toString().length()间接获取当前拼接结果的字符长度StringJoiner 本身无 length() 方法)

注意:StringJoiner 没有直接提供 length() 方法,但可以通过先调用 toString() 再获取长度的方式间接实现。


3.3 示例

3.3.1 基础拼接

import java.util.StringJoiner;public class demo1 {public static void main(String[] args) {StringJoiner sj = new StringJoiner(", ");sj.add("Apple").add("Banana").add("Orange");System.out.println(sj.toString());}
}

输出:
在这里插入图片描述

自动在元素间插入 , ,无需判断是否为最后一个元素。


3.3.2 带前缀和后缀(模拟数组格式)

import java.util.StringJoiner;public class demo2 {public static void main(String[] args) {StringJoiner sj = new StringJoiner(", ", "[", "]");sj.add("1").add("2").add("3").add("4").add("5");System.out.println(sj.toString()); }
}

输出:
在这里插入图片描述


3.4 空值与空容器处理

  • 如果从未调用 add(),则:
// 返回 "[]"
new StringJoiner(", ", "[", "]").toString();
  • 若需自定义“空时”的显示内容,可通过 setEmptyValue() 设置:
StringJoiner sj = new StringJoiner(",");
sj.setEmptyValue("无数据");
System.out.println(sj.toString());

默认空值为 prefix + suffix,若不需要可显式设置。


3.5 与 StringBuilder / 手动拼接对比

方式优点缺点
手动 +StringBuilder灵活需手动处理分隔符,易出错(如末尾多逗号)
StringJoiner自动处理分隔、支持前缀后缀、支持链式、可合并需 Java 8+

4. StringBuilder 与 StringJoiner 的输出细节

在开发和调试过程中,我们经常需要打印 StringBuilderStringJoiner 对象的内容,以验证拼接结果是否正确。那么,直接打印这些对象会输出内存地址吗?应该如何正确打印?

答案是:不会输出地址,直接打印即可获得期望的字符串内容!

4.1 原因:两者都重写了 toString() 方法

Java 中,System.out.println(obj) 会自动调用 obj.toString()
StringBuilderStringJoiner 均重写了 toString() 方法,返回其内部当前拼接好的字符串内容。

因此,无需手动调用 .toString(),直接打印对象本身即可


4.2 示例对比

4.2.1 StringBuilder 的打印

public class PrintDemo {public static void main(String[] args) {StringBuilder sb = new StringBuilder();sb.append("Java");sb.append(" ");sb.append("is");sb.append(" fun!");// 推荐:直接打印对象System.out.println(sb); // 功能相同但冗余(不推荐显式写 toString)System.out.println(sb.toString());}
}

输出:
在这里插入图片描述


4.2.2 StringJoiner 的打印

import java.util.StringJoiner;public class PrintDemo2 {public static void main(String[] args) {StringJoiner sj = new StringJoiner(", ", "[", "]");sj.add("Apple");sj.add("Banana");sj.add("Cherry");// 直接打印即可System.out.println(sj);}
}

输出:
在这里插入图片描述

注意:两种写法结果完全相同,但直接打印更简洁、符合惯例


5. 链式编程

链式编程是一种常见的编码风格,通过连续调用对象的方法,使代码更加简洁、直观。它的实现依赖于一个关键特性:每个方法执行后返回当前对象本身(或可继续调用的对象),从而支持“点号连续调用”。

链式编程广泛应用于 StringBuilder、流式处理(如 Stream)、以及各类工具类和框架中。


5.1 核心原理

实现方式:

  • 方法的返回类型为当前类类型;
  • 方法体末尾使用 return this; 返回自身实例。

特点:

  • 无需重复书写对象名;
  • 多个操作可在一行内完成;
  • 代码逻辑连贯,阅读更流畅。

注意:

  • 并非所有类都支持链式调用;
  • String 虽可写出链式形式,但因其不可变性,每一步都会创建新对象,不属于真正意义上的“链式修改”。

5.2 StringBuilder 的典型应用

StringBuilder 几乎所有修改方法都返回 this,是链式编程的最佳实践之一。

public class demo1 {public static void main(String[] args) {StringBuilder sb = new StringBuilder();sb.append("Java").append(' ').append("is").append(' ').append("awesome!").reverse();System.out.println(sb.toString()); }
}

输出:
在这里插入图片描述

每次调用 append()reverse() 后仍是对同一个 StringBuilder 对象的操作,效率高且写法简洁。

对比传统写法:

sb.append("Java");
sb.append(' ');
sb.append("is");
// ... 多行重复

链式写法显著减少冗余代码。


5.3 String 的“链式写法”

虽然 String 不可变,但也可以连续调用多个方法,形成类似链式的效果:

public class demo2 {public static void main(String[] args) {String result = "HELLO WORLD".toLowerCase()    .replace("world", "chain");System.out.println(result);}
}

输出:
在这里插入图片描述

注意:

  • 每一步操作都会生成新的 String 对象;
  • 在少量操作时可接受,但不适用于循环或高频场景,否则影响性能。

5.4 自定义链式调用(拓展)

若希望自定义类支持链式编程,只需在 setter 等方法中返回 this 即可:

public class User {private String name;private int age;public User setName(String name) {this.name = name;return this; // 关键:返回当前对象}public User setAge(int age) {this.age = age;return this;}public void info() {System.out.println("姓名:" + name + ",年龄:" + age);}public static void main(String[] args) {new User().setName("李四").setAge(20).info(); }
}

输出:
在这里插入图片描述

这种设计模式被称为 Fluent Interface(流式接口),被大量现代 Java 框架采用。


6. 字符串底层原理

6.1 字符串拼接的底层原理

6.1.1 情况一:拼接中包含变量

当字符串拼接表达式中包含变量时,Java 编译器不会进行优化,而是将其转换为 StringBuilder 的链式调用。

6.1.1.1 底层执行过程解析:
String s2 = s1 + "b"; // 等价于:
// new StringBuilder().append(s1).append("b").toString();

每执行一次 +,JVM 就会:

  • 创建一个 StringBuilder 实例;
  • 调用 append() 方法追加内容;
  • 调用 toString() 方法生成新的 String 对象;
  • 将结果赋值给变量。

每个 + 都会创建一个临时的 StringBuilder 和一个新的 String 对象!

6.1.1.2 内存模型示意图:

在这里插入图片描述
在这里插入图片描述

总共创建了 2 个 StringBuilder 对象和 2 个新 String 对象


6.1.2 情况二:拼接中全是字面量

6.1.2.1 底层执行过程解析:
String s = "a" + "b" + "c";// 编译后等价于:
//String s = "abc";

编译期优化机制会触发:Java 编译器会在 编译阶段 就将这些字符串合并成一个常量,这个过程称为 “字符串常量折叠”(String Constant Folding)

无需运行时创建任何对象,直接从常量池加载!

6.1.2.2 内存模型示意图:

在这里插入图片描述


6.1.3 JDK 8 字符串拼接的底层原理

在 JDK 8 中,字符串拼接(尤其是涉及变量的 + 操作)的底层实现发生了重要变化。虽然表面上仍表现为“自动转换为 StringBuilder”,但其核心机制已从临时对象堆叠转向了基于数组的高效构造


6.1.3.1 与 JDK7及以前版本的区别

在 JDK 8 的实际行为中,对于如下代码:

String result = a + b + c; // a, b, c 均为变量

javac 编译器会将其整体视为一个表达式,并生成等价于以下代码的字节码:

StringBuilder sb = new StringBuilder();
sb.append(a).append(b).append(c);
String result = sb.toString();

只创建一个 StringBuilder 实例,而非每遇到一个 + 就新建一次。

而这个 StringBuilder 的底层,正是依赖一个可变的 char[] 数组来存储字符内容。


6.1.3.2 底层数据结构:开数组 + 动态扩容

在这里插入图片描述

  • StringBuilder 内部维护一个 char[] value 数组;
  • 初始容量为 16;
  • 当追加内容超过当前容量时,会触发扩容:
    新容量 = (原容量 × 2) + 2
  • 扩容后,将旧数组内容复制到新数组中;
  • 最终调用 toString() 时,会复制该 char[] 数组,创建一个新的不可变 String 对象。

因此,“开数组”是 JDK 8 中字符串拼接高效的核心——它避免了每次拼接都生成中间字符串,而是通过可变数组累积内容,最后一次性构建结果。


6.1.4 小结

  • 简单、少量拼接可用 +
  • 循环、高频或不确定长度的拼接,应显式使用 StringBuilder 并预设初始容量
  • 需要分隔符格式化输出时,优先考虑 StringJoiner

6.2 StringBuilder 提高效率原理

StringBuilder通过 “可变字符数组” + “单次构建” 的方式,显著提升了字符串拼接的效率。

核心思想:集中处理,避免重复创建

在这里插入图片描述

StringBuilder 的核心优势在于:

所有要拼接的内容都统一放入一个可变的 char[] 数组中,只在最后调用 toString() 时生成最终的 String 对象。

这意味着:

  • 不会为每一个中间拼接步骤创建新的 String
  • 避免了多次对象分配与垃圾回收;
  • 减少了堆内存中无用临时对象的数量。

6.3 StringBuilder 源码分析

StringBuilder 是 Java 中用于高效拼接字符串的核心类,其底层实现基于可变的字符数组(char[])


6.3.1 核心数据结构:char[] 数组

StringBuilder 内部维护一个 char[] value 数组,用于存储所有追加的内容:

// StringBuilder 的核心字段(简化版)
char[] value;
int count; // 当前已使用的字符数量
  • value:存放实际字符的数组;
  • count:记录当前有效字符个数(即 length() 返回值);
  • 初始容量为 16,可通过构造函数指定。

6.3.2 默认容量与扩容机制

默认创建:长度为 16 的 char[] 数组
StringBuilder sb = new StringBuilder();
// 等价于:
// StringBuilder sb = new StringBuilder(16);

此时:

  • capacity() → 16
  • length() → 0

为什么是 16?这是 JVM 经过大量测试后选择的“最优默认值”,兼顾空间利用率和频繁扩容成本。


扩容规则:(原容量 × 2) + 2

当添加内容导致当前容量不足时,会触发扩容操作:

newCapacity = (oldCapacity * 2) + 2;

例如:
在这里插入图片描述

  • 初始容量:16
  • 添加 26 个字符(a~z)→ 超出 16 → 触发扩容
  • 新容量 = 16 × 2 + 2 = 34

在这里插入图片描述

注意:如果新内容长度大于 newCapacity,则直接以实际需要的长度为准!


扩容的本质:内存复制 + 数组重建

每次扩容都会:

  1. 创建一个新的、更大容量的 char[] 数组;
  2. 将旧数组中的内容复制到新数组;
  3. 更新 value 引用和 count 值;

这个过程涉及内存拷贝开销,因此在大量拼接时应尽量避免频繁扩容。


6.3.3 如何优化性能?

场景推荐做法
已知最终长度使用 StringBuilder(int capacity) 指定初始容量
大量拼接预估容量,如 new StringBuilder(100)
循环中拼接不要重复创建 StringBuilder,复用对象
http://www.dtcms.com/a/590812.html

相关文章:

  • 做网站优化选阿里巴巴还是百度班级网站设计与制作
  • 龙象建设集团有限公司网站电商运营培训哪个机构好
  • 广州市专业做网站电脑iis做网站
  • 本地多次 commit 但尚未 push 到远程仓库,如何合并为一次,以及如何导出他们的改动
  • 自学网站建设快吗网站建设捌金手指花总三
  • 佛山网站开发浙江众安建设集团有限公司网站
  • 人工智能训练师考试1.1.2
  • 3分钟上手CAD画图改图!零基础CAD制图指南
  • 三、项目进度管理
  • 石家庄市城乡建设部网站外贸网站建设要求
  • 百度网站下拉排名广州 网站设计
  • 下列关于网站开发网页上传中国人做英文网站
  • 做网站 前端wordpress更新超时
  • js网站一键变灰电商平台怎么做
  • 一个大数加法程序C 语言。
  • wordpress 源文件导入苏州百度seo关键词优化市场
  • 淘宝联盟上怎么建设网站做网站公司三年财务预算表
  • 陕西专业网站建设公司wordpress斗图
  • 长沙网站搭建首选智投未来2023年没有封闭的网站有哪些
  • 建设企业网站企业网上银行助手下载常用的h5制作平台有哪些
  • 专营网站建设论坛推广平台有哪些
  • 省规划建设发展局网站首页重庆响应式网站
  • 做网站鞍山wordpress邮件发送下载
  • 商城网站做推广长椿街网站建设
  • 站长工具中文精品做公司网站要营业执照吗
  • 【Java 工具类】集成AD域账号登录(完整实现)
  • 【应用服务器】Tomcat配置与部署详解
  • 重庆网站建设夹夹虫公司网站轮播怎么做
  • 大连商城网站建设建网站如何赚钱
  • 阜宁网站开发男生都知道的微信公众号