Java零基础入门:可变字符串与包装类:StringBuilder、StringBuffer 与 通讯录管理系统实战

Java零基础入门:可变字符串与包装类:StringBuilder、StringBuffer 与 通讯录管理系统实战

一、为什么需要 StringBuilder?------ String 的"不可变性"

1. String 是不可变的

在 Java 中,String 对象一旦创建,内容就不能改变。

String s = "Hello";

s = s + " World"; // 看起来是"修改"了,其实不是

真相是:

第1步:创建 "Hello" → 存在内存里

第2步:创建 " World" → 存在内存里

第3步:拼接产生 "Hello World" → 又是一个全新的字符串对象

第4步:让变量 s 指向这个新对象

原来的 "Hello" 并没有被修改,它还在内存里等着被垃圾回收。

2. 频繁拼接的灾难

// 这样写,循环10000次要创建10000多个临时对象!

String result = "";

for (int i = 0; i < 10000; i++) {

result = result + i; // 每次循环都产生新对象

}

后果:内存占用暴增 + 速度极慢(因为每次都要复制之前的字符)。

3. 解决方案:StringBuilder

StringBuilder 就像一块可擦写的白板 ,你可以在上面不断写字、擦字、插字,不会每次都换一块新白板。


二、StringBuilder 详解

1. 创建 StringBuilder

// 方式一:空的可变字符串

StringBuilder sb1 = new StringBuilder();

// 方式二:带初始内容的

StringBuilder sb2 = new StringBuilder("Hello");

// 方式三:指定初始容量(知道大概要多长时用)

StringBuilder sb3 = new StringBuilder(100);

2. 常用方法速查表

方法 作用 示例
append(任意类型) 在末尾追加 sb.append("World")
insert(位置, 内容) 在指定位置插入 sb.insert(5, " Java")
delete(开始, 结束) 删除指定范围 sb.delete(5, 10)
deleteCharAt(索引) 删除指定位置的字符 sb.deleteCharAt(0)
replace(开始, 结束, 新串) 替换 sb.replace(0, 5, "Hi")
reverse() 反转字符串 sb.reverse()
toString() 转成 String String s = sb.toString()
length() 获取长度 sb.length()
capacity() 获取当前容量 sb.capacity()
setLength(新长度) 设置长度 sb.setLength(5)

3. 基本使用示例

public class StringBuilderDemo {

public static void main(String\[\] args) {

StringBuilder sb = new StringBuilder("Hello");

// 1. 追加

sb.append(" ");

sb.append("World");

sb.append("!");

System.out.println(sb); // Hello World!

// 2. 插入

sb.insert(6, "Java "); // 在索引6处插入

System.out.println(sb); // Hello Java World!

// 3. 删除

sb.delete(6, 11); // 删除索引6到10("Java ")

System.out.println(sb); // Hello World!

// 4. 替换

sb.replace(6, 11, "Java");

System.out.println(sb); // Hello Java!

// 5. 反转

sb.reverse();

System.out.println(sb); // !avaJ olleH

// 6. 再反转回来

sb.reverse();

System.out.println(sb); // Hello Java!

// 7. 转成 String

String result = sb.toString();

System.out.println("转成String: " + result);

}

}

4. 链式调用(超好用)

StringBuilder 的大多数方法返回自身(return this),所以可以连着写:

StringBuilder sb = new StringBuilder();

sb.append("姓名:").append("张三").append(",年龄:").append(20).append("岁");

System.out.println(sb.toString());

// 输出:姓名:张三,年龄:20岁

一行搞定,不需要写一堆 + 号拼接。


三、StringBuffer:线程安全的版本

StringBuffer 和 StringBuilder 的用法完全一样,唯一区别是:

对比项 StringBuilder StringBuffer
线程安全 ❌ 不安全(单线程用) ✅ 安全(多线程用)
速度 更快 稍慢(因为加了同步锁)
使用场景 单线程(99%的情况) 多线程共享同一个字符串缓冲区

// StringBuffer 用法跟 StringBuilder 一模一样

StringBuffer sbf = new StringBuffer("Hello");

sbf.append(" World");

System.out.println(sbf.toString());

结论 :你写单线程程序(比如学生管理系统、通讯录),永远用 StringBuilder 就够了。


四、性能对比实验:String vs StringBuilder vs StringBuffer

完整测试代码

