【java基础语法】------ 数组
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 完整格式
数据类型[] 数组名 = new 数据类型[]{元素1, 元素2, ...};
- 明确使用
new关键字; - 指定数组类型和初始值;
- 编译器根据元素个数自动推断长度。
示例:
int[] arr = new int[]{1, 12, 13, 14};
1.3.2 简化格式
数据类型[] 数组名 = {元素1, 元素2, ...};
- 省略
new和类型重复,更简洁; - 是完整格式的语法糖(编译后等价);
示例:
int[] arr = {1, 12, 13, 14};
1.3.3 数组的地址值
数组是对象,其变量存储的是堆内存中的地址值。
当打印数组变量时,输出的是该地址的字符串表示形式,格式如下:
[类型@十六进制地址]
[:表示这是一个数组;类型:如I表示int,D表示double,Ljava.lang.String;表示String;@:分隔符;- 十六进制字符串:真正的内存地址(JVM 生成)。
示例:
int[] arr = {1, 2, 3}; System.out.println(arr); // 输出:[I@776ec8df解释:
[I:表示int类型的数组;@776ec8df:十六进制地址,唯一标识该数组对象。
1.3.4 示例代码
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 访问格式:
数组名[索引]
- 读取元素:
int num = arr[0]; - 修改元素:
arr[0] = 100;
1.4.2 示例代码:
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 循环
for (int i = 0; i < arr.length; i++) {// 处理 arr[i]
}
1.5.2 示例1:求和
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:统计满足条件的元素个数
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 格式:
数据类型[] 数组名 = new 数据类型[长度];
1.6.2 默认初始化值规律:
| 类型 | 默认值 |
|---|---|
| 整数(int, byte, short, long) | 0 |
| 小数(float, double) | 0.0 |
| 字符(char) | ‘\u0000’(空格) |
| 布尔(boolean) | false |
| 引用类型(String, 对象) | null |
1.6.3 示例代码:
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]); // 输出:helloSystem.out.println(arr[1]); // 输出:worldSystem.out.println(arr[2]); // 输出:null(未赋值)}
}

1.7 动态 vs 静态初始化的区别
| 特性 | 动态初始化 | 静态初始化 |
|---|---|---|
| 指定内容 | 只指定长度 | 指定具体元素 |
| 使用场景 | 元素未知,只知数量(如键盘输入) | 元素已知且明确 |
| 示例 | int[] arr = new int[5]; | int[] arr = {11, 22, 33}; |
小结:
- 动态:先占位,后赋值;
- 静态:直接赋值,无需手动设置长度。
1.8 索引越界异常
当访问的索引超出数组合法范围时,会抛出 ArrayIndexOutOfBoundsException。
1.8.1 错误示例:
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 静态初始化
静态初始化是指在声明时直接指定所有元素的值
- 完整格式:
数据类型[][] 数组名 = new 数据类型[][]{{元素...}, {元素...}}; - 简化格式(推荐):
数据类型[][] 数组名 = {{元素...}, {元素...}};
建议:每个一维数组单独占一行,提升代码可读性:
1.10.2 动态初始化
动态初始化是在声明时指定数组的行数和列数,系统自动分配空间并用默认值填充。
- 格式:
数据类型[][] 数组名 = 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 基础代码示例
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 综合示例

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类的定义:
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的实现
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的实现
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);// 删除操作:读取用户输入的idSystem.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); // 打印删除后的数组// 修改操作:读取要修改的学生idSystem.out.println("请输入要修改信息的学生id:");int checkId = sc.nextInt();int checkIndex = getIndex(checkId, arr);if (checkIndex >= 0) {// 示例修改:年龄+1arr[checkIndex].setAge(arr[checkIndex].getAge() + 1);} else {System.out.println("没有此id,修改失败");}printArr(arr); // 打印修改后的数组}// 根据id查找学生在数组中的索引,未找到返回-1public 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());}}}
}
运行结果示例:

