1. 数组
在 Java 面向对象编程中,数组(Array) 是最基础、最常用的数据结构之一。它是一种容器 ,用于存储相同数据类型的多个值,是处理批量数据的核心工具。
1.1 数组概述
1.1.1 什么是数组?
数组是一种有序的、固定长度的容器,用于存储多个相同类型的元素。
- 它可以看作是一个"盒子",里面装着若干个相同类型的数据;
- 每个数据都有一个唯一的索引(下标) ,从
0开始编号; - 数组一旦创建,其长度就不可改变(除非重新创建);
- 所有元素必须是同一数据类型 (如全部为
int、String或double)。
1.2 数组定义
1.2.1 定义格式
Java 支持两种等价的语法来定义数组,推荐使用第一种,更符合"类型 + []"的阅读习惯。
| 格式 | 示例 |
|---|---|
| 格式一:数据类型 [] 数组名 | int[] array; |
| 格式二:数据类型 数组名 [] | int array[]; |
1.2.2 类型匹配规则
数组作为容器,其数据类型决定了能存储哪些值 。Java 在存储数据时会自动进行隐式类型转换(如果允许),但需注意边界。
| 数组类型 | 可存储的数据类型 | 说明 |
|---|---|---|
int[] |
byte, short, int, char |
自动提升(隐式转换) |
double[] |
byte, short, int, long, float, double |
全部可存储,自动提升 |
1.3 静态初始化
静态初始化是指在定义数组时,直接指定元素值,由 JVM 自动确定数组长度并完成内存分配。
1.3.1 完整格式
java
数据类型[] 数组名 = new 数据类型[]{元素1, 元素2, ...};
- 明确使用
new关键字; - 指定数组类型和初始值;
- 编译器根据元素个数自动推断长度。
示例:
javaint[] arr = new int[]{1, 12, 13, 14};
1.3.2 简化格式
java
数据类型[] 数组名 = {元素1, 元素2, ...};
- 省略
new和类型重复,更简洁; - 是完整格式的语法糖(编译后等价);
示例:
javaint[] arr = {1, 12, 13, 14};
1.3.3 数组的地址值
数组是对象,其变量存储的是堆内存中的地址值 。
当打印数组变量时,输出的是该地址的字符串表示形式,格式如下:
[类型@十六进制地址]
[:表示这是一个数组;类型:如I表示int,D表示double,Ljava.lang.String;表示String;@:分隔符;- 十六进制字符串:真正的内存地址(JVM 生成)。
示例:
javaint[] arr = {1, 2, 3}; System.out.println(arr); // 输出:[I@776ec8df解释:
[I:表示int类型的数组;@776ec8df:十六进制地址,唯一标识该数组对象。
1.3.4 示例代码
java
package com.itheima.demo1;
public class ArrayDemo1 {
public static void main(String[] args) {
// 数组静态初始化:
// 完整格式:数据类型[] 数组名 = new 数据类型[]{元素...};
// 简化格式:数据类型[] 数组名 = {元素...};
int[] arr1 = new int[]{1, 12, 13, 14};
int[] arr2 = {1, 12, 13, 14};
String[] arr3 = new String[]{"张三", "李四", "王五"};
String[] arr4 = {"张三", "李四", "王五"};
double[] arr5 = new double[]{1.1, 2.2, 3.3, 4.4};
double[] arr6 = {1.1, 2.2, 3.3, 4.4};
}
}
1.4 数组元素访问
数组中的每个元素通过 索引(下标) 访问,索引从 0 开始,依次递增。
1.4.1 访问格式:
java
数组名[索引]
- 读取元素 :
int num = arr[0]; - 修改元素 :
arr[0] = 100;
1.4.2 示例代码:
java
package com.itheima.demo1;
public class ArrayDemo2 {
public static void main(String[] args) {
// 定义并初始化数组
int[] arr = {1, 12, 13, 14};
// 获取元素:通过索引访问(从0开始)
int num = arr[0];
System.out.println(num);
// 修改元素:通过索引赋值
arr[0] = 100;
System.out.println(arr[0]);
}
}

1.5 数组遍历
数组遍历是指依次访问数组中所有元素的过程,常用于打印、求和、统计等操作。
1.5.1常用方式:for 循环
java
for (int i = 0; i < arr.length; i++) {
// 处理 arr[i]
}
1.5.2 示例1:求和
java
package com.itheima.demo1;
public class ArrayTest1 {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
System.out.println("和为:" + sum);
}
}

1.5.3 示例2:统计满足条件的元素个数
java
package com.itheima.demo1;
public class ArrayTest2 {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int count = 0;
for (int i = 0; i < arr.length; i++) {
if (arr[i] % 3 == 0) {
count++;
}
}
System.out.println("个数为:" + count);
}
}

