九:字符串与 ArrayList——文本处理与动态数组

一、回顾与本篇目标

前面八篇,你从零开始学会了 Java 的基本语法、条件判断、循环、数组,以及面向对象的三大核心特性——封装、继承、多态。现在你已经能用面向对象的方式写出结构清晰的程序了。

在写程序的过程中,有两个东西你几乎每时每刻都会用到:字符串列表。字符串用来处理文本——用户输入的名字、邮箱、文章内容都是字符串。列表用来存储一组数据——学生的成绩、商品的信息、待办事项的清单。Java 提供了两个非常强大的类来分别处理这两种需求:StringArrayList

String 你其实已经在用了——从第一篇开始,"Hello World" 就是一个字符串。但 String 远不止是“用双引号包裹的一段文字”,它有几十个非常有用的方法,比如截取子串、替换内容、查找字符位置、大小写转换。ArrayList 则是一种动态数组——和普通数组不同,它的长度可以自动增长和缩小,不用提前确定大小。

本篇的目标:

  1. 理解 String 是引用类型,不是基本类型
  2. 掌握 String 最常用的十几个方法
  3. 学会用 StringBuilder 高效拼接字符串
  4. 理解 ArrayList 和普通数组的区别
  5. 掌握 ArrayList 的增删改查和遍历

二、String——字符串

2.1 String 是引用类型

在第二篇中我们学了八种基本数据类型(int、double、char、boolean 等)。String 不在其中——String 是一个类,属于引用类型。但因为它太常用了,Java 给了它特殊的待遇:可以像基本类型一样直接用双引号创建。

// 两种创建字符串的方式

// 方式一:直接用双引号(最常用,和基本类型一样方便)
String name = "张三";

// 方式二:用 new 关键字(和其他引用类型一样)
String name2 = new String("张三");

// 两种方式创建的都是 String 对象,但方式一更简洁,是推荐写法

String 的内容不可变:一旦创建了一个 String 对象,它的内容就不能被修改。所有看起来“修改”了字符串的操作(比如转大写、替换),实际上都是创建了一个新的 String 对象,原来的字符串没有变。

String text = "hello";
String upperText = text.toUpperCase();  // 创建了一个新字符串 "HELLO"

System.out.println(text);       // hello —— 原来的没变
System.out.println(upperText);  // HELLO —— 新创建的字符串

2.2 获取字符串长度

length() 方法获取字符串的字符个数:

String text = "Hello World";
System.out.println(text.length());  // 11(空格也算一个字符)

String chinese = "你好世界";
System.out.println(chinese.length());  // 4(一个中文也是一个字符)

注意length() 是一个方法,后面有括号。这和数组的 length 属性不同(数组的 length 没有括号)。这是初学者经常搞混的地方。

int[] arr = {1, 2, 3};
System.out.println(arr.length);     // 属性,没有括号 —— 3

String str = "abc";
System.out.println(str.length());   // 方法,有括号 —— 3

2.3 获取指定位置的字符

charAt(索引) 获取字符串中某个位置的字符。索引从 0 开始:

String text = "Hello";

System.out.println(text.charAt(0));  // H —— 第一个字符
System.out.println(text.charAt(1));  // e —— 第二个字符
System.out.println(text.charAt(4));  // o —— 最后一个字符

// 遍历字符串的每个字符
for (int i = 0; i < text.length(); i++) {
    System.out.print(text.charAt(i) + " ");  // H e l l o
}

2.4 截取子串

substring(开始索引, 结束索引) 截取一部分:

String text = "Hello World";

// 从索引 0 截取到索引 5(不包括 5)
System.out.println(text.substring(0, 5));  // Hello

// 从索引 6 截取到末尾
System.out.println(text.substring(6));     // World

// 从索引 3 截取到索引 7
System.out.println(text.substring(3, 8));  // lo Wo

规则substring(开始, 结束)——包含开始,不包含结束。如果只写一个参数,就从那个位置截取到末尾。

2.5 查找子串位置

indexOf() 查找某个子串第一次出现的位置:

String text = "Hello World";

System.out.println(text.indexOf("World"));  // 6 —— "World" 从索引 6 开始
System.out.println(text.indexOf("o"));      // 4 —— 第一个 "o" 在索引 4
System.out.println(text.indexOf("xyz"));    // -1 —— 没找到返回 -1

// 从指定位置开始查找
System.out.println(text.indexOf("o", 5));   // 7 —— 从索引 5 开始找,找到第二个 "o"

// lastIndexOf:查找最后一次出现的位置
System.out.println(text.lastIndexOf("o"));  // 7 —— 最后一个 "o" 在索引 7

2.6 替换内容

replace() 替换字符串中的内容:

String text = "Hello World";

// 替换所有匹配的字符或字符串
System.out.println(text.replace("o", "0"));      // Hell0 W0rld
System.out.println(text.replace("World", "Java")); // Hello Java

2.7 去除首尾空白

trim() 去除字符串开头和结尾的空白字符(空格、Tab、换行等):

String input = "   张三   ";
System.out.println("[" + input + "]");         // [   张三   ]
System.out.println("[" + input.trim() + "]");  // [张三]

这在处理用户输入时非常有用——用户可能在名字前后多打了空格,用 trim() 可以清理掉。

2.8 大小写转换

String text = "Hello World";

System.out.println(text.toUpperCase());  // HELLO WORLD —— 全部大写
System.out.println(text.toLowerCase());  // hello world —— 全部小写

2.9 判断是否以某内容开头或结尾

String filename = "report.pdf";

System.out.println(filename.startsWith("report"));  // true
System.out.println(filename.endsWith(".pdf"));      // true
System.out.println(filename.endsWith(".doc"));      // false

2.10 字符串比较——用 equals 而不是 ==

这是一个非常重要的知识点。在 Java 中比较两个字符串的内容是否相同,必须使用 equals() 方法,不能用 ==

String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");

// == 比较的是内存地址(两个变量是否指向同一个对象)
System.out.println(s1 == s2);      // true(这种情况特殊,因为字符串常量池)
System.out.println(s1 == s3);      // false(s3 是 new 出来的,地址不同)

// equals 比较的是内容
System.out.println(s1.equals(s2));  // true(内容相同)
System.out.println(s1.equals(s3));  // true(内容相同)

// equalsIgnoreCase:忽略大小写比较
System.out.println("Hello".equalsIgnoreCase("hello"));  // true

一句话规则:比较字符串内容永远用 equals(),不要用 ==

2.11 判断是否为空

String str1 = "";
String str2 = "  ";
String str3 = null;

// isEmpty():判断长度是否为 0
System.out.println(str1.isEmpty());  // true

// isBlank()(Java 11+):判断是否为空或只包含空白字符
// System.out.println(str2.isBlank());  // true

// 判断是否为 null
System.out.println(str3 == null);  // true

三、StringBuilder——高效拼接字符串

如果你需要在循环中频繁地拼接字符串,直接用 + 号效率很低——因为 String 是不可变的,每次拼接都会创建一个新的 String 对象。Java 提供了 StringBuilder 来解决这个问题。

// 低效的方式:用 + 号在循环中拼接(每次创建新对象)
String result1 = "";
for (int i = 1; i <= 5; i++) {
    result1 = result1 + i + " ";  // 每次都创建新字符串
}
System.out.println(result1);  // 1 2 3 4 5

// 高效的方式:用 StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 1; i <= 5; i++) {
    sb.append(i).append(" ");  // 往同一个对象里追加
}
String result2 = sb.toString();
System.out.println(result2);  // 1 2 3 4 5

StringBuilder 常用方法

StringBuilder sb = new StringBuilder("Hello");

sb.append(" World");        // 追加:Hello World
sb.insert(5, " Java");      // 在索引 5 插入:Hello Java World
sb.replace(6, 10, "C++");   // 替换索引 6-10:Hello C++ World
sb.delete(5, 9);            // 删除索引 5-9:Hello World
sb.reverse();               // 反转:dlroW olleH

System.out.println(sb.toString());  // 最终转成 String

什么时候用 StringBuilder:在循环中拼接字符串时使用。平时简单的拼接(如 "你好" + name),直接使用 + 号就够了——编译器会自动优化。

四、ArrayList——动态数组

4.1 为什么需要 ArrayList

在第六篇中我们学了普通数组——int[] arr = new int[5];。普通数组有一个最大的缺点:大小在创建时就定死了,之后不能改变。如果你一开始不知道要存多少个数据(比如用户输入的班级人数是运行时才确定的),就无法确定数组的大小。

ArrayList 就是一种能自动伸缩的数组——你不必提前确定大小,添加元素时它自动扩容,删除元素时它自动缩容。

// 普通数组:大小固定
int[] arr = new int[5];  // 只能存 5 个元素,不能超过

