首页 > 编程笔记 > Java笔记 阅读:10

Java一维数组的定义和使用(非常详细)

在 Java 程序开发过程中,有时候需要存储大量的同类型数据。例如,存储一个班级 50 名学生的姓名,这时需要定义 50 个变量来保存姓名数据,但这种做法太烦琐了。那么,如何解决这类问题呢?

Java 语言提供了数组结构,它类似于一个容器,可以批量存储相同数据类型的元素。

一维数组的逻辑结构是线性表,它是最简单的数组。使用一维数组时,要先定义,然后做初始化,最后才能使用。本节为大家详细讲解一维数组的具体用法。

Java一维数组的定义

在 Java 中创建一个数组,需要 3 个步骤:声明数组、为数组对象分配内存空间、为数组元素赋值。其中,声明数组和为数组对象分配内存空间的语法如下:
数据类型[] 数组名;          // 声明一维数组,这种是推荐的写法
数据类型 数组名[];          // 声明一维数组的第二种写法
数组名 = new 数据类型[数组元素个数];  // 为数组对象分配内存空间

数组声明之后,就需要为数组对象分配一个固定长度的存储空间。在为数组对象分配内存空间时,需要使用 new 运算符。通过 new 运算符可以告诉编译器声明的数组存储什么类型的数据,以及数组要存储的元素个数。数组一旦创建长度就固定了,不能再次改变。

针对数组的声明和内存分配,举例说明如下:
int[] nums;      // 声明数组名为nums的整型数组
nums = new int[10];  // 为nums数组分配存储10个整数的内存空间
第 1 行代码只是声明了一个整数类型的数组变量 nums,但是这个变量没有指向任何一个数组对象空间,声明的数组变量在 JVM 的栈内存中分配空间。

第 2 行代码先通过 new 运算符创建了一个长度为 10 的整型数组空间,创建的数组空间是在 JVM 的堆内存中分配空间的。接着把数组对象赋值给 nums,也就是将数组对象的地址存储到了 nums 变量中。此时,nums 变量就指向了这个数据对象,因此通过 nums 可以访问到数组对象空间中的每一个元素。对于数组声明和数组对象内存分配,可以参考下图。


图 1 数组声明和内存分配

图 1(a)声明了一个整型数组变量 nums,这个变量对应一个栈内存的空间。因为 nums 没有指向数组对象,所以 nums 存储的内容是null。

图 1(b)中在堆内存中创建了一个连续的长度为 10 的整型数组对象空间,数组对象的首地址是 0x8A21。图 1(b)中声明的数组变量 nums 存储了数组对象的首地址,因此 nums 变量指向了这个数组对象,与这个数组对象有了引用关联。通过 nums 变量可以用下标索引的方式,访问数组中的每个元素。

另外,也可以使用一条语句完成数组的声明和内存分配,语法格式如下:
数据类型[] 数组名 = new 数据类型[数组元素个数]
使用这种方式会在定义数组的同时为数组分配内存空间,举例说明如下:
int[] nums = new int[10];  // 声明数组的同时为数组分配内存空间
在这行代码中,等号左边的 nums 是声明的数组变量,它指向了右边使用 new 运算符创建的数组对象。

在 Java 中,数组对象被创建之后,数组中的元素都具有一个初始的默认值:

数组元素的分配

在创建数组对象之后,就可以存储数据到数组元素空间中,进行数组元素的分配,也就是为数组元素赋值。为数组元素赋值的方式有3种:静态初始化、动态初始化、通过数组下标为数组赋值。

1) 静态初始化

静态初始化就是在声明数组时,由开发者显式指定每个数组元素的初始值,初始值的类型要和定义数组的类型一致。根据这些初始值,系统会自动创建对应长度的数组空间,用于存储每个数组元素的数据。

静态初始化语法如下:
数据类型[] 数组名 = {数据1,数据2,数据3,…,数据n};   // 静态初始化,第1种方式
数据类型[] 数组名;
数组名 = new 数据类型[]{数据1,数据2,数据3,…,数据n};  // 静态初始化,第2种方式
第 1 种静态初始化的方式,需要在声明数组的同时进行数据初始化,初始化的数据要写在大括号中,并以逗号分隔。

第 2 种静态初始化的方式可以先声明数组变量,然后使用 new 运算符进行数组元素的初始化。另外,第 2 种方式中,右边表达式中的[ ]中不允许写数组长度,否则会发生语法错误。

静态初始化示例代码如下:
int[] nums = {10,20,30,40,50};                 // 声明数组并进行静态初始化
String[] names;                        // 声明数组变量
names = new String[]{"唐僧","孙悟空","猪八戒","沙和尚"};       // 进行静态初始化

2) 动态初始化

进行数组动态初始化时,开发者只需要指定数组长度,然后由系统自动为数组元素分配初始值。动态初始化的语法格式如下:
数据类型[] 数组名 = new 数据类型[数组长度];
在进行动态初始化后,程序会根据指定的数组长度,创建对应长度的数组元素空间,并为每个数组元素空间设置初始值。

动态初始化的示例代码如下:
int[] nums = new int[5];     // 创建长度为5的整型数组,数组元素的初始值都是0
String[] names = new String[3];  // 创建长度为3的字符串数组,数组元素的初始值都为null

3) 通过数组下标为数组赋值

在数组创建之后,可以使用数组名结合数组下标的方式,为数组空间中的每个元素赋值。

使用数组下标赋值的语法格式如下:
数据类型[] 数组名 = new 数据类型[数组长度];
数组名[下标1] = 数值1;
数组名[下标2] = 数值2;
…
数组名[数组长度-1] = 数值n;
在通过数组下标为数组元素赋值时,数组下标的取值范围从 0 到数组长度减 1 为止。下标超出这个范围,会发生“ArrayIndexOutOfBoundsException”数组下标越界的异常。

通过数组下标为元素赋值的代码示例如下:
String[] names = new String[4];  // 声明长度为4的字符串数组
names[0] = "唐僧";         // 通过下标为每个数组元素赋值
names[1] = "孙悟空";
names[2] = "猪八戒";
names[3] = "沙和尚";

数组元素的访问

数组创建之后,最常用的操作就是访问数组元素,这包含为数组元素赋值和输出数组元素中的值。访问数组元素的方式是通过数组名结合数组下标的方式完成的。

接下来,通过案例来演示如何访问数组元素。
public class Demo {
    public static void main(String[] args) {
        int[] nums = new int[3];    // 定义长度为3的整型数组
        nums[0] = 10;              // 为数组元素赋值
        nums[1] = 20;
        nums[2] = 30;
        System.out.println(nums[0]); // 输出数组元素中的值
        System.out.println(nums[1]);
        System.out.println(nums[2]);
    }
}
程序的运行结果如下:

10
20
30

程序中先创建了一个长度为 3 的整型数组,然后使用数组名结合下标的方式分别为 3 个数组元素赋值。注意,数组的下标必须写在中括号内。最后,输出每个数组元素内的数据。

length获取数组的长度

要获取数组的长度,可以通过数组对象的 length 属性。

每个数组都有 length 属性,当通过 length 属性获取了数组长度之后,就可以通过循环的方式,使用下标逐一遍历数组中的每个元素。

接下来,使用 length 获取数组长度,并通过循环遍历数组元素。
public class Demo {
    public static void main(String[] args) {    // 创建长度为3的数组对象
        int[] nums = new int[3];
        nums[0] = 10;                        // 为数组元素赋值
        nums[1] = 20;
        nums[2] = 30;
        for (int i = 0; i < nums.length; i++) {    // 使用length获取数组长度,作为循环条件
            System.out.println(nums[i]);          // 循环输出每个元素
        }
    }
}
程序的运行结果如下:

10
20
30

程序中先定义了长度为3的整型数组,然后使用数组名结合下标的方式为每个数组元素赋值,接着使用数组的 length 属性获取数组的长度,作为 for 循环的循环条件,最后通过 for 循环逐一遍历数组元素,并打印输出。

使用foreach遍历数组

除了使用 for 循环遍历数组外,Java 中还有另外一种更简洁的遍历方法:foreach 循环遍历。这种方式也称为增强 for 循环,它的功能比较强大,遍历时不需要依赖数组的长度和下标,即可实现数组遍历。

foreach 循环的语法格式如下:
for (数组中元素的类型 临时变量 : 数组对象变量) {
   程序语句;
}
通过上面的语法结构可以看出,foreach 循环遍历数组时不需要获取数组长度,也不需要用索引去访问数组中的元素,这是与 for 循环不同的地方。foreach 循环会自动将数组中的元素逐一取出,存入一个临时变量中,然后使用临时变量进行数据处理,从而完成数组元素的遍历。

接下来,通过案例来演示 foreach 循环遍历数组。
public class Demo {
    public static void main(String[] args) {
        String[] names = {"唐僧", "孙悟空", "猪八戒", "沙和尚"};    // 声明数组并进行初始化
        for (String name : names) {    // 使用foreach循环逐一取出数组元素并存入临时变量
            System.out.println(name);  // 输出临时变量存储的数据
        }
    }
}
程序的运行结果如下:

唐僧
孙悟空
猪八戒
沙和尚

首先定义了一个数组对象,并初始化了 4 个字符串数据。然后使用 foreach 循环遍历数组,每次循环时 foreach 都通过临时变量存储当前遍历到的元素,并将元素输出。

注意,foreach 循环代码简洁,编写方便,但是有其局限性,当使用 foreach 循环遍历数组时,只能访问其中的元素,不能对元素进行修改。

接下来,通过案例进一步演示在使用 foreach 循环遍历数组的过程中,对元素进行修改会有什么结果。
public class Demo {
    public static void main(String[] args) {
        String[] strs = new String[3];    // 创建一个长度为3的数组
        int i = 0;
        for (String str : strs) {        // 循环遍历数组
            str = new String("字符串:" + i);    // 修改每个遍历到的值
            i++;
        }
        for (String str : strs) {        // 打印数组中的值
            System.out.println(str);
        }
    }
}
程序的运行结果如下:

null
null
null

先定义了一个长度为 3 的字符串数组。然后通过第 1 个 foreach 循环将遍历到的每个数组元素的数据都进行了修改。但在第 2 个 foreach 循环中,遍历输出的每个元素依旧是 null。这说明在使用 foreach 循环遍历数组时,遍历的元素并没有真正被修改。原因是第 6 行中只是将临时变量 str 指向了一个新字符串,变量 str 和数组中的元素实际上没有任何联系。

所以,foreach 循环遍历数组的过程中无法修改所遍历的数据。因此,foreach并不能替代for循环,仅仅是让遍历的方法变得更简洁。

基本类型数组的初始化

按照数据类型的不同,数组可分为基本类型数组和引用类型数组。

基本类型数组的特点是,数组元素的值是直接存储在数组元素中的。所以,定义基本类型数组并初始化时,会先为数组分配内存空间,然后将数据直接存入对应的数组元素中。

基本类型数组的初始化示例如下图所示。


图 2 值类型数组的初始化和设值

图 2(a)定义了一个值类型数组,也就是整型数组 nums。该数组长度为 5,初始化后每个数组元素的值都是 0。图 2(b)是为 nums 数组中的元素都设置一个整数值。从图 2 中可以看出,值类型数组的数据都是直接存储在数组元素中的。

接下来,通过案例来演示值类型数组的初始化和设值。
public class Demo {
    public static void main(String[] args) {
        int[] nums = new int[5];    // 定义长度为5的数组,并动态初始化
        for (int n : nums) {       // 使用foreach输出每个元素值
            System.out.println(n);
        }
        System.out.println("==========");  // 输出分隔符

        nums[0] = 34;
        nums[1] = 21;
        nums[2] = 15;
        nums[3] = 56;
        nums[4] = 71;    // 为每个数组元素设置特定值

        for (int i = 0; i < nums.length; i++) {    // 使用for循环输出每个元素
            System.out.println(nums[i]);
        }
    }
}
程序的运行结果如下:

0
0
0
0
0
==========
34
21
15
56
71

程序中先定义了一个长度为 5 的整型数组。接着,使用 foreach 循环输出数组元素中的数据,此时数组中元素的数据都为 0。然后,通过数组名结合下标的方式,为每个数组元素设置特定数据,最后使用 for 循环将数组元素的数据逐一打印出来。

引用类型数组的初始化

引用类型数组中存储的是数据的引用地址。通过引用地址指向了实际存储数据的内存区域。

下面通过定义一个 Student 类型的数组来演示引用类型数组的使用。
class Student {    // 学生类
    String name;   // 姓名
    int age;       // 年龄
}

public class Demo {
    public static void main(String[] args) {
        Student[] stus = new Student[2];    // 创建长度为2的学生数组
        stus[0] = new Student();            // 为第1个数组元素存储学生对象
        stus[0].name = "张三";              // 设置学生对象的属性
        stus[0].age = 20;

        stus[1] = new Student();            // 为第2个数组元素存储学生对象
        stus[1].name = "李四";              // 设置学生对象的属性
        stus[1].age = 18;

        for (Student s : stus) {            // 使用foreach循环输出学生对象数据
            System.out.println(s.name + " " + s.age);
        }
    }
}
程序的运行结果如下:

张三 20
李四 18

程序中先定义了一个长度为 2 的 Student 类型的数组。接着,为每个数组元素存储一个学生类型的对象,并为存储的学生对象设置属性。然后,使用 foreach 循环输出数组元素中的学生对象,将学生的姓名和年龄打印出来。

为了便于大家更好地理解引用类型数组存储数据的特点,下面通过一个图例对引用类型数组的使用进行说明,如下图所示。


图 3 引用类型数组的数据存储方式

图 3(a)是引用类型数组定义并初始化的情况,引用类型数组的元素默认值都为 null,不指向任何数据。图 3(b)中,定义了学生类型的数组,数组长度为 2,数组包含两个元素。每个数组元素都存储了一个地址,分别指向不同的学生对象。

相关文章