public class StringPerformanceTest {

public static void main(String\[\] args) {

int count = 50000; // 拼接次数

// ===== 测试 String =====

long start1 = System.currentTimeMillis();

String s = "";

for (int i = 0; i < count; i++) {

s = s + i;

}

long end1 = System.currentTimeMillis();

System.out.println("String 拼接 " + count + " 次耗时:" + (end1 - start1) + " ms");

// ===== 测试 StringBuilder =====

long start2 = System.currentTimeMillis();

StringBuilder sb = new StringBuilder();

for (int i = 0; i < count; i++) {

sb.append(i);

}

long end2 = System.currentTimeMillis();

System.out.println("StringBuilder 拼接 " + count + " 次耗时:" + (end2 - start2) + " ms");

// ===== 测试 StringBuffer =====

long start3 = System.currentTimeMillis();

StringBuffer sbf = new StringBuffer();

for (int i = 0; i < count; i++) {

sbf.append(i);

}

long end3 = System.currentTimeMillis();

System.out.println("StringBuffer 拼接 " + count + " 次耗时:" + (end3 - start3) + " ms");

}

}

典型运行结果(不同电脑有差异)

String 拼接 50000 次耗时:4821 ms

StringBuilder 拼接 50000 次耗时:3 ms

StringBuffer 拼接 50000 次耗时:5 ms

差距是几百倍到上千倍!

为什么 String 这么慢?

每次 s = s + i 都做了这些事:

  1. 创建新的 StringBuilder(隐式)
  2. 把 s 的内容复制进去
  3. 把 i 追加进去
  4. 调用 toString() 生成新 String
  5. 原来的 String 变成垃圾等回收

循环 50000 次 = 创建了 50000 个临时对象 + 复制了 50000 次数组。

而 StringBuilder 只在同一个字符数组上操作,满了才扩容,效率天差地别。


五、包装类:基本类型的"马甲"

1. 为什么需要包装类?

Java 有 8 种基本类型(int、double、boolean 等),但它们不是对象,不能:

  • 放进集合里(ArrayList<int> ❌ 不允许)
  • 调用方法
  • 参与面向对象的多态

所以 Java 给每种基本类型都配了一个对应的类 ,叫包装类(Wrapper Class)。

2. 八种基本类型与包装类对照表

基本类型 包装类 备注
byte Byte
short Short
int Integer​ 最常用
long Long
float Float
double Double
char Character 唯一不是以基本类型名+s结尾的
boolean Boolean

3. 自动装箱与自动拆箱

手动装箱/拆箱(Java 5 之前必须这样写)

// 手动装箱:基本类型 → 包装类

Integer num1 = Integer.valueOf(100);

// 手动拆箱:包装类 → 基本类型

int num2 = num1.intValue();

自动装箱/拆箱(Java 5 之后,编译器帮你做)

// 自动装箱:编译器自动把 int 100 变成 Integer.valueOf(100)

Integer num1 = 100;

// 自动拆箱:编译器自动调用 num1.intValue()

int num2 = num1;

// 甚至可以直接参与运算

Integer a = 10;

Integer b = 20;

int sum = a + b; // a 和 b 自动拆箱成 int,相加后再赋值给 sum

底层原理:你写 Integer num = 100;,编译器编译后变成 Integer num = Integer.valueOf(100);。你写 int x = num;,编译后变成 int x = num.intValue();。

4. 常见坑:Integer 缓存

Integer a = 127;

Integer b = 127;

System.out.println(a == b); // true(缓存范围内)

Integer c = 128;

Integer d = 128;

System.out.println(c == d); // false(超出缓存范围)

System.out.println(c.equals(d)); // true(用 equals 比较值才正确)

原因 :Integer.valueOf() 对 -128 到 127 之间的数会缓存复用,超出范围才 new 新对象。

记住 :比较包装类的值,永远用 equals(),不要用 ==。


六、字符串与基本类型互转

1. 字符串 → 基本类型

// 方式一:用包装类的 parseXxx() 方法(最常用)

int age = Integer.parseInt("20");

double price = Double.parseDouble("19.99");

boolean flag = Boolean.parseBoolean("true");

// 方式二:用 valueOf()(返回的是包装类对象)

Integer ageObj = Integer.valueOf("20");

int age2 = ageObj; // 自动拆箱

注意:字符串格式必须正确,否则抛异常:

int x = Integer.parseInt("abc"); // ❌ NumberFormatException

2. 基本类型 → 字符串

// 方式一:String.valueOf()(最推荐,不会抛异常)

String s1 = String.valueOf(123); // "123"

String s2 = String.valueOf(3.14); // "3.14"

String s3 = String.valueOf(true); // "true"

// 方式二:拼接空字符串

String s4 = 123 + "";

// 方式三:包装类的 toString()

String s5 = Integer.toString(123);

3. 综合练习:用户输入数字并计算

import java.util.Scanner;

public class StringParseDemo {

public static void main(String\[\] args) {

Scanner sc = new Scanner(System.in);

System.out.print("请输入第一个数字:");

String input1 = sc.nextLine();

int num1 = Integer.parseInt(input1); // 字符串 → int

System.out.print("请输入第二个数字:");

String input2 = sc.nextLine();

int num2 = Integer.parseInt(input2);

int sum = num1 + num2;

// int → String 输出

System.out.println("结果:" + String.valueOf(sum));

// 或者直接用 + 拼接

System.out.println("结果:" + sum);

sc.close();

}

}


七、综合小项目:通讯录管理系统

1. 需求分析

功能 描述
添加联系人 输入姓名、电话、邮箱、分组
删除联系人 按姓名或电话删除
修改联系人 按姓名查找,修改电话/邮箱
查询联系人 按姓名模糊搜索
显示所有 列出所有联系人
退出 保存并退出

2. 完整代码实现

Contact.java(联系人实体类)

public class Contact {

private String name;

private String phone;

private String email;

private String group; // 分组:家人、朋友、同事等

public Contact(String name, String phone, String email, String group) {

this.name = name;

this.phone = phone;

this.email = email;

this.group = group;

}

// Getter 和 Setter

public String getName() { return name; }

public void setName(String name) { this.name = name; }

public String getPhone() { return phone; }

public void setPhone(String phone) { this.phone = phone; }

public String getEmail() { return email; }

public void setEmail(String email) { this.email = email; }

public String getGroup() { return group; }

public void setGroup(String group) { this.group = group; }

// 重写 toString,用 StringBuilder 拼接(性能更好)

@Override

public String toString() {

StringBuilder sb = new StringBuilder();

sb.append("【").append(group).append("】")

.append(name).append(" - ")

.append(phone).append(" - ")

.append(email);

return sb.toString();

}

}

ContactManager.java(管理类)

import java.util.ArrayList;

public class ContactManager {

private ArrayList<Contact> contacts = new ArrayList<>();

// ===== 添加联系人 =====

public boolean addContact(Contact contact) {

// 检查电话是否已存在

for (Contact c : contacts) {

if (c.getPhone().equals(contact.getPhone())) {

System.out.println("❌ 该电话号码已存在!");

return false;

}

}

contacts.add(contact);

System.out.println("✅ 添加成功!");

return true;

}

// ===== 删除联系人(按电话)=====

public boolean removeByPhone(String phone) {

for (int i = 0; i < contacts.size(); i++) {

if (contacts.get(i).getPhone().equals(phone)) {

Contact removed = contacts.remove(i);

System.out.println("✅ 已删除:" + removed.getName());

return true;

}

}

System.out.println("❌ 未找到该电话号码对应的联系人");

return false;

}

// ===== 修改联系人 =====

public boolean updateContact(String phone, String newName, String newEmail, String newGroup) {

for (Contact c : contacts) {

if (c.getPhone().equals(phone)) {

if (newName != null && !newName.trim().isEmpty()) {

c.setName(newName);

}

if (newEmail != null && !newEmail.trim().isEmpty()) {

c.setEmail(newEmail);

}

if (newGroup != null && !newGroup.trim().isEmpty()) {

c.setGroup(newGroup);

}

System.out.println("✅ 修改成功!");

return true;

}

}

System.out.println("❌ 未找到该电话号码对应的联系人");

return false;

}

// ===== 按姓名模糊搜索 =====

public void searchByName(String keyword) {

boolean found = false;

System.out.println("\n====== 搜索结果 ======");

for (Contact c : contacts) {

if (c.getName().toLowerCase().contains(keyword.toLowerCase())) {

System.out.println(c);

found = true;

}

}

if (!found) {

System.out.println("未找到包含 \"" + keyword + "\" 的联系人");

}

System.out.println("======================\n");

}

// ===== 显示所有联系人 =====

public void showAll() {

if (contacts.isEmpty()) {

System.out.println("📭 通讯录为空");

return;

}

System.out.println("\n====== 通讯录(共 " + contacts.size() + " 人)======");

for (int i = 0; i < contacts.size(); i++) {

System.out.println((i + 1) + ". " + contacts.get(i));

}

System.out.println("================================\n");

}

// ===== 按分组显示 =====

public void showByGroup(String group) {

boolean found = false;

System.out.println("\n====== " + group + " 分组 ======");

for (Contact c : contacts) {

if (c.getGroup().equals(group)) {

System.out.println(c);

found = true;

}

}

if (!found) {

System.out.println("该分组下暂无联系人");

}

System.out.println("==========================\n");

}

}

ContactSystem.java(主程序/菜单交互)

import java.util.Scanner;

