数组基础
数组是相同类型数据的有序集合。其中,每一个数据称作一个元素,每个元素可以通过一个索引(下标)来访问。
数组有以下特点:
- 长度是确定的。数组一旦被创建,它的大小就是不可以改变的
- 其元素的类型必须是相同类型,不允许出现混合类型
- 数组类型可以是任何数据类型,包括基本类型和引用类型
- 数组有索引的:索引从
0
开始,到数组名.length-1
结束 - 数组变量属于引用类型,数组也是对象,数组中的每个元素相当于该对象的成员变量。由于数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中存储的
内存分析
int[] arr = new int[4];
arr[0] = 12;
arr[3] = 47;
arr[2] = 98;
arr[1] = 56;
arr[2] = 66;
上述代码在内存空间中的活动如下图所示:
可以看到,首先在栈中生成 main()
方法的栈帧,里面存了 arr
变量和它的值 0x01
,而数组对象在堆中,0x01
就是数组首元素的地址,每个元素都被默认初始化为 0
,然后依次赋值和更改。
栈中存放局部变量和形参,堆中存放所有
new
出来的东西,方法区暂且不谈。
数组声明和初始化
数组声明
下面两种声明都可以,不过感觉用第一种较多:
int[] arr;
int arr[];
注意:如果数组只声明,没有后续操作,那么相当于没定义。这跟将数组赋值为 null
是不一样的。只声明而没有后续操作的语句反编译如下:
public class Test
{
public static void main(String[] args)
{
}
}
而将数组赋值为 null
的语句反编译如下:
public class Test
{
public static void main(String[] args)
{
int[] array = null;
}
}
数组初始化
数组初始化有三种方式:静态初始化、动态初始化和默认初始化。
静态初始化
静态初始化是在定义数组的同时就直接为数组元素分配空间并赋值:
// 完整形式:
int[] arr = new int[]{12,23,45};
// 简化形式(常用):
int[] arr = {12,23,45};
注意:
-
静态初始化不可指定数组长度,例如:
int[] arr = new int[3]{12,23,45}; // 错误!
-
声明和创建分开写时,不可以使用静态初始化的简化形式,而要用完整形式,例如:
// 错误! int[] arr1; arr1 = {12,23,45}; // 正确! int[] arr2; arr2 = new int[]{12,23,45};
动态初始化
动态初始化是指数组的定义和数组的赋值分开进行,例如:
int[] arr = new int[3];
arr[0] = 12;
arr[1] = 23;
arr[2] = 45;
默认初始化
数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。
数组默认的初始化值与数组类型相关:
byte[]
元素默认初始化为(byte)0
short[]
元素默认初始化为(short)0
int[]
元素默认初始化为0
long[]
元素默认初始化为0L
foat[]
元素默认初始化为0.0f
double[]
元素默认初始化为0.0d
char[]
元素默认初始化为'\0000'
boolean[]
元素默认初始化为false
- 引用数据类型元素默认初始化为
null
例如 int[] arr = new int[3];
语句将 arr
数组中每个元素默认初始化为 0
数组的遍历
数组的遍历一般有两种,分别是普通 for
循环遍历和增强 for
循环遍历。
-
普通
for
循环遍历// 正向遍历 for(int i=0;i<=9;i++){ System.out.println("第"+(i+1)+"个学生的成绩为:"+scores[i]); } // 反向遍历 for(int i=9;i>=0;i--){ System.out.println("第"+(i+1)+"个学生的成绩为:"+scores[i]); }
-
增强
for
循环遍历// 对scores数组进行遍历,遍历出来每个元素都用int类型的num接收 // 优点:代码简单 缺点:单纯的增强for循环不能涉及跟索引相关的操作 int count = 0; for(int num: scores){ count++; System.out.println("第"+count+"个学生的成绩为:"+num); }