1.6 动态初始化
动态初始化是在创建数组时指定长度,由系统自动分配默认初始值。
1.6.1 格式:
java
数据类型[] 数组名 = new 数据类型[长度];
1.6.2 默认初始化值规律:
| 类型 | 默认值 |
|---|---|
| 整数(int, byte, short, long) | 0 |
| 小数(float, double) | 0.0 |
| 字符(char) | '\u0000'(空格) |
| 布尔(boolean) | false |
| 引用类型(String, 对象) | null |
1.6.3 示例代码:
java
package com.itheima.demo1;
public class ArrayTest4 {
public static void main(String[] args) {
// 动态初始化:指定长度,系统赋默认值
String[] arr = new String[50];
arr[0] = "hello";
arr[1] = "world";
System.out.println(arr[0]); // 输出:hello
System.out.println(arr[1]); // 输出:world
System.out.println(arr[2]); // 输出:null(未赋值)
}
}

1.7 动态 vs 静态初始化的区别
| 特性 | 动态初始化 | 静态初始化 |
|---|---|---|
| 指定内容 | 只指定长度 | 指定具体元素 |
| 使用场景 | 元素未知,只知数量(如键盘输入) | 元素已知且明确 |
| 示例 | int[] arr = new int[5]; |
int[] arr = {11, 22, 33}; |
小结:
- 动态:先占位,后赋值;
- 静态:直接赋值,无需手动设置长度。
1.8 索引越界异常
当访问的索引超出数组合法范围时,会抛出 ArrayIndexOutOfBoundsException。
1.8.1 错误示例:
java
package com.itheima.demo1;
public class ArrayTest5 {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
System.out.println(arr[10]); // 错误!索引最大为 4
}
}

避免方法:
- 使用
arr.length控制循环边界;- 在访问前判断索引是否有效。
1.9 内存分析
1.9.1 Java 内存分配

Java 程序运行时,内存主要划分为以上几个区域,其中与数组和变量密切相关的主要是 栈内存(Stack) 和 堆内存(Heap):
1.9.1.1 栈内存(Stack)
- 用于存储局部变量(包括基本数据类型变量和引用类型变量);
- 方法调用时,会在栈中创建一个"栈帧",方法执行完毕后自动释放;
- 特点:存储速度快、生命周期短、空间有限。
2. 堆内存(Heap)
- 用于存储 new 创建的对象和数组;
- 所有通过
new关键字创建的实体(如new int[3]、new String[5])都存在于堆中; - 特点:空间大、由垃圾回收器(GC)自动管理、生命周期由引用决定。
3. 方法区(Method Area,含常量池)
- 存储类信息、静态变量、常量(如字符串字面量
"hello")等; - 在 JDK 8 之后,方法区由 元空间(Metaspace) 实现。

1.9.2 一维数组内存分析
1.9.2.1 正常一维数组
一维数组的动态初始化过程

注:通过
new int[2]在堆内存中创建一个长度为2的整型数组,系统自动为每个元素赋予默认初始值0;同时,在栈内存中声明一个数组引用变量arr,并将其指向堆中新建的数组对象,从而完成数组的创建。
一维数组地址的打印