public class ContactSystem {

public static void main(String\[\] args) {

Scanner sc = new Scanner(System.in);

ContactManager manager = new ContactManager();

// 预置几个联系人

manager.addContact(new Contact("张三", "13800138001", "zhangsan@email.com", "朋友"));

manager.addContact(new Contact("李四", "13800138002", "lisi@email.com", "同事"));

manager.addContact(new Contact("王五", "13800138003", "wangwu@email.com", "家人"));

while (true) {

System.out.println("\n====== 通讯录管理系统 ======");

System.out.println("1. 添加联系人");

System.out.println("2. 删除联系人");

System.out.println("3. 修改联系人");

System.out.println("4. 搜索联系人(按姓名)");

System.out.println("5. 显示所有联系人");

System.out.println("6. 按分组查看");

System.out.println("7. 退出");

System.out.print("请选择:");

String choice = sc.nextLine();

int choiceNum = Integer.parseInt(choice); // 字符串 → int

switch (choiceNum) {

case 1: {

// 添加

System.out.print("姓名:");

String name = sc.nextLine();

System.out.print("电话:");

String phone = sc.nextLine();

System.out.print("邮箱:");

String email = sc.nextLine();

System.out.print("分组(朋友/同事/家人):");

String group = sc.nextLine();

Contact c = new Contact(name, phone, email, group);

manager.addContact(c);

break;

}

case 2: {

// 删除

System.out.print("输入要删除的联系人电话:");

String phone = sc.nextLine();

manager.removeByPhone(phone);

break;

}

case 3: {

// 修改

System.out.print("输入要修改的联系人电话:");

String phone = sc.nextLine();

System.out.print("新姓名(不改直接回车):");

String newName = sc.nextLine();

System.out.print("新邮箱(不改直接回车):");

String newEmail = sc.nextLine();

System.out.print("新分组(不改直接回车):");

String newGroup = sc.nextLine();

manager.updateContact(phone, newName, newEmail, newGroup);

break;

}

case 4: {

// 搜索

System.out.print("输入搜索关键词(姓名):");

String keyword = sc.nextLine();

manager.searchByName(keyword);

break;

}

case 5: {

// 显示所有

manager.showAll();

break;

}

case 6: {

// 按分组查看

System.out.print("输入分组名称:");

String group = sc.nextLine();

manager.showByGroup(group);

break;

}

case 7: {

// 退出

System.out.println("👋 再见!");

sc.close();

System.exit(0);

break;

}

default:

System.out.println("❌ 无效选择,请重新输入");

}

}

}

}

3. 代码亮点总结

知识点 在代码中的体现
ArrayList 存储所有联系人
StringBuilder Contact.toString() 中拼接输出信息
包装类 Integer.parseInt(choice) 字符串转 int
字符串处理 trim()、isEmpty()、toLowerCase()、contains()
CRUD 完整实现 增删改查全覆盖
菜单循环 while(true) + switch
空指针防护 修改时判断 null 和空字符串

八、每日一练

练习1(基础):StringBuilder 操作

  • 创建 StringBuilder,追加你的姓名、年龄、爱好
  • 在姓名和年龄之间插入"今年"
  • 删除爱好部分
  • 最后反转输出

练习2(进阶):包装类转换

  • 从键盘输入5个字符串形式的数字,转成 int 后存入 ArrayList<Integer>
  • 计算它们的平均值并输出

练习3(挑战):给通讯录加新功能

  • 添加"导出为文本"功能:把所有联系人信息用 StringBuilder 拼接后写入 .txt 文件
  • 添加"排序"功能:按姓名拼音排序(提示:用 Collections.sort() + Comparator)

相关推荐
启观川1 小时前
数据结构与算法 -第 3 章 常用算法-动态规划
数据结构·笔记·python·算法
码爸1 小时前
排序算法介绍
python·算法·排序算法
paopaokaka_luck1 小时前
基于springboot+vue的智慧家政管理系统(AI问答、 WebSocket实时沟通、ECharts运营分析、订单规则与服务追踪)
java·javascript·spring boot·数据分析·echarts·mybatis
sm_926787051 小时前
RFID 标签打印的技术实现要点与二次开发实践
java·大数据·前端·c++·编辑器
阿森_dev1 小时前
2027 届毕设选题怎么选:30 个 Java 方向选题的实现难度排序
java·开发语言·spring boot·毕业设计·课程设计
by209991 小时前
C++ 多态:同一个基类接口,调用不同的函数
开发语言·c++·经验分享·多态
笔墨登场说说2 小时前
flink bin/start-cluster.sh 帮我做成开机启动
开发语言·python
Sarvartha2 小时前
Lambda 表达式和函数式接口
java·开发语言
计算机毕业设计杰瑞2 小时前
【计算机毕设精品】基于SpringBoot的一体化教务与学业质量管理系统的设计与实现,附源码_增删改查_java项目_可完美运行部署_网站项目
java·spring boot·课程设计