StringBuilder
StringBuilder
StringBuilder 可以看成是一个容器, 一旦创建了里面的内容是可以变化的.
StringBuilder 的构造方法有两个:
-
空参构造.
public StringBuilder();
创建一个空白可变字符串对象, 不含有任何内容. -
带一个字符串参数的构造.
public StringBuilder(String str);
根据字符串的内容来创建可变字符串对象.
StringBuilder 对象的成员方法:
public StringBuilder append(任意类型);
添加数据并返回对象本身.
public StringBuilder reverse();
反转容器中的内容.
public int length();
返回长度, 即字符的个数.
public String toString();
把 StringBuilder 转换为 String.
代码 1:
public class StringBuilderDemo { public static void main(String[] args) { StringBuilder sb1 = new StringBuilder(); // 使用 StringBuilder 的空参构造创建一个 StringBuilder 对象 System.out.println(sb1); } }
打印一个空行. 原因: StringBuilder 是 Java 已经写好的类, Java 对其底层做了些特殊处理, 打印对象不是地址值, 而是属性值.
代码 2:
public class StringBuilderDemo { public static void main(String[] args) { StringBuilder sb2 = new StringBuilder("你好"); // 使用 StringBuilder 的带参构造创建一个 StringBuilder 对象 System.out.println(sb2); // 你好 } }
代码 3:
public class StringBuilderDemo { public static void main(String[] args) { StringBuilder sb2 = new StringBuilder("你好"); System.out.println(sb2); // 你好 sb2.append(1); System.out.println(sb2); // 你好1 sb2.append(99.99); System.out.println(sb2); // 你好199.99 sb2.append(true); System.out.println(sb2); // 你好199.99true } }
代码 4:
// 反转字符串 public class StringBuilderDemo { public static void main(String[] args) { StringBuilder sb2 = new StringBuilder("你好"); System.out.println(sb2); // 你好 sb2.reverse(); System.out.println(sb2); // 好你 } }
代码 5:
// 获取长度 public class StringBuilderDemo { public static void main(String[] args) { StringBuilder sb2 = new StringBuilder("你好"); System.out.println(sb2); // 你好 int length = sb2.length(); System.out.println(length); // 2 } }
代码 6:
// 转变为 String 类型 public class StringBuilderDemo { public static void main(String[] args) { StringBuilder sb2 = new StringBuilder("你好"); System.out.println(sb2); // 你好 String s = sb2.toString(); System.out.println(s); // 你好 } }
链式编程:
调用一个方法时, 不需要定义一个变量去接收返回的结果, 可以继续调用其他方法.
代码 1:
import java.util.Scanner; public class StringBuilderDemo { public static void main(String[] args) { int length = getString().substring(1).length(); System.out.println(length); } public static String getString() { System.out.print("输入一个字符串: "); Scanner sc = new Scanner(System.in); String s = sc.next(); return s; } }
执行结果:
输入一个字符串: hello 4
代码 2:
public class StringBuilderDemo { public static void main(String[] args) { StringBuilder s = new StringBuilder(); s.append("hello").append(" world").append(" good").append(" morning!"); System.out.println(s); } }
执行结果:
hello world good morning!
练习:
对称字符串
需求: 键盘接受一个字符串, 程序判断出该字符串是否是否是对称字符串, 并在控制台打印是或不是
对称字符串: 123321, 111
非对称字符串: 123123
代码:
import java.util.Scanner; public class StringBuilderDemo { public static void main(String[] args) { System.out.print("请输入一个字符串: "); Scanner sc = new Scanner(System.in); String s = sc.next(); StringBuilder sb = new StringBuilder(s); sb.reverse(); String reversedS = sb.toString(); System.out.println(reversedS); System.out.println(reversedS.equals(s)); } }
另一种写法:
import java.util.Scanner; public class StringBuilderDemo { public static void main(String[] args) { System.out.print("请输入一个字符串: "); Scanner sc = new Scanner(System.in); String s = sc.next(); // 链式编程 String res = new StringBuilder(s).reverse().toString(); System.out.println(res); System.out.println(res.equals(s)); } }
常用 StringBuilder 的两个场景:
-
字符串拼接.
-
字符串反转.
练习:
拼接字符串
需求: 定义一个方法, 把 int 数组中的数据按照指定的格式并接成一个字符串返回.
调用该方法, 并在控制台输出结果.
例如: 数组为 int[] arr = {1, 2, 3};
执行方法后的输出结果为: [1, 2, 3]
程序示例:
public class Test { public static void main(String[] args) { int[] arr = {1, 2, 3}; String convertArr = convert(arr); System.out.println(convertArr); } public static String convert(int[] arr) { StringBuilder sb = new StringBuilder("["); for (int i = 0; i < arr.length; i++) { if (i == arr.length - 1) { sb.append(arr[i]).append("]"); } else { sb.append(arr[i]).append(", "); } } return sb.toString(); } }
StringBuilder 源码分析

找到的内容:




从 newString 进去.

可以看出来是 new 了一个 String.
如果代码里面已经写了 StringBuilder, 也可以选中它, 按下 Ctrl 和鼠标左键, 同样可以进入 StringBuilder 的源码.
因此, toString 的底层是直接 new 出来了一个 String 对象, 那么这个对象就一定在堆内存中而不是串池中.
StringBuilder 的整体设计
StringBuilder 在刚开始创建的时候, 会创建一个字节数组, 默认的容量是 16, 默认的值为 0, 此时因为没有放进去内容, 故长度为 0.
StringBuilder 中, 容量概念表示最多能装多少内容, 长度概念指的是实际装了多少.
当把字符串 "abc" 存放到 StringBuilder 中的时候, 是将 abc 的 ASCII 码存放进去了. 没有数据存放进去的位置还是默认的 0. 此时长度就变为了 3.

代码:
public class StringBuilderDemo { public static void main(String[] args) { StringBuilder sb = new StringBuilder(); System.out.println(sb.capacity()); // 16 System.out.println(sb.length()); // 0 sb.append("abc"); System.out.println(sb.capacity()); // 16 System.out.println(sb.length()); // 3 } }
代码:
public class StringBuilderDemo { public static void main(String[] args) { StringBuilder sb = new StringBuilder(); System.out.println(sb.capacity()); // 16 System.out.println(sb.length()); // 0 sb.append("abcdefghijklmnopqrstuvwxyz"); System.out.println(sb.capacity()); // 34 System.out.println(sb.length()); // 26 } }
代码:
public class StringBuilderDemo { public static void main(String[] args) { StringBuilder sb = new StringBuilder(); System.out.println(sb.capacity()); // 16 System.out.println(sb.length()); // 0 sb.append("abcdefghijklmnopqrstuvwxyz0123456789"); System.out.println(sb.capacity()); // 36 System.out.println(sb.length()); // 36 } }
StringBuilder 内存原理
StringBuilder 的空参构造:

从 super 进去.

这里 new 了一个 byte 数组, 长度 capacity 就是从 super 里面传递过来的 16.
接着在 String Builder.java 文件中找 append 方法. 还是按下 Ctrl+F12 进行搜索. append 方法有很多重载的方法, 这里从倒数第三个进去.


这个 append 方法里面又调用了其他的 append 方法, 再从这个被调用的 append 方法进去.

首先对参数进行非空校验, 如果参数为空, 则调用 appendNull 方法, 从这个 appendNull 方法进去.

返回到上一步, 即 append 方法中, 接着往下走, 图 5 的 581 行, 先获取到要添加的这个字符串的长度, 这里的 count 是容器目前的长度, 可以这样看 count 的含义:
按下 Ctrl+F12, 找到 length 方法:

count + len 得到的就是需要的最小的容量.

进入 ensureCapacityInternal 方法内:

假设现在需要放入字符串为 abc, 长度为 3, 则最小需求的容量为 3 (假设 StringBuilder 原来为空, 即 count 为 0, 而 len 为 3, 则最小容量也就是 count + len
= 3), 而老的容量为 16, 则 if 的判断条件为 false, 不执行内部语句, 该方法其实什么也没有执行. if 的语句体所做的工作就是扩容.
再假设现在放入的字符串为 a 到 z, 即 26 个小写英文字母, 则 if 的判断条件为 true, 进入内部开始执行, 从 newCapacity 进去.
oldLength 表示老容量, newLength 表示最小容量. 左移 coder 是和编码表有关的, 比如考虑了中文, 不涉及编码表时则等于零, 目前可以先暂时忽略.
growth 则为需要新增的容量.

再进入 newLength 方法,


再回到 ensureCapacityInternal 方法, 进入 copyOf 方法:


可以看到, 这里会根据长度创建一个新的字节数组.
示例:

练习:
要求:
转换罗马数字
键盘录入一个字符串,
要求 1: 长度为小于等于 9
要求 2: 只能是数字
将内容变成罗马数字
下面是阿拉伯数字跟罗马数字的对比关系:
I - 1, II - 2, III - 3, IV - 4, V - 5, VI - 6, VII - 7, VII - 8, IX - 9
注意点:
罗马数字里面是没有 0 的
如果键盘录入的数字包含 0, 可以变成 "" (长度为 0 的字符串)
代码:
import java.util.Scanner; import java.util.StringJoiner; public class Test { public static void main(String[] args) { // 键盘录入一个字符串 Scanner sc = new Scanner(System.in); String str; // 在循环外面定义这个字符串, 在循环里面获取字符串, 在循环后面还要继续用这个字符串 while (true) { System.out.print("输入一个字符串: "); str = sc.next(); if (check(str)) { break; } else { System.out.println("输入字符串有误, 请重新输入. "); } } // 将阿拉伯数字变成罗马数字, 用 String 拼接 // String res; // for (int i = 0; i < str.length(); i++) { // res = res + toRoman(str.charAt(i)) + " "; // } // 用 StringBuilder 拼接 // StringBuilder sb = new StringBuilder(); // for (int i = 0; i < str.length(); i++) { // sb.append(toRoman(str.charAt(i))); // } // 用 StringJoiner 拼接 StringJoiner sj = new StringJoiner(" "); for (int i = 0; i < str.length(); i++) { sj.add(toRoman(str.charAt(i))); } // 打印 // System.out.println(res); // System.out.println(sb); System.out.println(sj); } // 将阿拉伯数字变成罗马数字 public static String toRoman(char c) { String[] romans = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VII", "IX"}; int index = c - '0'; return romans[index]; } // 校验字符串是否符合要求 public static boolean check(String str) { if (str.length() > 9) return false; for (int i = 0; i < str.length(); i++) if (!Character.isDigit(str.charAt(i))) return false; return true; } }
练习:
要求:
调整字符串
给定两个字符串, A 和 B.
A 的旋转操作就是将 A 最左边的字符移动到最右边.
例如, 若 A='abcde', 在移动一次之后结果就是 'bcdea'.
如果在若干次调整操作之后, A 能变成 B, 那么返回 True.
如果不能匹配成功, 则返回 false
代码:
public class test { public static void main(String[] args) { String a = "abcde"; String b = "cdeab"; System.out.println(change(a, b)); } private static boolean change(String a, String b) { for (int i = 0; i < a.length(); i++) { if (a.equals(b)) return true; else { char[] tmp = a.toCharArray(); // 字符串变为字符数组 char c = tmp[0]; // 原来的数组的第一个字符 for (int j = 1; j <= tmp.length - 1; j++) // 把每一个字符都往前移动一位 tmp[j - 1] = tmp[j]; tmp[tmp.length - 1] = c; // 最后一个字符是原来的第一个字符 a = new String(tmp); // 字符数组变为字符串 } } return false; } }
代码:
public class test { public static void main(String[] args) { String a = "abcde"; String b = "cdeab"; System.out.println(change(a, b)); } private static boolean change(String a, String b) { for (int i = 0; i < a.length(); i++) { if (a.equals(b)) return true; else { a = rotate(a); } } return false; } private static String rotate(String a) { char[] tmp = a.toCharArray(); char c = tmp[0]; for (int j = 1; j <= tmp.length - 1; j++) tmp[j - 1] = tmp[j]; tmp[tmp.length - 1] = c; a = new String(tmp); return a; } }
套路: 如果需要修改字符串的内容, 通常有两种方法:
-
用 substring 截取, replace 替换, append 添加等.
-
先将字符串转换为字符数组, 修改数组, 再将数组转换为字符串.
代码:
public class test { public static void main(String[] args) { String a = "abcde"; String b = "cdeab"; System.out.println(check(a, b)); } private static boolean check(String a, String b) { for (int i = 0; i < a.length(); i++) { if (a.equals(b)) return true; else a = rotate(a); } return false; } private static String rotate(String a) { char fisrt = a.charAt(0); String last = a.substring(1); return last + fisrt; } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!