ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Java零基础入门:数组、集合与字符串核心原理与实战应用

2026/8/10 14:09:12 拓冰建站 浏览量
Java零基础入门:数组、集合与字符串核心原理与实战应用 最近在辅导几位零基础同学冲击 Java 实习岗位时发现很多人在学习初期容易陷入“只学语法不懂应用”的困境。特别是面对数组、集合、字符串这些看似基础却在面试和实战中高频出现的核心知识点时往往知其然不知其所以然。本文是“Java零基础冲实习”系列的第六篇我们将聚焦于Java 数组、集合框架Collection和字符串String这三大基石通过大量可运行的代码示例带你从零理解其原理、掌握其用法并梳理出面试中常见的“八股文”考点和避坑指南。学完本文你将能独立完成数组遍历、集合增删改查、字符串处理等常见任务为后续学习更复杂的数据结构和框架打下坚实基础。1. 核心概念为什么需要数组、集合和字符串在编程中我们经常需要处理一组相同类型的数据比如一个班级所有学生的成绩、一份购物车里的商品列表、或者一段需要被处理的文本信息。Java 提供了三种核心机制来高效地组织和管理这些数据。数组Array是 Java 中最基本的数据结构用于存储固定长度的、相同类型的元素序列。它在内存中连续分配通过下标索引访问效率极高。但它的长度一旦声明就无法改变这在处理动态数据时显得力不从心。集合框架Collection Framework正是为了解决数组的局限性而诞生的。它提供了一套丰富的接口和类如 List, Set, Map用于存储和操作动态长度的对象组。集合框架封装了复杂的数据结构算法如链表、哈希表、树让我们可以更专注于业务逻辑。字符串String在 Java 中并非基本数据类型而是一个不可变Immutable的类对象用于表示文本。几乎所有的 Java 程序都会用到字符串处理因此深入理解其特性如不可变性、字符串常量池对于编写高效、正确的代码至关重要。简单来说数组定长、高效、基础。集合动态、功能丰富、面向对象。字符串不可变、使用频繁、有特殊优化。2. 环境准备与学习目标学习环境JDK 版本建议使用 JDK 8 或 JDK 11LTS长期支持版本。本文示例代码在这些版本上均可运行。IDE集成开发环境强烈推荐使用 IntelliJ IDEA Community Edition免费或 Eclipse。它们能提供代码提示、自动补全和调试功能极大提升学习效率。基础要求已了解 Java 基本语法变量、循环、条件判断、类和对象的概念。本文学习目标掌握数组的声明、初始化、遍历及基本操作。理解ArrayList,LinkedList,HashSet,HashMap等核心集合类的特性和使用场景。熟练运用String类的常用方法进行字符串处理。理解String的不可变性及其影响。能分析和解决数组越界、空指针等常见异常。了解相关面试高频考点“八股文”。3. Java 数组详解与实战数组是存储同一类型数据的容器通过整型索引访问元素索引从 0 开始。3.1 数组的声明与初始化// 1. 声明数组 int[] arr1; // 推荐写法 int arr2[]; // 兼容C风格的写法不推荐 // 2. 静态初始化声明的同时赋值 int[] staticArr {1, 2, 3, 4, 5}; String[] names {Alice, Bob, Charlie}; // 3. 动态初始化先声明长度后赋值 int[] dynamicArr new int[5]; // 创建一个长度为5的int数组默认值全是0 dynamicArr[0] 10; dynamicArr[1] 20; // dynamicArr[5] 30; // 错误数组索引越界 ArrayIndexOutOfBoundsException // 4. 默认值规则 // 整数类型byte, short, int, long0 // 浮点类型float, double0.0 // 字符类型char\u0000 (空字符) // 布尔类型booleanfalse // 引用类型类、数组、String等null3.2 数组的遍历与操作遍历数组是基本操作主要有三种方式public class ArrayDemo { public static void main(String[] args) { int[] numbers {10, 20, 30, 40, 50}; // 方式1普通for循环可以获取索引 System.out.println( for循环遍历 ); for (int i 0; i numbers.length; i) { // 使用 .length 属性获取长度 System.out.println(索引 i 的值是: numbers[i]); } // 方式2增强for循环foreach简洁但无法获取索引 System.out.println( 增强for循环遍历 ); for (int num : numbers) { System.out.println(元素值是: num); } // 方式3使用 Arrays.toString() 快速打印数组内容调试常用 System.out.println( Arrays.toString() 输出 ); System.out.println(java.util.Arrays.toString(numbers)); // 输出: [10, 20, 30, 40, 50] // 数组拷贝 int[] copiedArr new int[numbers.length]; // System.arraycopy(源数组, 源起始位置, 目标数组, 目标起始位置, 拷贝长度) System.arraycopy(numbers, 0, copiedArr, 0, numbers.length); System.out.println(拷贝后的数组: java.util.Arrays.toString(copiedArr)); // 数组排序 int[] unsorted {5, 3, 8, 1, 2}; java.util.Arrays.sort(unsorted); // 升序排序 System.out.println(排序后: java.util.Arrays.toString(unsorted)); } }3.3 多维数组二维数组可以理解为“数组的数组”常用于表示表格、矩阵等数据。public class TwoDArrayDemo { public static void main(String[] args) { // 声明并初始化一个 2行3列 的二维数组 int[][] matrix { {1, 2, 3}, {4, 5, 6} }; // 遍历二维数组嵌套循环 System.out.println(二维数组内容); for (int i 0; i matrix.length; i) { // 遍历行 for (int j 0; j matrix[i].length; j) { // 遍历列 System.out.print(matrix[i][j] \t); } System.out.println(); // 换行 } // 动态初始化一个 3x3 的数组 char[][] board new char[3][3]; // 初始化一个井字棋棋盘 for (int i 0; i board.length; i) { for (int j 0; j board[i].length; j) { board[i][j] -; } } board[1][1] X; // 在中心放置一个‘X’ // 打印棋盘 for (char[] row : board) { for (char cell : row) { System.out.print(cell ); } System.out.println(); } } }4. Java 集合框架核心篇Java 集合框架主要位于java.util包中其核心接口关系如下图所示概念图Collection (接口) ├── List (接口) 有序、可重复 │ ├── ArrayList (类) 基于动态数组查询快增删慢尾部增删快 │ └── LinkedList (类) 基于双向链表增删快查询慢 └── Set (接口) 无序、不可重复 ├── HashSet (类) 基于哈希表存取最快无序 └── TreeSet (类) 基于红黑树元素有序按比较器 Map (接口) 键值对映射键不可重复 ├── HashMap (类) 基于哈希表存取快无序主流选择 └── TreeMap (类) 基于红黑树键有序4.1 List 接口ArrayList 与 LinkedListList的特点是有序存入顺序、可重复、有索引。ArrayList 实战import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class ArrayListDemo { public static void main(String[] args) { // 1. 创建ArrayList (推荐使用接口类型声明提高灵活性) ListString list new ArrayList(); // 2. 添加元素 list.add(Java); list.add(Python); list.add(C); list.add(Java); // 可以重复 System.out.println(初始列表: list); // [Java, Python, C, Java] // 3. 获取元素和大小 System.out.println(第一个元素: list.get(0)); System.out.println(列表大小: list.size()); // 4. 遍历List System.out.println( 遍历方式1: 普通for循环 ); for (int i 0; i list.size(); i) { System.out.println(list.get(i)); } System.out.println( 遍历方式2: 增强for循环 ); for (String lang : list) { System.out.println(lang); } System.out.println( 遍历方式3: 迭代器Iterator ); IteratorString iterator list.iterator(); while (iterator.hasNext()) { String item iterator.next(); System.out.println(item); // 可以在迭代中使用 iterator.remove() 安全删除当前元素 } // 5. 判断包含与查找索引 System.out.println(是否包含‘Python’: list.contains(Python)); // true System.out.println(‘Java’第一次出现的索引: list.indexOf(Java)); // 0 System.out.println(‘Java’最后一次出现的索引: list.lastIndexOf(Java)); // 3 // 6. 删除元素 list.remove(1); // 删除索引为1的元素Python System.out.println(删除索引1后: list); // [Java, C, Java] list.remove(Java); // 删除第一个匹配的“Java” System.out.println(删除第一个‘Java’后: list); // [C, Java] // 7. 修改元素 list.set(0, Go); System.out.println(修改索引0后: list); // [Go, Java] // 8. 清空与判空 list.clear(); System.out.println(清空后列表是否为空: list.isEmpty()); // true } }LinkedList 实战与对比LinkedList还实现了Deque接口可以作为栈或队列使用。import java.util.LinkedList; public class LinkedListDemo { public static void main(String[] args) { LinkedListInteger linkedList new LinkedList(); // 作为List使用 linkedList.add(1); linkedList.addFirst(0); // 添加到头部 linkedList.addLast(2); // 添加到尾部 System.out.println(LinkedList: linkedList); // [0, 1, 2] // 作为队列Queue使用FIFO linkedList.offer(3); // 入队添加到尾部 System.out.println(队首元素(不删除): linkedList.peek()); // 0 System.out.println(出队元素: linkedList.poll()); // 0 移除并返回队首 System.out.println(出队后: linkedList); // [1, 2, 3] // 作为栈Stack使用LIFO linkedList.push(9); // 压栈添加到头部 System.out.println(栈顶元素: linkedList.peek()); // 9 System.out.println(弹栈: linkedList.pop()); // 9 移除并返回头部 System.out.println(弹栈后: linkedList); // [1, 2, 3] } }ArrayList vs LinkedList 如何选择查询多增删少选ArrayList。get(int index)是 O(1) 常数时间。增删多查询少选LinkedList。在头部或中间插入/删除是 O(1)如果已知节点位置。不确定时优先使用ArrayList因为其实现更简单CPU缓存友好在大多数场景下综合性能更好。4.2 Set 接口HashSetSet的特点是无序不保证顺序、不可重复。最常用的实现是HashSet。import java.util.HashSet; import java.util.Set; public class HashSetDemo { public static void main(String[] args) { SetString set new HashSet(); // 添加元素 set.add(Apple); set.add(Banana); set.add(Orange); set.add(Apple); // 重复元素添加失败但不会报错 System.out.println(Set集合: set); // 输出无序可能是 [Orange, Apple, Banana] // 判断包含 System.out.println(是否包含‘Banana’: set.contains(Banana)); // true // 遍历Set (只能用增强for循环或迭代器因为没有索引) System.out.println( 遍历HashSet ); for (String fruit : set) { System.out.println(fruit); } // 删除元素 set.remove(Orange); System.out.println(删除‘Orange’后: set); // 另一个重要特性去重 ListInteger numbersWithDuplicates Arrays.asList(1, 2, 2, 3, 4, 4, 5); SetInteger uniqueNumbers new HashSet(numbersWithDuplicates); System.out.println(原始列表: numbersWithDuplicates); System.out.println(去重后的Set: uniqueNumbers); // [1, 2, 3, 4, 5] } }关键点HashSet的“无序”是指不保证插入顺序也不保证任何特定顺序。它的底层是HashMap依靠hashCode()和equals()方法来判断元素是否重复。因此存入HashSet的自定义对象必须正确重写这两个方法。4.3 Map 接口HashMapMap存储键值对Key-ValueKey不允许重复每个Key最多映射一个Value。import java.util.HashMap; import java.util.Map; public class HashMapDemo { public static void main(String[] args) { // 创建HashMap Key为String Value为Integer MapString, Integer scoreMap new HashMap(); // 1. 添加/更新元素 put(K key, V value) scoreMap.put(Alice, 95); scoreMap.put(Bob, 87); scoreMap.put(Charlie, 92); scoreMap.put(Alice, 100); // Key已存在则更新对应的Value System.out.println(成绩Map: scoreMap); // {Charlie92, Bob87, Alice100} // 2. 获取元素 get(Object key) Integer aliceScore scoreMap.get(Alice); System.out.println(Alice的成绩: aliceScore); // 100 System.out.println(不存在的Key: scoreMap.get(David)); // null // 3. 判断Key或Value是否存在 System.out.println(是否有Key‘Bob’: scoreMap.containsKey(Bob)); // true System.out.println(是否有Value 92: scoreMap.containsValue(92)); // true // 4. 遍历HashMap (几种重要方式) System.out.println( 遍历方式1: 遍历KeySet ); for (String name : scoreMap.keySet()) { Integer score scoreMap.get(name); System.out.println(name score); } System.out.println( 遍历方式2: 遍历EntrySet (推荐效率高) ); for (Map.EntryString, Integer entry : scoreMap.entrySet()) { System.out.println(entry.getKey() entry.getValue()); } System.out.println( 遍历方式3: 遍历Values ); for (Integer score : scoreMap.values()) { System.out.println(分数: score); } // 5. 删除元素 remove(Object key) scoreMap.remove(Charlie); System.out.println(删除Charlie后: scoreMap); // {Bob87, Alice100} // 6. 获取大小和判空 System.out.println(Map大小: scoreMap.size()); // 2 System.out.println(Map是否为空: scoreMap.isEmpty()); // false } }面试高频点HashMap的工作原理基于数组链表/红黑树、hashCode()和equals()的作用、扩容机制、线程不安全等。Hashtable是线程安全的但性能差ConcurrentHashMap是线程安全的高性能选择。5. 字符串String深度解析String可能是 Java 中使用最频繁的类理解其“不可变性”是关键。5.1 String 的创建与不可变性public class StringImmutable { public static void main(String[] args) { // 创建String对象的几种方式 String s1 Hello; // 字面量存储在字符串常量池 String s2 new String(Hello); // 在堆中新建对象 String s3 Hello; String s4 new String(Hello); System.out.println(s1 s2); // false 比较内存地址 System.out.println(s1 s3); // true s1和s3指向常量池的同一个对象 System.out.println(s2 s4); // false 两个不同的堆对象 System.out.println(s1.equals(s2)); // true 比较内容 // 不可变性演示 String str Hello; System.out.println(原始str: str , hashCode: System.identityHashCode(str)); str str.concat( World); // concat() 返回一个新的String对象 System.out.println(操作后str: str , hashCode: System.identityHashCode(str)); // 你会发现hashCode变了说明str指向了新的对象原对象Hello并未改变。 } }重要结论任何对String的修改操作如concat,substring,replace,toLowerCase都不会改变原字符串而是返回一个新的String对象。5.2 String 常用方法实战public class StringMethodsDemo { public static void main(String[] args) { String text Hello, Java World! ; // 1. 长度与判空 System.out.println(长度: text.length()); // 21 (包含空格) System.out.println(是否为空: text.isEmpty()); // false System.out.println(是否为空白: text.isBlank()); // false (JDK 11) // 2. 去除首尾空格 trim() / strip() (JDK 11 strip更强大) String trimmed text.trim(); System.out.println(trim后: \ trimmed \); // Hello, Java World! System.out.println(strip后: \ text.strip() \); // 同上 // 3. 大小写转换 System.out.println(大写: text.toUpperCase()); // HELLO, JAVA WORLD! System.out.println(小写: text.toLowerCase()); // hello, java world! // 4. 查找与判断 System.out.println(是否以‘ He’开头: text.startsWith( He)); // true System.out.println(是否以‘! ’结尾: text.endsWith(! )); // true System.out.println(包含‘Java’吗: text.contains(Java)); // true System.out.println(‘Java’的索引: text.indexOf(Java)); // 8 System.out.println(从末尾查找‘o’: text.lastIndexOf(o)); // 15 // 5. 截取与分割 String sub1 text.substring(8); // 从索引8开始到结尾 System.out.println(substring(8): \ sub1 \); // Java World! String sub2 text.substring(8, 12); // [8, 12) System.out.println(substring(8,12): \ sub2 \); // Java String[] words trimmed.split(, ); // 按“, ”分割 System.out.println(分割后数组: java.util.Arrays.toString(words)); // [Hello, Java World!] // 6. 替换 String replaced text.replace(Java, Python); System.out.println(替换后: \ replaced \); // Hello, Python World! // 7. 拼接与格式化 String joined String.join( - , A, B, C); System.out.println(join结果: joined); // A - B - C String formatted String.format(姓名: %s, 年龄: %d, 张三, 25); System.out.println(格式化: formatted); // 姓名: 张三, 年龄: 25 } }5.3 StringBuilder 与 StringBuffer由于String不可变在需要频繁拼接字符串时如在循环中会产生大量临时对象性能低下。这时应使用StringBuilder非线程安全或StringBuffer线程安全。public class StringBuilderDemo { public static void main(String[] args) { // 错误示范在循环中使用String拼接 String result ; for (int i 0; i 10000; i) { result i; // 每次循环都创建新的String对象性能极差 } // 正确示范使用StringBuilder StringBuilder sb new StringBuilder(); for (int i 0; i 10000; i) { sb.append(i); // 在同一个对象上修改高效 } String finalString sb.toString(); // 最后再转换为String System.out.println(长度: finalString.length()); // StringBuilder常用方法 StringBuilder builder new StringBuilder(Hello); builder.append( World); // 追加 builder.insert(5, ,); // 在索引5处插入 builder.replace(7, 12, Java); // 替换[7,12)区间 builder.delete(0, 6); // 删除[0,6)区间 builder.reverse(); // 反转 System.out.println(最终结果: builder.toString()); // 输出反转后的字符串 } }选择建议单线程环境用StringBuilder多线程环境用StringBuffer但通常可以通过其他方式同步StringBuilder更常用。6. 综合实战学生成绩管理系统控制台版我们将运用所学知识实现一个简单的学生成绩管理系统。import java.util.*; public class StudentGradeManager { // 使用Map存储学生信息Key为学号Value为学生对象 private MapString, Student studentMap new HashMap(); // 内部类学生 static class Student { String id; String name; ListInteger scores; // 使用List存储多门课成绩 public Student(String id, String name) { this.id id; this.name name; this.scores new ArrayList(); } // 添加成绩 public void addScore(int score) { scores.add(score); } // 计算平均分 public double getAverageScore() { if (scores.isEmpty()) { return 0.0; } int sum 0; for (int score : scores) { sum score; } return (double) sum / scores.size(); } Override public String toString() { return String.format(学号: %s, 姓名: %s, 成绩: %s, 平均分: %.2f, id, name, scores, getAverageScore()); } } // 添加学生 public void addStudent(String id, String name) { if (!studentMap.containsKey(id)) { studentMap.put(id, new Student(id, name)); System.out.println(添加成功: name); } else { System.out.println(添加失败学号已存在: id); } } // 为学生添加成绩 public void addScoreForStudent(String id, int score) { Student student studentMap.get(id); if (student ! null) { student.addScore(score); System.out.println(为 student.name 添加成绩: score); } else { System.out.println(学生不存在学号: id); } } // 查询所有学生按平均分排序 public void displayAllStudentsSorted() { if (studentMap.isEmpty()) { System.out.println(暂无学生信息。); return; } // 将Map的Value转为List以便排序 ListStudent studentList new ArrayList(studentMap.values()); // 使用Collections.sort和Lambda表达式按平均分降序排序 studentList.sort((s1, s2) - Double.compare(s2.getAverageScore(), s1.getAverageScore())); System.out.println( 学生成绩排名按平均分降序); for (Student student : studentList) { System.out.println(student); } } // 根据学号查询学生 public void queryStudent(String id) { Student student studentMap.get(id); if (student ! null) { System.out.println(查询结果: student); } else { System.out.println(未找到学号为 id 的学生。); } } // 主方法模拟一个简单的控制台交互 public static void main(String[] args) { StudentGradeManager manager new StudentGradeManager(); Scanner scanner new Scanner(System.in); // 初始化一些测试数据 manager.addStudent(1001, 张三); manager.addStudent(1002, 李四); manager.addStudent(1003, 王五); manager.addScoreForStudent(1001, 85); manager.addScoreForStudent(1001, 90); manager.addScoreForStudent(1002, 78); manager.addScoreForStudent(1002, 92); manager.addScoreForStudent(1003, 88); System.out.println(\n当前所有学生信息); manager.displayAllStudentsSorted(); System.out.println(\n--- 简单查询演示 ---); manager.queryStudent(1001); manager.queryStudent(9999); // 不存在的学号 scanner.close(); } }7. 常见问题与排查思路在学习和使用数组、集合、字符串时你一定会遇到以下几个经典错误。问题现象常见原因解决思路java.lang.ArrayIndexOutOfBoundsException访问数组时索引值超出了数组长度范围index 0或index array.length。1. 检查循环条件确保索引从0开始到array.length - 1结束。2. 在访问前先判断索引是否有效。java.lang.NullPointerException1. 调用null引用对象的方法或属性。2. 自动拆箱时包装类为null。3. 数组元素未初始化就访问。1. 在使用对象前始终进行非空判断if (obj ! null)。2. 检查集合、数组的初始化代码。3. 明确区分基本类型和包装类如intvsInteger。集合遍历时修改抛出ConcurrentModificationException在使用迭代器或增强for循环遍历ArrayList,HashMap等非线程安全集合时直接调用集合自身的add,remove方法修改集合。1. 使用迭代器自身的remove()方法进行删除。2. 遍历时将需要修改的元素暂存到另一个集合遍历完再统一处理。3. 使用CopyOnWriteArrayList等并发集合高级主题。String比较使用导致逻辑错误比较的是对象内存地址对于String字面量和new String()创建的对象地址可能不同。永远使用equals()方法比较字符串内容str1.equals(str2)。向Set或作为Map的Key存入自定义对象后无法正确去重或查找自定义类没有正确重写hashCode()和equals()方法。1. 使用 IDE如 IDEA自动生成hashCode()和equals()方法。2. 确保两个方法逻辑一致使用相同的字段进行计算。java.lang.OutOfMemoryError: Java heap space1. 内存泄漏如静态集合持续增长。2. 加载了过大的文件到内存如图片、视频。3. 堆内存设置过小。1. 检查代码中是否有集合对象被静态引用导致无法被GC回收。2. 处理大文件时使用流Stream而非一次性加载。3. 适当增加JVM堆内存参数如-Xmx1024m。8. 最佳实践与工程建议集合初始化指定容量对于ArrayList和HashMap如果你能预估大致的大小最好在创建时指定初始容量避免多次扩容带来的性能损耗。// 预估有100个元素 ListString list new ArrayList(100); MapString, Object map new HashMap(128);使用接口类型声明集合声明时使用List,Set,Map等接口而不是具体的实现类如ArrayList。这提高了代码的灵活性和可替换性是面向接口编程的体现。// 推荐 ListString names new ArrayList(); // 不推荐 ArrayListString names new ArrayList();字符串拼接用StringBuilder在循环内或多次拼接字符串时务必使用StringBuilder单线程或StringBuffer多线程。注意equals和的区别比较基本数据类型的值或比较对象的内存地址。equals比较对象的内容是否逻辑相等默认比较地址但如String,Integer等类已重写。黄金法则比较包装类对象和字符串一律用equals。使用java.util.Arrays和java.util.Collections工具类它们提供了大量静态方法用于操作数组和集合如排序、搜索、填充、反转等比自己写循环更安全高效。int[] arr {3,1,4,1,5}; Arrays.sort(arr); // 排序 int index Arrays.binarySearch(arr, 4); // 二分查找必须先排序 ListInteger list Arrays.asList(1,2,3); // 数组转List固定大小 ListString myList new ArrayList(); Collections.sort(myList); // 排序 Collections.reverse(myList); // 反转 String max Collections.max(myList); // 最大值为自定义的集合Key重写hashCode和equals如果你打算将自定义类的对象放入HashSet或作为HashMap的Key必须正确重写hashCode()和equals()方法。IDEA 的AltInsert可以一键生成。生产环境慎用System.out.println本文示例大量使用System.out.println是为了演示。在实际项目中应该使用日志框架如 SLF4J Logback来记录信息便于控制输出级别、格式和目的地。数组、集合和字符串是 Java 世界的基石几乎所有的业务逻辑都围绕着它们展开。理解数组的定长与高效掌握集合框架中ArrayList,LinkedList,HashSet,HashMap这四大金刚的特性和选用场景牢记String的不可变性并善用StringBuilder是每一位 Java 开发者入门的基本功。建议你将本文中的代码全部手动敲一遍并尝试修改和扩展那个学生成绩管理系统例如增加按姓名查询、删除学生、将数据保存到文件等功能。在练习中你会更深刻地体会到不同数据结构的选择对程序的影响。接下来你可以继续学习异常处理、IO流和面向对象的高级特性这些内容将帮助你构建更健壮、更复杂的应用程序。如果在实践中遇到问题多查阅官方文档Oracle Java Docs和利用 IDE 的调试功能这是成长最快的途径。