上一篇我们掌握了Java流程控制,能通过分支和循环实现复杂逻辑(比如打印九九乘法表、金字塔),但这些案例中,我们只能处理单个变量的数据。实际开发中,我们经常需要存储"多个相同类型的数据"(比如一个班级50名学生的成绩、一组商品的价格),或者"处理文本信息"(比如用户输入的姓名、密码),这就需要用到Java中的数组 和字符串。

数组是存储多个相同类型数据的"容器",字符串是处理文本信息的核心工具,二者都是Java基础中高频使用、高频面试的知识点。本篇文章将从基础入手,手把手教你掌握数组的定义、初始化、遍历方式,String类的核心常用方法,拆解新手高频易错点(比如数组越界、字符串不可变),同时讲解面试中常见的数组与字符串问题,让你不仅会用,还能吃透底层逻辑,为后续学习集合、面向对象进阶筑牢基础。
核心目标:掌握数组的定义、初始化、遍历(普通for、增强for)和常见操作,熟练运用String类的核心方法(拼接、截取、替换、判断等),理解字符串不可变特性,能独立完成数组排序、字符串处理等实战案例,规避常见坑,掌握基础面试考点。
一、前置认知:数组与字符串的核心作用
在学习具体用法之前,我们先搞清楚:为什么需要数组和字符串?它们能解决什么问题?
-
数组的作用:存储多个相同类型的数据,避免重复定义变量。比如存储5名学生的成绩,不用定义score1、score2、score3、score4、score5,只需定义一个数组,就能存储所有成绩,且方便统一操作(比如遍历所有成绩、求平均分);
-
字符串的作用:处理文本信息,Java中用String类封装文本,提供了大量便捷方法,比如判断两个文本是否相等、截取文本、替换文本等,无需我们自己编写复杂逻辑。
核心结论:数组解决"多个相同类型数据存储"的问题,字符串解决"文本处理"的问题,二者是Java开发中最基础、最常用的工具,几乎所有程序都会用到。
二、Java数组:存储多个相同类型数据的容器
数组的核心特点:固定长度、相同类型、连续存储。一旦数组初始化完成,长度就不能修改;数组中所有元素的类型必须一致(比如int数组只能存整数,String数组只能存字符串);数组元素在内存中是连续存储的,访问效率高。
2.1 数组的定义与初始化(3种常用方式)
数组的定义有两种格式,初始化分为"静态初始化"和"动态初始化",新手重点掌握前两种初始化方式,根据场景选择使用。
(1)数组的定义格式(两种,任选其一)
java
// 格式1:数据类型[] 数组名;(推荐,更规范,一眼能看出是数组)
int[] scores; // 定义一个int类型的数组,用于存储成绩
String[] names; // 定义一个String类型的数组,用于存储姓名
// 格式2:数据类型 数组名[];(兼容C/C++语法,不推荐)
int scores[];
String names[];
注意:仅定义数组,不会分配内存空间,数组名只是一个"引用",必须初始化后才能使用(否则会报错)。
(2)静态初始化:明确指定数组元素,长度由元素个数自动决定
语法格式:
java
// 格式1:完整写法
数据类型[] 数组名 = new 数据类型[]{元素1, 元素2, 元素3, ...};
// 格式2:简化写法(推荐,简洁)
数据类型[] 数组名 = {元素1, 元素2, 元素3, ...};
核心特点:初始化时,直接指定数组中的每一个元素,数组长度 = 元素个数,无需手动指定长度。
实战案例:
java
public class ArrayInit1 {
public static void main(String[] args) {
// 静态初始化:存储5名学生的成绩(int类型)
int[] scores = {90, 85, 95, 78, 88};
// 静态初始化:存储3个姓名(String类型)
String[] names = new String[]{"张三", "李四", "王五"};
// 打印数组(直接打印数组名,输出的是数组的内存地址,不是元素)
System.out.println(scores); // 输出:[I@7c30a502([I表示int数组,后面是内存地址)
System.out.println(names); // 输出:[Ljava.lang.String;@49e4cb85
}
}
避坑点1:静态初始化的简化写法({元素1, 元素2}),只能在定义数组时使用,不能先定义再赋值。比如"int[] scores; scores = {90,85};"是错误的,正确写法是"int[] scores = {90,85};"或"scores = new int[]{90,85};"。
(3)动态初始化:明确指定数组长度,元素由系统赋默认值
语法格式:
java
数据类型[] 数组名 = new 数据类型[数组长度];
核心特点:初始化时,只指定数组长度,不指定具体元素,系统会给数组中每一个元素赋"默认值"(和基本数据类型的默认值一致),后续再手动给元素赋值。
数组元素的默认值(重点,和第2篇基本数据类型默认值一致):
-
整数类型(int、long、byte、short):默认值0;
-
浮点类型(double、float):默认值0.0;
-
字符类型(char):默认值'\u0000'(空字符,不可见);
-
布尔类型(boolean):默认值false;
-
引用类型(String、数组等):默认值null(表示空引用,没有指向任何对象)。
实战案例:
java
public class ArrayInit2 {
public static void main(String[] args) {
// 动态初始化:定义一个长度为5的int数组(存储成绩),元素默认值0
int[] scores = new int[5];
// 动态初始化:定义一个长度为3的String数组(存储姓名),元素默认值null
String[] names = new String[3];
// 手动给数组元素赋值(数组索引从0开始)
scores[0] = 90; // 给第1个元素赋值(索引0)
scores[1] = 85; // 给第2个元素赋值(索引1)
scores[2] = 95;
scores[3] = 78;
scores[4] = 88;
names[0] = "张三";
names[1] = "李四";
names[2] = "王五";
// 打印数组元素(通过索引访问)
System.out.println(scores[0]); // 输出90
System.out.println(names[2]); // 输出王五
}
}
避坑点2:数组的索引从0开始,不是从1开始!比如长度为5的数组,索引范围是0~4,访问索引5会报"数组越界异常"(新手最易犯)。
2.2 数组的核心操作:访问、遍历、修改(重点)
数组的核心操作围绕"索引"展开,通过索引可以访问、修改数组元素;遍历是数组最常用的操作(遍历所有元素,进行查询、计算等)。
(1)数组元素的访问与修改
语法格式:
java
// 访问数组元素:数组名[索引]
数组名[索引];
// 修改数组元素:数组名[索引] = 新值;
数组名[索引] = 新值;
实战案例:
java
public class ArrayOperate1 {
public static void main(String[] args) {
int[] scores = {90, 85, 95, 78, 88};
// 访问元素:获取索引2的元素(第3个元素)
int score = scores[2];
System.out.println("索引2的元素:" + score); // 输出95
// 修改元素:将索引3的元素(78)修改为80
scores[3] = 80;
System.out.println("修改后索引3的元素:" + scores[3]); // 输出80
// 错误示例:数组越界(长度为5,索引最大是4,访问索引5报错)
// System.out.println(scores[5]); // 报错:ArrayIndexOutOfBoundsException
}
}
重点提醒:数组越界异常(ArrayIndexOutOfBoundsException)是新手最常遇到的异常之一,原因是访问的索引超出了数组的索引范围(0 ~ 数组长度-1),务必注意!
(2)数组的遍历(3种方式,重点掌握前两种)
遍历:依次访问数组中的每一个元素,常用场景:打印所有元素、求总和、求平均值、找最大值/最小值等。
方式1:普通for循环(最灵活,可获取索引,适合需要修改元素的场景)
java
public class ArrayTraverse1 {
public static void main(String[] args) {
int[] scores = {90, 85, 95, 80, 88};
// 普通for循环:i从0到数组长度-1(scores.length获取数组长度)
for (int i = 0; i < scores.length; i++) {
System.out.println("索引" + i + "的元素:" + scores[i]);
}
// 实战:求成绩总和和平均值
int sum = 0;
for (int i = 0; i < scores.length; i++) {
sum += scores[i]; // 累加所有元素
}
double avg = sum / (double) scores.length; // 强转为double,避免整数除法
System.out.println("成绩总和:" + sum);
System.out.println("成绩平均值:" + avg);
}
}
说明:数组名.length 可以获取数组的长度(固定值,初始化后不变),无需手动记录长度,避免出错。
方式2:增强for循环(foreach循环,简洁,适合仅遍历、不修改元素的场景)
语法格式:
java
for (数据类型 变量名 : 数组名) {
// 变量名表示数组中的当前元素,依次遍历每一个元素
}
实战案例:
java
public class ArrayTraverse2 {
public static void main(String[] args) {
String[] names = {"张三", "李四", "王五", "赵六"};
// 增强for循环:依次遍历names数组中的每一个元素,赋值给name变量
for (String name : names) {
System.out.println("姓名:" + name);
}
// 注意:增强for循环无法修改数组元素(变量name是临时变量,修改它不影响原数组)
for (int score : scores) {
score = 100; // 无效,原数组元素不会改变
}
}
}
避坑点3:增强for循环只能遍历元素,无法修改原数组元素(临时变量不影响原数组);如果需要修改元素,必须用普通for循环(通过索引修改)。
方式3:Arrays.toString()方法(快速打印数组,适合调试)
Java提供了Arrays工具类(需要导入import java.util.Arrays;),其中toString()方法可以快速将数组转为字符串,直接打印所有元素,无需手动遍历。
java
import java.util.Arrays;
public class ArrayTraverse3 {
public static void main(String[] args) {
int[] scores = {90, 85, 95, 80, 88};
String[] names = {"张三", "李四", "王五"};
// 快速打印数组,格式:[元素1, 元素2, 元素3]
System.out.println(Arrays.toString(scores)); // 输出:[90, 85, 95, 80, 88]
System.out.println(Arrays.toString(names)); // 输出:[张三, 李四, 王五]
}
}
2.3 数组的常见实战案例
结合数组的遍历和修改,完成3个经典实战案例,巩固数组用法。
案例1:找出数组中的最大值和最小值
java
public class ArrayCase1 {
public static void main(String[] args) {
int[] nums = {15, 28, 9, 36, 22, 10};
// 假设第一个元素是最大值和最小值
int max = nums[0];
int min = nums[0];
// 遍历数组,更新最大值和最小值
for (int num : nums) {
if (num > max) {
max = num; // 找到更大的值,更新max
}
if (num < min) {
min = num; // 找到更小的值,更新min
}
}
System.out.println("数组中的最大值:" + max); // 输出36
System.out.println("数组中的最小值:" + min); // 输出9
}
}
案例2:数组排序(使用Arrays.sort()方法,常用)
java
import java.util.Arrays;
public class ArrayCase2 {
public static void main(String[] args) {
int[] nums = {15, 28, 9, 36, 22, 10};
System.out.println("排序前:" + Arrays.toString(nums));
// 调用Arrays.sort()方法,对数组进行升序排序(从小到大)
Arrays.sort(nums);
System.out.println("排序后:" + Arrays.toString(nums)); // 输出:[9, 10, 15, 22, 28, 36]
}
}
说明:Arrays.sort()方法默认是升序排序,如果需要降序排序,后续会讲解(需要结合其他方法,新手先掌握升序即可)。
案例3:数组复制(从一个数组复制到另一个数组)
java
import java.util.Arrays;
public class ArrayCase3 {
public static void main(String[] args) {
int[] srcArr = {10, 20, 30, 40, 50}; // 原数组
int[] destArr = new int[5]; // 目标数组(长度和原数组一致)
// 方式1:循环复制(手动复制,灵活)
for (int i = 0; i < srcArr.length; i++) {
destArr[i] = srcArr[i];
}
System.out.println("循环复制后:" + Arrays.toString(destArr));
// 方式2:Arrays.copyOf()方法(推荐,简洁)
int[] destArr2 = Arrays.copyOf(srcArr, srcArr.length);
System.out.println("copyOf复制后:" + Arrays.toString(destArr2));
}
}
2.4 数组的常见易错点(新手必看)
-
数组越界异常:访问的索引超出0~数组长度-1的范围,比如长度为5的数组,访问索引5;
-
静态初始化简化写法使用错误:先定义数组,再用{元素}赋值(比如int[] arr; arr = {1,2,3}; 错误);
-
增强for循环修改数组元素:误以为修改临时变量能改变原数组,实际无效;
-
数组长度修改错误:试图修改数组的length属性(比如arr.length = 10; 错误),数组长度一旦初始化就固定,无法修改;
-
空指针异常:数组未初始化(仅定义,未赋值),就访问数组元素(比如int[] arr; System.out.println(arr[0]); 错误)。
三、Java字符串:处理文本信息的核心工具
Java中,字符串用String类表示,String是一个引用类型(不是基本数据类型),用于封装文本信息。String类提供了大量便捷的方法,能快速实现字符串的拼接、截取、替换、判断等操作,是Java开发中最常用的类之一。
核心注意点:String类是不可变的(字符串一旦创建,内容就不能修改),这是String类的核心特性,也是新手容易踩坑的点,后续会详细讲解。
3.1 字符串的定义与初始化(3种常用方式)
字符串的初始化有3种方式,重点掌握前两种,理解"直接赋值"和"new关键字创建"的区别(面试高频考点)。
(1)方式1:直接赋值(推荐,简洁,会复用字符串常量池中的对象)
java
String 变量名 = "字符串内容";
// 示例
String name = "张三";
String message = "Hello Java!";
核心原理(面试考点):Java中有一个"字符串常量池"(内存中的一块区域),当用直接赋值方式创建字符串时,会先检查常量池中是否存在该字符串:如果存在,直接引用;如果不存在,创建一个新的字符串存入常量池,再引用。这样可以节省内存,避免重复创建相同的字符串。
(2)方式2:new关键字创建(不推荐,会创建新对象,不复用常量池)
java
String 变量名 = new String("字符串内容");
// 示例
String name = new String("张三");
String message = new String("Hello Java!");
核心原理(面试考点):用new关键字创建字符串时,无论常量池中是否存在该字符串,都会在内存中创建一个新的String对象,然后将对象的引用赋值给变量,不会复用常量池中的对象,会占用更多内存。
(3)方式3:通过字符数组创建(了解即可)
java
char[] chars = {'H', 'e', 'l', 'l', 'o'};
String message = new String(chars); // 将字符数组转为字符串
System.out.println(message); // 输出:Hello
面试题:String s1 = "abc"; 和 String s2 = new String("abc"); 的区别?
答案:
-
s1:直接赋值,先检查字符串常量池,若没有"abc",则在常量池中创建"abc",s1引用常量池中的对象;若有,直接引用,只创建1个对象(常量池中的对象);
-
s2:new关键字创建,无论常量池中是否有"abc",都会在内存中创建一个新的String对象,s2引用这个新对象,最多创建2个对象(常量池中的"abc"和新创建的对象)。
实战验证:
java
public class StringDemo1 {
public static void main(String[] args) {
String s1 = "abc";
String s2 = "abc";
String s3 = new String("abc");
String s4 = new String("abc");
// == 比较的是对象的内存地址(引用地址)
System.out.println(s1 == s2); // true(s1和s2引用常量池中的同一个对象)
System.out.println(s3 == s4); // false(s3和s4引用两个不同的对象)
System.out.println(s1 == s3); // false(s1引用常量池,s3引用新对象)
// equals() 比较的是字符串的内容(推荐用于字符串比较)
System.out.println(s1.equals(s2)); // true(内容相同)
System.out.println(s3.equals(s4)); // true(内容相同)
System.out.println(s1.equals(s3)); // true(内容相同)
}
}
避坑点4:比较两个字符串的内容,必须用equals()方法,不能用"==";"=="比较的是两个字符串的引用地址(是否是同一个对象),不是内容。
3.2 String类核心常用方法(重点,必须掌握)
String类提供了大量方法,新手重点掌握以下10个常用方法,能满足日常开发中的大部分需求,结合案例理解用法。
|----------|-----------|---------|---------|
| 方法名 | 方法作用 | 示例 | 结果 |
|------------------------------------------|-----------------------------|------------------------------------|-------------------|
| length() | 获取字符串的长度(字符个数) | "Hello".length() | 5 |
| equals(String str) | 比较两个字符串的内容是否相同(区分大小写) | "abc".equals("ABC") | false |
| equalsIgnoreCase(String str) | 比较两个字符串的内容是否相同(不区分大小写) | "abc".equalsIgnoreCase("ABC") | true |
| concat(String str) | 拼接两个字符串(等价于"+") | "Hello".concat(" Java") | "Hello Java" |
| charAt(int index) | 获取指定索引位置的字符(索引从0开始) | "Hello".charAt(1) | 'e' |
| | | | |
| | | | |
| replace(char oldChar, char newChar) | 替换字符串中的指定字符 | "Hello".replace('l','x') | "Hexxo" |
| isEmpty() | 判断字符串是否为空(长度为0) | "".isEmpty() | true |
| trim() | 去除字符串前后的空格(不去除中间的空格) | " Hello Java ".trim() | "Hello Java" |
实战演示(常用方法综合运用)
java
public class StringMethodDemo {
public static void main(String[] args) {
String str = " Hello Java! ";
// 1. 获取字符串长度
System.out.println("字符串长度:" + str.length()); // 输出14(包含前后空格)
// 2. 去除前后空格
String str2 = str.trim();
System.out.println("去除空格后:" + str2); // 输出:Hello Java!
System.out.println("去除空格后长度:" + str2.length()); // 输出11
// 3. 拼接字符串
String str3 = str2.concat(" 你好!");
System.out.println("拼接后:" + str3); // 输出:Hello Java! 你好!
// 4. 截取字符串(从索引6开始,截取到末尾)
String str4 = str2.substring(6);
System.out.println("截取后:" + str4); // 输出:Java!
// 5. 替换字符
String str5 = str2.replace('J', 'j');
System.out.println("替换后:" + str5); // 输出:Hello java!
// 6. 比较字符串
String str6 = "Hello Java!";
System.out.println(str2.equals(str6)); // true(内容相同)
System.out.println(str2.equalsIgnoreCase("hello java!")); // true(不区分大小写)
// 7. 获取指定索引的字符
char c = str2.charAt(0);
System.out.println("索引0的字符:" + c); // 输出:H
}
}
3.3 String不可变特性(核心考点,必懂)
String类的核心特性:不可变,即字符串一旦创建,其内容就不能被修改。我们平时写的"字符串修改"(比如拼接、替换),并不是修改原字符串的内容,而是创建了一个新的字符串对象,原字符串的内容依然不变。
实战演示(理解不可变):
java
public class StringImmutable {
public static void main(String[] args) {
String s = "Hello";
// 看似修改字符串,实际是创建了一个新的字符串对象
s = s.concat(" Java"); // 拼接后,创建新对象"Hello Java",s引用新对象
System.out.println(s); // 输出:Hello Java
// 原字符串"Hello"依然存在于常量池中,没有被修改
String s2 = "Hello";
System.out.println(s2); // 输出:Hello(原字符串未变)
}
}
四、数组与字符串的综合实战
结合数组和字符串的知识点,完成一个综合实战案例,提升代码综合运用能力。
案例:存储5名学生的姓名和成绩,完成以下操作:1. 录入学生信息;2. 打印所有学生信息;3. 求平均成绩;4. 找出成绩最高的学生;5. 查找指定姓名的学生成绩。
java
import java.util.Arrays;
import java.util.Scanner;
public class ArrayStringDemo {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int count = 5; // 学生人数
// 定义数组:存储学生姓名(String数组)和成绩(int数组)
String[] names = new String[count];
int[] scores = new int[count];
// 1. 录入学生信息
for (int i = 0; i < count; i++) {
System.out.print("请输入第" + (i+1) + "名学生的姓名:");
names[i] = scanner.next();
System.out.print("请输入第" + (i+1) + "名学生的成绩:");
scores[i] = scanner.nextInt();
}
// 2. 打印所有学生信息
System.out.println("\n所有学生信息:");
for (int i = 0; i < count; i++) {
System.out.println("姓名:" + names[i] + ",成绩:" + scores[i]);
}
// 3. 求平均成绩
int sum = 0;
for (int score : scores) {
sum += score;
}
double avg = sum / (double) count;
System.out.println("\n平均成绩:" + avg);
// 4. 找出成绩最高的学生
int maxScore = scores[0];
String maxName = names[0];
for (int i = 1; i < count; i++) {
if (scores[i] > maxScore) {
maxScore = scores[i];
maxName = names[i];
}
}
System.out.println("成绩最高的学生:" + maxName + ",成绩:" + maxScore);
// 5. 查找指定姓名的学生成绩
System.out.print("\n请输入要查找的学生姓名:");
String searchName = scanner.next();
boolean found = false;
for (int i = 0; i < count; i++) {
// 用equals()方法比较字符串(姓名)
if (names[i].equals(searchName)) {
System.out.println(searchName + "的成绩是:" + scores[i]);
found = true;
break;
}
}
if (!found) {
System.out.println("未找到姓名为" + searchName + "的学生");
}
scanner.close();
}
}
运行效果:
java
请输入第1名学生的姓名:张三
请输入第1名学生的成绩:90
请输入第2名学生的姓名:李四
请输入第2名学生的成绩:85
请输入第3名学生的姓名:王五
请输入第3名学生的成绩:95
请输入第4名学生的姓名:赵六
请输入第4名学生的成绩:78
请输入第5名学生的姓名:孙七
请输入第5名学生的成绩:88
所有学生信息:
姓名:张三,成绩:90
姓名:李四,成绩:85
姓名:王五,成绩:95
姓名:赵六,成绩:78
姓名:孙七,成绩:88
平均成绩:87.2
成绩最高的学生:王五,成绩:95
请输入要查找的学生姓名:李四
李四的成绩是:85
五、新手高频易错点总结(必看,避坑指南)
-
数组相关:数组越界、静态初始化简化写法错误、增强for循环修改元素无效、数组长度修改错误;
-
字符串相关:用"=="比较内容、忽略字符串不可变特性、索引越界、trim()方法误用、空指针异常;
-
综合易错:数组和字符串的索引都是从0开始,忘记这一点导致频繁报错;
-
面试易错:String直接赋值和new关键字创建的区别、String不可变的原理。
六、总结与下期预告
本篇文章重点讲解了Java数组与字符串的核心知识点:数组的定义、初始化、遍历和常见操作,String类的定义、常用方法和不可变特性,同时结合多个实战案例(数组排序、字符串回文判断、学生信息管理),拆解了新手高频易错点和面试高频考点,让你不仅会用,还能理解底层逻辑。