一维数组具体元素的打印

注:程序执行
sout(arr[1])时,JVM 根据栈中引用变量 arr 指向的堆内存地址,定位到数组对象,并通过索引1访问该位置存储的元素值(初始为 0),最终将该值输出至控制台。
一维数组具体元素的覆盖

注:程序执行
arr[0] = 11;和arr[1] = 22;时,JVM 通过栈中引用变量arr定位到堆内存中的数组对象,分别将索引0和1处的默认值0替换为11和22,完成对数组元素的赋值操作。
打印覆盖后的元素

一维数组的静态初始化过程

注:JVM 在堆内存中创建一个长度为
3的新数组对象,并将初始值{33, 44, 55}直接赋给对应位置。同时,在栈内存中声明引用变量 arr2,并将其指向该堆中数组的地址(如0x4c966a),完成静态初始化过程。
一维数组地址的打印

一维数组具体元素的打印

1.9.2.2 两个一维数组指向同一块空间
静态初始化一个一维数组

将一维数组arr1 的 地址值赋值给另一个一维数组arr2

通过两种方式访问同一个元素

元素覆盖

通过两种方式访问同一个被覆盖的元素

小结
- 当两个数组引用指向堆内存中的同一块数组空间时,它们共享同一个数组对象。
- 对其中一个数组的元素进行修改,实质上是修改了堆中共享的数据,因此另一个数组再次访问该元素时,得到的将是修改后的结果。
1.10 二维数组
1.10.1 静态初始化
静态初始化是指在声明时直接指定所有元素的值
-
完整格式 :
java数据类型[][] 数组名 = new 数据类型[][]{{元素...}, {元素...}}; -
简化格式(推荐) :
java数据类型[][] 数组名 = {{元素...}, {元素...}};
建议:每个一维数组单独占一行,提升代码可读性:
1.10.2 动态初始化
动态初始化是在声明时指定数组的行数和列数,系统自动分配空间并用默认值填充。
-
格式 :
java数据类型[][] 数组名 = new 数据类型[行数][列数];m表示可以存放多少个一维数组(即行数)n表示每个一维数组可存放多少个元素(即列数)
1.10.3 数组内存图分析
1.10.3.1 正常情况
二维数组的内存结构(动态初始化)

注:执行
int[][] arr = new int[2][3];时,JVM 在堆中创建一个长度为 2 的二维数组对象(地址如0x0011),其包含两个引用,分别指向两个长度为 3 的一维数组(地址0x0022和0x0033),所有元素默认为 0;栈中变量arr指向该二维数组对象。
访问第一行数组

注:
arr[0]表示获取二维数组的第一行一维数组的引用,即指向堆中地址0x0022的数组对象,可进一步访问其元素。
访问指定元素

注:
arr[0][1]表示先通过arr[0]定位到第一行数组(0x0022),再访问该数组索引为 1 的元素,对应堆中值为 0 的位置。
1.10.3.2 特殊情况
二维数组的不规则结构(锯齿数组)

注:JVM 在堆中创建一个长度为
2的二维数组对象(地址如0x0011),其两个元素初始为null;栈中引用arr指向该对象。随后创建两个长度不同的数组arr1 = {11, 22}和arr2 = {44, 55, 66},分别位于堆中不同位置。JVM将row1和row2的地址分别赋给arr的第 0 行和第 1 行,形成每行长度不同的"锯齿数组"。
二维数组的引用赋值

