Java基础 数组

5 阅读14分钟

一、数组的基本概念

1. 定义

数组是 Java 中用于存储固定个数、相同数据类型元素的有序集合,是一种基础的引用数据类型。数组将多个同类型数据按一定顺序排列,通过数组名 + 索引快速访问元素,实现数据的批量存储和管理。

2. 核心特征

  • 类型一致性:数组中所有元素数据类型必须相同,不可混合存储 int、String 等不同类型(除 Object 数组,基础阶段暂不涉及);
  • 长度固定性:数组一旦创建,长度不可修改,若需改变元素个数,需重新创建新数组;
  • 有序性:元素按创建顺序存储,通过索引(下标) 定位,索引从0开始,最大值为数组长度-1
  • 引用类型:数组本身是引用类型,存储在堆内存中,数组名是存储堆内存地址的引用变量,存于栈内存;
  • 元素可重复:数组中允许存储重复的元素值。

二、数组的声明与创建

1. 数组的声明

声明仅定义数组的类型和名称,未分配实际内存空间,不能指定长度,有两种标准语法格式(推荐格式 1,更易读)。

格式 1(推荐):数据类型 [] 数组名;

// 声明int类型数组
int[] arr1;
// 声明String类型数组
String[] arr2;
// 声明double类型数组
double[] arr3;

格式 2:数据类型 数组名 [];

// 与格式1等价,为兼容C/C++语法设计,不推荐
int arr4[];

2. 数组的创建

数组声明后需通过new关键字创建,分配实际内存空间,指定数组长度,创建后元素会被赋予对应数据类型的默认值

方式 1:声明后单独创建

// 声明
int[] arr;
// 创建:指定长度为5,分配内存,元素赋默认值0
arr = new int[5];

方式 2:声明与创建一体化(常用)

// 直接创建长度为3的String数组,默认值null
String[] arr = new String[3];

3. 数组的初始化

初始化是为数组元素显式赋值的过程,分为静态初始化动态初始化,初始化后数组长度由赋值元素个数 / 指定长度决定。

(1)动态初始化

先指定数组长度,由 JVM 为元素赋默认值,后续手动为每个元素赋值,适合提前知道元素个数,不知道具体值的场景。

// 动态初始化:创建长度为4的int数组,默认值[0,0,0,0]
int[] arr = new int[4];
// 手动为元素赋值,通过索引定位
arr[0] = 10;
arr[1] = 20;
arr[2] = 30;
arr[3] = 40;

各数据类型数组默认值

数据类型数组默认值
整数型(byte/short/int/long)0
浮点型(float/double)0.0
布尔型(boolean)false
字符型(char)'\u0000'(空字符)
引用类型(String / 类对象)null

(2)静态初始化

直接为数组赋值,由 JVM自动计算数组长度,无需指定,适合提前知道所有元素值的场景,有两种写法。

标准写法
// 静态初始化:JVM自动识别长度为3,元素为[1,2,3]
int[] arr1 = new int[]{1,2,3};
// 字符串数组,长度为2,元素为["Java","Array"]
String[] arr2 = new String[]{"Java","Array"};
简化写法(常用,仅声明 + 初始化一体时可用)
// 简化版,与标准写法等价,不可拆分写(声明后单独赋值报错)
int[] arr3 = {10,20,30,40};
double[] arr4 = {1.1,2.2,3.3};

4. 数组的创建与初始化注意事项

  • 静态初始化不能同时指定长度,如int[] arr = new int[3]{1,2,3};编译报错;
  • 简化版静态初始化仅能在声明时使用,如int[] arr; arr = {1,2,3};编译报错;
  • 数组长度必须是非负整数,指定长度为负数会抛出NegativeArraySizeException
  • 数组声明后未创建 / 初始化,直接使用会抛出NullPointerException(空指针异常)。

三、数组的访问与遍历

1. 数组的访问

通过数组名 [索引] 访问数组的单个元素,可进行取值赋值操作,索引范围必须是0 ~ 数组长度-1,超出范围会抛出ArrayIndexOutOfBoundsException(数组索引越界异常)。

取值:数组名 [索引]

int[] arr = {10,20,30};
// 取索引0的元素,结果为10
int num1 = arr[0];
// 取索引2的元素,结果为30
int num2 = arr[2];

赋值:数组名 [索引] = 数值;

int[] arr = new int[3];
// 为索引0赋值100
arr[0] = 100;
// 为索引2赋值300
arr[2] = 300;
// 数组变为[100,0,300]

2. 获取数组长度

通过数组名.length 获取数组的固定长度,返回值为 int 类型,是数组的属性(非方法,无小括号)。

int[] arr = {1,2,3,4,5};
// 获取数组长度,结果为5
int len = arr.length;
System.out.println("数组长度:" + len);

3. 数组的遍历

遍历即依次访问数组的所有元素,基础阶段常用for 循环增强 for 循环(for-each) ,前者可操作索引,后者仅用于遍历取值,不可修改元素。

方式 1:普通 for 循环(推荐,可操作索引)

通过索引从 0 遍历到arr.length-1,可同时实现取值和赋值,灵活度高。

int[] arr = {10,20,30,40};
// 普通for循环遍历,i为索引
for (int i = 0; i < arr.length; i++) {
    // 取索引i的元素
    System.out.println("索引" + i + "的元素:" + arr[i]);
    // 可为元素重新赋值,如每个元素+1
    arr[i] = arr[i] + 1;
}
// 遍历后数组变为[11,21,31,41]

方式 2:增强 for 循环(for-each,仅取值)

专门用于遍历集合 / 数组,无需索引,直接获取每个元素,仅能取值,不能修改元素(修改临时变量无意义),适合仅需遍历查看元素的场景。

int[] arr = {1,2,3,4};
// 增强for循环:num为数组中依次取出的元素
for (int num : arr) {
    System.out.println("数组元素:" + num);
    // 此处修改num不会改变原数组元素
    num = num * 2;
}

4. 数组遍历注意事项

  • 普通 for 循环的循环条件建议使用 arr.length,避免硬编码长度,数组长度修改时代码无需同步修改;
  • 增强 for 循环无索引,无法对数组元素进行赋值操作,也无法跳过 / 指定元素遍历;
  • 遍历过程中需严格控制索引范围,避免索引越界异常。

四、数组的常见操作

1. 数组的最值查找

遍历数组,通过临时变量记录当前最大值 / 最小值,依次与数组元素比较,更新临时变量,最终得到最值。

// 查找int数组的最大值
int[] arr = {18,5,99,34,77};
// 初始化最值为数组第一个元素
int max = arr[0];
int min = arr[0];
for (int i = 1; i < arr.length; i++) {
    // 找最大值
    if (arr[i] > max) {
        max = arr[i];
    }
    // 找最小值
    if (arr[i] < min) {
        min = arr[i];
    }
}
System.out.println("最大值:" + max); // 99
System.out.println("最小值:" + min); // 5

2. 数组的元素反转

将数组首尾元素依次交换,遍历到数组中间位置即可,核心公式:交换索引 i 和索引 arr.length-1-i 的元素

int[] arr = {1,2,3,4,5};
// 遍历到数组中间,i < arr.length/2
for (int i = 0; i < arr.length / 2; i++) {
    // 临时变量存储当前元素
    int temp = arr[i];
    // 首尾交换
    arr[i] = arr[arr.length - 1 - i];
    arr[arr.length - 1 - i] = temp;
}
// 反转后数组:[5,4,3,2,1]

3. 数组的元素查找(线性查找)

遍历数组,判断元素是否与目标值相等,找到则返回对应索引,未找到则返回指定标识(如 - 1)。

// 查找目标值20在数组中的索引
int[] arr = {10,20,30,40,20};
int target = 20;
// 初始化索引为-1(表示未找到)
int index = -1;
for (int i = 0; i < arr.length; i++) {
    if (arr[i] == target) {
        index = i;
        // 若需查找第一个目标值,找到后直接退出循环
        break;
        // 若需查找所有目标值,注释break,继续遍历
    }
}
if (index == -1) {
    System.out.println("元素未找到");
} else {
    System.out.println("元素索引:" + index); // 1
}

4. 数组的元素赋值 / 替换

遍历数组,根据条件为元素赋值或替换指定值,如将数组中所有 0 替换为 100。

int[] arr = {0,1,0,3,0,5};
for (int i = 0; i < arr.length; i++) {
    // 将所有0替换为100
    if (arr[i] == 0) {
        arr[i] = 100;
    }
}
// 替换后数组:[100,1,100,3,100,5]

五、多维数组(基础阶段重点:二维数组)

1. 定义