// ArrayList:大小动态变化
ArrayList list = new ArrayList<>();  // 不指定大小,可以随意添加

4.2 创建 ArrayList

ArrayList 在 java.util 包中,使用前需要导入。语法中有一对尖括号 <>,里面写要存储的数据类型:

import java.util.ArrayList;  // 导入 ArrayList

public class ArrayListDemo {
    public static void main(String[] args) {
        // 创建一个存储字符串的 ArrayList
        ArrayList names = new ArrayList<>();

        // 创建一个存储整数的 ArrayList
        // 注意:尖括号里不能写基本类型,要写包装类 Integer
        ArrayList scores = new ArrayList<>();

        // 创建一个存储小数的 ArrayList
        ArrayList prices = new ArrayList<>();
    }
}

为什么是 Integer 而不是 int:尖括号 <>只能写引用类型,不能写基本类型。Java 为每种基本类型提供了对应的包装类

基本类型 包装类
int Integer
double Double
char Character
boolean Boolean

Java 会在基本类型和包装类之间自动转换(自动装箱和拆箱),所以你用起来和基本类型几乎没有区别:

ArrayList list = new ArrayList<>();
list.add(10);       // 自动把 int 10 转换成 Integer
int num = list.get(0);  // 自动把 Integer 转换回 int

4.3 ArrayList 的增删改查

import java.util.ArrayList;

public class ArrayListOperations {
    public static void main(String[] args) {
        ArrayList fruits = new ArrayList<>();

        // 1. 添加元素
        fruits.add("苹果");        // 在末尾添加
        fruits.add("香蕉");
        fruits.add("橘子");
        fruits.add(1, "草莓");     // 在索引 1 的位置插入
        System.out.println(fruits);  // [苹果, 草莓, 香蕉, 橘子]

        // 2. 获取元素
        String first = fruits.get(0);   // 获取索引 0 的元素
        String second = fruits.get(1);  // 获取索引 1 的元素
        System.out.println("第一个:" + first);   // 苹果
        System.out.println("第二个:" + second);  // 草莓

        // 3. 获取大小
        System.out.println("元素个数:" + fruits.size());  // 4

        // 4. 修改元素
        fruits.set(1, "西瓜");   // 把索引 1 的元素改成"西瓜"
        System.out.println(fruits);  // [苹果, 西瓜, 香蕉, 橘子]

        // 5. 删除元素
        fruits.remove(2);         // 删除索引 2 的元素(香蕉)
        fruits.remove("橘子");    // 删除值为"橘子"的元素
        System.out.println(fruits);  // [苹果, 西瓜]

        // 6. 判断是否包含
        System.out.println(fruits.contains("苹果"));  // true
        System.out.println(fruits.contains("香蕉"));  // false

        // 7. 判断是否为空
        System.out.println(fruits.isEmpty());  // false

        // 8. 清空所有元素
        fruits.clear();
        System.out.println(fruits.isEmpty());  // true
    }
}

4.4 遍历 ArrayList

import java.util.ArrayList;

public class ArrayListTraversal {
    public static void main(String[] args) {
        ArrayList fruits = new ArrayList<>();
        fruits.add("苹果");
        fruits.add("香蕉");
        fruits.add("橘子");
        fruits.add("葡萄");

        // 方式一:普通 for 循环(按索引访问)
        System.out.println("===== 普通 for 循环 =====");
        for (int i = 0; i < fruits.size(); i++) {
            System.out.println("第 " + (i + 1) + " 个:" + fruits.get(i));
        }

        // 方式二:增强 for 循环(最简洁)
        System.out.println("===== 增强 for 循环 =====");
        for (String fruit : fruits) {
            System.out.println(fruit);
        }
    }
}

和普通数组一样:

  • 需要索引时用普通 for 循环。
  • 只需要值时用增强 for 循环。

4.5 ArrayList vs 普通数组

特性 普通数组 ArrayList
创建方式 int[] arr = new int[5]; ArrayList list = new ArrayList<>();
大小 固定,不能改变 动态,自动伸缩
获取大小 arr.length(属性) list.size()(方法)
访问元素 arr[0] list.get(0)
修改元素 arr[0] = 10; list.set(0, 10);
添加元素 不能(长度固定) list.add(10);
删除元素 不能 list.remove(0);
存储基本类型 ✅ 可以 ❌ 必须用包装类(如 Integer)

五、综合演示:学生名单管理

下面这个示例综合运用了 String 的方法和 ArrayList 的操作:

import java.util.ArrayList;

public class StudentManager {
    public static void main(String[] args) {
        ArrayList students = new ArrayList<>();

        // 添加学生
        students.add("张三");
        students.add("李四");
        students.add("王五");
        students.add("赵六");
        students.add("孙七");

        System.out.println("===== 学生名单 =====");
        printList(students);

        // 查找名字中包含"三"的学生
        System.out.println("\n===== 名字中含"三"的学生 =====");
        for (String name : students) {
            if (name.contains("三")) {
                System.out.println(name);
            }
        }

        // 把所有名字转成大写输出
        System.out.println("\n===== 大写名单 =====");
        for (String name : students) {
            System.out.print(name.toUpperCase() + " ");
        }
        System.out.println();

        // 删除名字长度大于 2 的学生(中文名超过两个字)
        // 注意:从后往前删,避免索引错乱
        for (int i = students.size() - 1; i >= 0; i--) {
            if (students.get(i).length() > 2) {
                System.out.println("\n删除了:" + students.remove(i));
            }
        }

        System.out.println("\n===== 删除后名单 =====");
        printList(students);
    }

    // 辅助方法:打印列表
    public static void printList(ArrayList list) {
        if (list.isEmpty()) {
            System.out.println("列表为空");
            return;
        }
        for (int i = 0; i < list.size(); i++) {
            System.out.println((i + 1) + ". " + list.get(i));
        }
    }
}

输出:

===== 学生名单 =====
1. 张三
2. 李四
3. 王五
4. 赵六
5. 孙七

===== 名字中含"三"的学生 =====
张三

===== 大写名单 =====
张三 李四 王五 赵六 孙七 

删除了:孙七

===== 删除后名单 =====
1. 张三
2. 李四

关键点:删除元素时从后往前遍历。因为删除一个元素后,后面元素的索引会自动前移。如果从前往后删,可能会跳过某些元素。

六、本篇动手练习

练习 1:字符串处理

新建 Practice9_1.java。让用户输入一个邮箱地址,判断是否包含 @、是否以 .com 结尾、提取用户名部分(@ 之前的部分)。由于还没学键盘输入,可以把邮箱地址直接写在代码里。

练习 2:StringBuilder 练习

新建 Practice9_2.java。用 StringBuilder 把 1 到 10 的数字拼接成字符串 "1, 2, 3, ..., 10"。要求数字之间用逗号加空格分隔。

练习 3:ArrayList 增删改查

新建 Practice9_3.java。创建一个存储分数的 ArrayList。添加 5 个分数,然后:输出所有分数、计算平均分、找到最高分、删除所有不及格(小于 60)的分数、再次输出。

练习 4:购物清单

新建 Practice9_4.java。创建 Product 类(属性:名称、价格),然后创建一个 ArrayList 模拟购物清单。实现:添加商品、计算总价、找出最贵的商品、删除所有价格超过 100 元的商品。

七、本篇小结

这一篇你学会了 Java 中两个最常用的工具:

  • String:引用类型,内容不可变。常用方法:length()(长度)、charAt()(取字符)、substring()(截取)、indexOf()(查找)、replace()(替换)、trim()(去空白)、toUpperCase()/toLowerCase()(大小写转换)、equals()(比较内容,不能用 ==)。
  • StringBuilder:可变字符串,用于高效拼接。主要方法:append()(追加)、insert()(插入)、delete()(删除)、toString()(转成 String)。
  • ArrayList:动态数组,大小自动伸缩。创建时用 new ArrayList<>(),尖括号里写包装类(如 Integer 而不是 int)。常用方法:add()(添加)、get()(获取)、set()(修改)、remove()(删除)、size()(大小)、contains()(判断存在)、clear()(清空)。

String 和 ArrayList 是日常编程中最频繁使用的两个类。下一篇,我们将学习 Java 中的异常处理机制——用 try/catch 优雅地处理错误,以及如何读写文件,让数据能持久化保存。

下一篇预告

下一篇——《异常处理与文件读写》:什么是异常、try/catch/finally 语法、常见异常类型、文件读取(FileReader/BufferedReader)、文件写入(FileWriter/BufferedWriter)。让程序能处理意外情况,并能把数据保存到硬盘上。

Java 零基础入门,每周更新。

© 版权声明
THE END
喜欢就支持一下吧
点赞14 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容