【java基础语法】------ 数组

1. 数组

在 Java 面向对象编程中,数组(Array) 是最基础、最常用的数据结构之一。它是一种容器 ,用于存储相同数据类型的多个值,是处理批量数据的核心工具。


1.1 数组概述

1.1.1 什么是数组?

数组是一种有序的、固定长度的容器,用于存储多个相同类型的元素。

  • 它可以看作是一个"盒子",里面装着若干个相同类型的数据;
  • 每个数据都有一个唯一的索引(下标) ,从 0 开始编号;
  • 数组一旦创建,其长度就不可改变(除非重新创建);
  • 所有元素必须是同一数据类型 (如全部为 intStringdouble)。

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 关键字;
  • 指定数组类型和初始值;
  • 编译器根据元素个数自动推断长度。

示例:

java 复制代码
int[] arr = new int[]{1, 12, 13, 14};

1.3.2 简化格式

java 复制代码
数据类型[] 数组名 = {元素1, 元素2, ...};
  • 省略 new 和类型重复,更简洁;
  • 是完整格式的语法糖(编译后等价);

示例:

java 复制代码
int[] arr = {1, 12, 13, 14};

1.3.3 数组的地址值

数组是对象,其变量存储的是堆内存中的地址值

当打印数组变量时,输出的是该地址的字符串表示形式,格式如下:

复制代码
[类型@十六进制地址]
  • [:表示这是一个数组;
  • 类型:如 I 表示 intD 表示 doubleLjava.lang.String; 表示 String
  • @:分隔符;
  • 十六进制字符串:真正的内存地址(JVM 生成)。

示例:

java 复制代码
int[] 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 定位到堆内存中的数组对象,分别将索引 01 处的默认值 0 替换为 1122,完成对数组元素的赋值操作。

打印覆盖后的元素
一维数组的静态初始化过程

注: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 的一维数组(地址 0x00220x0033),所有元素默认为 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},分别位于堆中不同位置。JVMrow1row2 的地址分别赋给 arr 的第 0 行和第 1 行,形成每行长度不同的"锯齿数组"。

二维数组的引用赋值

注:JVM 在堆中创建一个长度为 2 的二维数组对象(地址如 0x0011),其每一行都指向一个长度为 3 的一维数组,所有元素默认初始化为 0;栈中变量 arr 指向该对象。随后定义两个独立的一维数组 arr1 = {11, 22}arr2 = {44, 55, 66},分别存储在堆中(地址如 0x00AA0x00BB)。当执行 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 对象数组练习:

需求:

  1. 初始化数据 定义一个长度为3的数组。

    数组中存储1~3名学生对象作为初始数据。

    学生对象属性:学号(id)、姓名、年龄。

    要求:学号和姓名各不相同。

  2. 添加学生 添加一个新学生对象。

    添加前需判断学号是否唯一,若已存在则不能添加。

    在添加完成后,遍历并输出所有学生信息。

  3. 删除学生 通过学号(id)删除学生信息。

    若存在,则删除成功;若不存在,则提示"删除失败"。

    删除操作完成后,再次遍历所有学生信息。

  4. 查询并修改 查询指定学号的学生。

    如果存在,则将其年龄增加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());
            }
        }
    }
}
运行结果示例:
相关推荐
懂得节能嘛.21 小时前
【SDK开发实践】从Java编码到阿里云制品仓库部署
java·阿里云·maven
空空kkk21 小时前
SpringMVC——异常
java·前端·javascript
重整旗鼓~21 小时前
1.大模型使用
java·语言模型·langchain
sino爱学习1 天前
FastUtil 高性能集合最佳实践:让你的 Java 程序真正“快”起来
java·后端
.豆鲨包1 天前
【Android】 View事件分发机制源码分析
android·java
北京地铁1号线1 天前
数据结构:堆
java·数据结构·算法
百***86461 天前
Spring Boot应用关闭分析
java·spring boot·后端
tanxiaomi1 天前
Spring、Spring MVC 和 Spring Boot ,mybatis 相关面试题
java·开发语言·mybatis
弥巷1 天前
【Android】常见滑动冲突场景及解决方案
android·java
间彧1 天前
GraalVM 深度解析:下一代 Java 技术平台
java