多维数组是数组的数组,即数组中的每个元素又是一个数组,基础阶段核心掌握二维数组,可理解为 “行 + 列” 的表格结构(如矩阵、表格数据)。

2. 二维数组的声明与创建

声明格式(推荐格式 1)

// 格式1:数据类型[][] 数组名;
int[][] arr1;
String[][] arr2;
// 格式2:数据类型 数组名[][];(不推荐)
int arr3[][];

创建与初始化

二维数组分为规则二维数组(每行元素个数相同)和不规则二维数组(每行元素个数不同),基础阶段以规则二维数组为主。

(1)动态初始化(规则二维数组)

指定行数列数,JVM 为元素赋默认值,格式:数据类型[][] 数组名 = new 数据类型[行数][列数];

// 动态初始化:3行2列的int二维数组,所有元素默认值0
int[][] arr = new int[3][2];
// 为指定行/列赋值:第1行第1列(索引[0][0])赋值10
arr[0][0] = 10;
// 第2行第2列(索引[1][1])赋值20
arr[1][1] = 20;
(2)静态初始化(规则 / 不规则均可)

直接为每行赋值,JVM 自动计算行数和列数,不规则数组可指定每行不同长度。

// 规则二维数组:3行2列,静态初始化
int[][] arr1 = {{1,2},{3,4},{5,6}};
// 不规则二维数组:3行,列数分别为1、2、3
int[][] arr2 = {{1},{2,3},{4,5,6}};

3. 二维数组的遍历

采用嵌套 for 循环,外层循环遍历,内层循环遍历,通过数组名[行索引][列索引]访问元素。

// 静态初始化3行2列二维数组
int[][] arr = {{1,2},{3,4},{5,6}};
// 外层循环:遍历行,i为行索引
for (int i = 0; i < arr.length; i++) {
    // 内层循环:遍历列,j为列索引,arr[i].length为当前行的列数
    for (int j = 0; j < arr[i].length; j++) {
        System.out.print(arr[i][j] + " ");
    }
    // 每行遍历完换行
    System.out.println();
}

遍历结果

1 2 
3 4 
5 6

六、数组的核心类与方法(Arrays 工具类)

1. Arrays 工具类概述

java.util.Arrays是 Java 提供的数组工具类,包含大量静态方法,用于实现数组的排序、查找、填充、比较、转字符串等常用操作,简化数组开发,使用前需导包import java.util.Arrays;

2. Arrays 工具类常用静态方法

方法名方法作用示例
Arrays.toString(数组)将一维数组转为字符串格式,方便打印输出int[] arr={1,2,3}; System.out.println(Arrays.toString(arr)); → 输出 [1,2,3]
Arrays.sort(数组)对数组进行升序排序(数值型按大小,字符串按 Unicode 码)int[] arr={3,1,2}; Arrays.sort(arr); → 数组变为 [1,2,3]
Arrays.fill(数组, 数值)将数组所有元素填充为指定数值int[] arr=new int[3]; Arrays.fill(arr, 5); → 数组变为 [5,5,5]
Arrays.fill(数组, 起始索引, 结束索引, 数值)将数组指定范围元素填充为指定数值(左闭右开:包含起始,不包含结束)int[] arr={1,2,3,4}; Arrays.fill(arr,1,3,0); → 数组变为 [1,0,0,4]
Arrays.equals(数组1, 数组2)比较两个数组长度和对应元素是否完全相同,相同返回 true,否则 falseint[] a={1,2}; int[] b={1,2}; Arrays.equals(a,b); → true
Arrays.binarySearch(数组, 目标值)已排序的数组进行二分查找,找到返回目标值索引,未找到返回负数(基础阶段暂不深入)int[] arr={1,2,3}; Arrays.binarySearch(arr,2); → 1

3. 常用方法代码示例

import java.util.Arrays;
public class ArraysDemo {
    public static void main(String[] args) {
        int[] arr = {5,2,9,1,3};
        // 1. 转为字符串打印
        System.out.println(Arrays.toString(arr)); // [5,2,9,1,3]
        // 2. 升序排序
        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr)); // [1,2,3,5,9]
        // 3. 填充所有元素为0
        Arrays.fill(arr, 0);
        System.out.println(Arrays.toString(arr)); // [0,0,0,0,0]
        // 4. 比较两个数组
        int[] arr1 = {1,2};
        int[] arr2 = {1,2};
        System.out.println(Arrays.equals(arr1, arr2)); // true
    }
}

七、数组的常见异常

数组开发中易出现 3 种运行时异常,需重点规避,异常均继承自RuntimeException,编译时不报错,运行时触发。

1. 空指针异常(NullPointerException)

触发场景:数组声明后未创建 / 初始化,直接使用数组(如访问元素、获取长度)。

// 仅声明,未创建
int[] arr;
// 触发空指针异常
System.out.println(arr.length);

规避:使用数组前必须确保已通过new创建并初始化。

2. 数组索引越界异常(ArrayIndexOutOfBoundsException)

触发场景:访问数组时,索引小于 0 或大于等于数组长度(index < 0 || index >= arr.length)。

int[] arr = {1,2,3};
// 索引3 >= 长度3,触发越界异常
System.out.println(arr[3]);

规避:遍历 / 访问数组时,索引严格控制在0 ~ arr.length-1范围内,循环条件建议使用arr.length

3. 负数组长度异常(NegativeArraySizeException)

触发场景:创建数组时,指定的长度为负数。

// 长度为-5,触发负数组长度异常
int[] arr = new int[-5];

规避:数组长度必须指定为非负整数,若通过变量指定长度,需先校验变量值是否合法。

八、数组的开发规范与注意事项

  1. 数组命名:遵循小驼峰命名法,见名知意,如scoreArr(成绩数组)、userNameArr(用户名数组),避免单字母命名(如 a、arr);
  2. 长度选择:创建数组时根据实际业务需求指定长度,避免创建过长的数组造成内存浪费;
  3. 遍历规范:普通 for 循环优先使用arr.length控制循环条件,避免硬编码长度,提高代码可维护性;
  4. 工具类使用:数组的排序、打印、填充等操作,优先使用java.util.Arrays工具类,避免重复编写代码;
  5. 空值校验:使用数组前先进行空值校验(if (arr != null)),避免空指针异常;
  6. 索引校验:通过变量作为索引访问数组时,先校验索引范围(if (index >=0 && index < arr.length)),避免索引越界;
  7. 不可变性处理:数组长度固定,若需频繁添加 / 删除元素,基础阶段可通过创建新数组实现,后续可使用集合(如 ArrayList)替代;
  8. 二维数组规范:开发中优先使用规则二维数组,不规则二维数组仅在特殊业务场景使用,保证代码可读性;
  9. 注释规范:创建数组时,若数组含义特殊(如存储成绩、年龄),添加单行注释说明数组用途,方便团队协作。

九、数组的经典应用示例

1. 数组存储学生成绩,计算平均分

// 存储5名学生的成绩
int[] scoreArr = {85,92,78,90,88};
int sum = 0;
// 遍历求和
for (int score : scoreArr) {
    sum += score;
}
// 计算平均分(强转为double避免整数除法)
double avg = (double) sum / scoreArr.length;
System.out.println("总成绩:" + sum); // 433
System.out.println("平均分:" + avg); // 86.6

2. 数组去重(基础版,保留第一个出现的元素)

int[] arr = {1,2,2,3,3,3,4};
// 临时数组存储去重结果,长度最大为原数组长度
int[] newArr = new int[arr.length];
// 记录去重后元素的个数
int count = 0;
for (int i = 0; i < arr.length; i++) {
    // 标记当前元素是否重复
    boolean isRepeat = false;
    // 遍历已存入新数组的元素,判断是否重复
    for (int j = 0; j < count; j++) {
        if (arr[i] == newArr[j]) {
            isRepeat = true;
            break;
        }
    }
    // 不重复则存入新数组
    if (!isRepeat) {
        newArr[count++] = arr[i];
    }
}
// 打印去重结果
System.out.print("去重后:");
for (int i = 0; i < count; i++) {
    System.out.print(newArr[i] + " "); // 1 2 3 4
}

3. 二维数组存储矩阵,实现矩阵的行求和

// 3行3列矩阵,存储二维数组
int[][] matrix = {{1,2,3},{4,5,6},{7,8,9}};
// 遍历每行,计算行和
for (int i = 0; i < matrix.length; i++) {
    int rowSum = 0;
    for (int j = 0; j < matrix[i].length; j++) {
        rowSum += matrix[i][j];
    }
    System.out.println("第" + (i+1) + "行和:" + rowSum);
}

运行结果

第1行和:6
第2行和:15
第3行和:24