注:
JVM在堆中创建一个长度为2的二维数组对象(地址如0x0011),其每一行都指向一个长度为3的一维数组,所有元素默认初始化为0;栈中变量arr指向该对象。随后定义两个独立的一维数组arr1 = {11, 22}和arr2 = {44, 55, 66},分别存储在堆中(地址如0x00AA和0x00BB)。当执行arr[0] = arr1;和arr[1] = arr2;时,JVM将这两个一维数组的地址赋给arr的第0行和第1行,覆盖了原本由new int[3]创建的数组,最终形成一个每行长度不同的不规则二维数组结构。
1.10.4 基础代码示例
java
package com.itheima.demo1;
public class ArrayTest7 {
public static void main(String[] args) {
//1.静态初始化
//完整格式:数据类型[][] 数组名 = new 数据类型[][]{{元素...},{元素...}};
int[][] arr1 =new int[][]{{1,2,3},{4,5,6}};
//简化格式:数据类型[][] 数组名 = {{元素...},{元素...}};
int[][] arr2 = {{1,2,3},{4,5,6}};
//建议每个一维数组占一行,方便阅读
int[][] arr3={
{1,2,3},
{4,5,6,7,8}
};
//2.动态初始化
//完整格式:数据类型[][] 数组名 = new 数据类型[行数][列数];
//表示创建一个含有3个一维数组(每个一维数组有5个元素)的二维数组
int[][] arr4=new int[3][5];
//3.获取数组元素
//获取数组中索引为0的元素(即第一个一维数组的地址值)
System.out.println(arr1[0]);
//arr[0]: 获取第一个一维数组的地址
//[0]: 获取第一个一维数组中索引为0的元素 " 1 "
System.out.println(arr1[0][0]);
//4.遍历
// 外层循环:遍历二维数组 arr3 的每一行
for (int i = 0; i < arr3.length; i++) {
// 内层循环:遍历当前行 arr3[i] 中的每一个元素
for (int j = 0; j < arr3[i].length; j++) {
// 打印当前元素 arr3[i][j]
System.out.print(arr3[i][j] + " ");
}
}
}
}

注意:
arr3.length:获取二维数组的行数;arr3[i].length:获取第 i 行一维数组的长度(支持不规则二维数组);- 使用嵌套循环遍历每一行、每一列的元素。
1.10.5 综合示例

java
package com.itheima.demo1;
public class ArrayDemo4 {
public static void main(String[] args) {
// 定义一个二维数组,表示一年中4个季度(每行一个季度),每个季度有3个月的营业额
int[][] yearArrArr = {
{22, 66, 44}, // 第一季度
{77, 33, 88}, // 第二季度
{25, 45, 65}, // 第三季度
{11, 66, 99} // 第四季度
};
int yearSum = 0; // 用于累计全年总营业额
// 遍历每个季度(外层循环)
for (int i = 0; i < yearArrArr.length; i++) {
int[] quarterArr = yearArrArr[i]; // 获取第 i 个季度的月营业额数组
int sum = getSum(quarterArr); // 调用方法计算该季度总营业额
System.out.println("第" + (i + 1) + "个季度的总营业额为:" + sum);
yearSum += sum; // 累加到全年总营业额
}
System.out.println("总营业额为:" + yearSum); // 输出全年总营业额
}
// 求单个季度(即一个一维数组)的营业额总和
public static int getSum(int[] arr) {
int sum = 0;
// 遍历数组中的每个月营业额并累加
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum; // 返回该季度总营业额
}
}

1.11 对象数组练习:
需求:
初始化数据 定义一个长度为3的数组。
数组中存储1~3名学生对象作为初始数据。
学生对象属性:学号(id)、姓名、年龄。
要求:学号和姓名各不相同。
添加学生 添加一个新学生对象。
添加前需判断学号是否唯一,若已存在则不能添加。
在添加完成后,遍历并输出所有学生信息。
删除学生 通过学号(id)删除学生信息。
若存在,则删除成功;若不存在,则提示"删除失败"。
删除操作完成后,再次遍历所有学生信息。
查询并修改 查询指定学号的学生。
如果存在,则将其年龄增加1岁。
修改操作完成后,再次遍历所有学生信息。
代码实现:

Student类的定义:
java
package test5;
public class Student {
private int id;
private String name;
private int age;
//构造方法
public Student() {
}
public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
//getter和setter方法
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
需求1,2的实现
java
package test5;
import java.util.Scanner;
public class StudentTest1 {
public static void main(String[] args) {
// 创建长度为3的学生数组,并初始化3个学生对象
Student[] arr = new Student[3];
Student stu1 = new Student(1, "张三", 23);
Student stu2 = new Student(2, "李四", 24);
Student stu3 = new Student(3, "王五", 25);
arr[0] = stu1;
arr[1] = stu2;
arr[2] = stu3;
// 尝试添加新学生(id=4)
Student stu4 = new Student(4, "林六", 27);
// 检查id是否已存在
if (contains(arr, stu4)) {
System.out.println("当前id重复,请修改之后再添加");
} else {
int count = getLength(arr); // 获取当前有效元素个数
if (count == arr.length) { // 数组已满,需扩容
Student[] newArr = addArr(arr); // 扩容数组
newArr[count] = stu4; // 添加新元素
printArr(newArr); // 打印结果
} else {
arr[count] = stu4; // 直接添加到第一个空位
printArr(arr); // 打印结果
}
}
}
// 打印数组中所有非null学生信息
public static void printArr(Student[] arr) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] != null) {
System.out.println(arr[i].getId() + ", " + arr[i].getName() + ", " + arr[i].getAge());
}
}
}
// 扩容:创建一个比原数组长1的新数组,并复制元素
public static Student[] addArr(Student[] arr) {
Student[] newArr = new Student[arr.length + 1];
for (int i = 0; i < arr.length; i++) {
newArr[i] = arr[i];
}
return newArr;
}
// 判断数组中是否已存在相同id的学生
public static boolean contains(Student[] arr, Student stu) {
int id = stu.getId();
for (int i = 0; i < arr.length; i++) {
if (arr[i] != null && id == arr[i].getId()) {
return true;
}
}
return false;
}
// 统计数组中非null元素的个数
public static int getLength(Student[] arr) {
int count = 0;
for (int i = 0; i < arr.length; i++) {
if (arr[i] != null) {
count++;
}
}
return count;
}
}
运行结果示例:

需求3,4的实现
java
package test5;
import java.util.Scanner;
public class StudentTest2 {
public static void main(String[] args) {
// 创建长度为3的学生数组,并初始化3个学生对象
Student[] arr = new Student[3];
arr[0] = new Student(1, "张三", 23);
arr[1] = new Student(2, "李四", 24);
arr[2] = new Student(3, "王五", 25);
// 删除操作:读取用户输入的id
System.out.println("请输入要删除信息的学生id:");
Scanner sc = new Scanner(System.in);
int deleteId = sc.nextInt();
// 查找对应id的索引
int deleteIndex = getIndex(deleteId, arr);
if (deleteIndex >= 0) {
arr[deleteIndex] = null; // 删除:置为null
} else {
System.out.println("没有此id,删除失败");
}
printArr(arr); // 打印删除后的数组
// 修改操作:读取要修改的学生id
System.out.println("请输入要修改信息的学生id:");
int checkId = sc.nextInt();
int checkIndex = getIndex(checkId, arr);
if (checkIndex >= 0) {
// 示例修改:年龄+1
arr[checkIndex].setAge(arr[checkIndex].getAge() + 1);
} else {
System.out.println("没有此id,修改失败");
}
printArr(arr); // 打印修改后的数组
}
// 根据id查找学生在数组中的索引,未找到返回-1
public static int getIndex(int id, Student[] arr) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] != null && id == arr[i].getId()) {
return i;
}
}
return -1;
}
// 打印数组中所有非null学生的信息
public static void printArr(Student[] arr) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] != null) {
System.out.println(arr[i].getId() + ", " + arr[i].getName() + ", " + arr[i].getAge());
}
}
}
}
运行结果示例:
