数组(Array)
1. 数组概述
数组是用于储存多个相同类型数据的集合,数组中的元素被存储在一段连续的内存空间中。
Java中数组的特点:
- 数组中存储的数据类型必须相同;
- 数组既可以存储基本数据类型,也可以存储引用数据类型;
- 数组的长度固定(一旦数组对象初始化完毕,就不可以改变长度了)
2. 数组的定义和初始化
2.1 数组的定义
声明一个数组有两种方式:
第一种:数据类型[] 数组名
// 例如:
int[] iArr; // 定义了一个int[] 名字叫iArr,该数组就用于存储整数
double[] dArr;
第二种:数据类型 数组名[]
// 例如:
int iArr[];
double dArr[];
通常情况下都是使用第一种方式, 比较适合大多数人的习惯。
2.2 数组的初始化
2.2.1 动态初始化
动态初始化的意思是定义数组时指定数组的长度(即数组可以存储的元素个数),由系统给出数组中元素的默认初始化值。
格式:
数据类型[] 数组名 = new 数据类型[数组长度];
例如:
int[] arr = new int[5];
格式详解
-
等号左边:
-
int:数组的数据类型
-
[]:代表这是一个数组
- arr:代表数组的名称
-
-
等号右边:
-
new:为数组开辟内存空间
-
int:数组的数据类型
-
[]:代表这是一个数组
- 5:代表数组的长度
-
2.2.2 数组元素访问
数组元素用整个数组的名字和它自己在数组中的顺序位置来表示,这个用于区分数组的各个元素的数字编号称为索引(index),也称为下标。索引从0开始。
访问格式:
数组名[索引]
示例:
public class ArrayDemo {
public static void main(String[] args) {
int[] arr = new int[3];
// 输出数组名
System.out.println(arr); //[I@880ec60
// 输出数组中的元素
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
}
}
2.2.3 Java中的内存分配
程序,无论是代码还是数据,都需要存储在内存中,JVM为Java程序提供并管理所需要的内存空间。一般Java在内存分配时会涉及到以下区域:
区域名称 | 作用 |
---|---|
寄存器 | 给CPU使用,和我们开发无关。 |
本地方法栈 | JVM在使用操作系统功能的时候使用,和我们开发无关。 |
方法区 | 存储可以运行的class文件。 记录类中的所有信息。 |
堆内存 | 存储对象或者数组,new来创建的,都存储在堆内存。 |
方法栈 | 方法运行时使用的内存,比如main方法运行,进入方法栈中执行。 |
堆
-
线程特点: JVM只有一个堆区,在虚拟机启动时创建,被所有线程共享。
-
存放数据:堆中存放new出来的对象和数组,对象的成员变量同时在堆内存分配空间。
-
销毁机制:由JVM的垃圾回收器 (GC)自动管理。对象实例本身存放于堆内存,并不会随方法结束而销毁,且可以被其他变量引用,当没有任何变量引用该对象时,对象成为垃圾,等待垃圾回收器在合适的时间点回收。
-
数据特点:堆内存中的数据都有默认的初始化值
- 基本数据类型:
整数(byte short int long):0
小数(float double):0.0
字符(char):'\u0000' (\u开头表示是一个unicode码的字符,'\u0000'显示到控制台上是一个空格)
布尔类型(boolean):false
- 引用数据类型(数组 字符串 类 接口等等):null
栈
-
线程特点:每个线程包含一个栈区。
-
存放数据:栈中只保存局部变量中的基本数据类型和对象的引用,对象都存放在堆区中。
每个栈中的数据(原始类型 和 对象引用)都是私有的,其他栈不能访问。
-
数据特点:栈的优势是,存取速度比堆要快。每个方法执行时,该方法会建立自身的内存栈,也就是栈帧,以便于将该方法内定义的变量(局部变量)加入到栈帧中,方法结束时,栈帧销毁,该方法内的局部变量也随之销毁。
- 销毁机制:栈中没有垃圾回收机制,只有进栈和弹栈,进栈则分配内存,弹栈则释放内存(遵循后进先出的数据结构特点)。用完即销毁。
方法区
-
存放数据:方法区主要用于存放对象实例的类(Class文件)信息(对象的类型、父类、实现接口和方法本体等)、常量(常量池,内部含有字符串常量池)、静态(静态区,包含静态变量和静态方法)。
-
线程机制:方法区被所有线程共享。
-
销毁机制:垃圾收集行为在方法区很少出现,这块区域回收的主要目标是针对常量池的回收和对类型的卸载。
程序运行时,类信息加载;程序结束时,类信息卸载。
2.2.4 数组的内存图
2.2.5 数组静态初始化
数组静态初始化指的是:在创建数组时,直接将元素确定。
-
完整格式
数据类型[] 数组名 = new 数据类型[]{元素1,元素2,...};
-
简化格式
数据类型[] 数组名 = {元素1,元素2,...};
示例:
public class ArrayDemo {
public static void main(String[] args) {
//定义数组
int[] arr = new int[] {1, 2, 3};
// int[] arr = {1, 2, 3};
//输出数组名
System.out.println(arr);
//输出数组中的元素
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
}
}
2.2.6 使用数组的注意事项
数组索引越界异常
-
出现原因
public class ArrayDemo { public static void main(String[] args) { int[] arr = new int[3]; System.out.println(arr[3]); } }
数组长度为3,索引范围是0~2,但是我们却访问了一个3的索引。
程序运行后,将会抛出ArrayIndexOutOfBoundsException 数组索引越界异常。
-
解决方案
将错误的索引修改为正确的索引范围即可。
空指针异常
-
出现原因
public class ArrayDemo { public static void main(String[] args) { int[] arr = new int[3]; //把null赋值给数组 arr = null; System.out.println(arr[0]); } }
arr = null 这行代码,意味着变量arr将不会再保存数组的内存地址,也就不允许再操作数组了,因此运行的时候会抛出 NullPointerException 空指针异常。
-
解决方案
使用数组对象时先判断对象是否为空,或者保证数组对象真实存在。
3. 数组的常见操作
3.1 数组遍历
-
数组遍历:就是将数组中的每个元素分别获取出来,就是遍历。遍历也是数组的最基本操作。
public class ArrayTest01 { public static void main(String[] args) { //定义数组 int[] arr = {11, 22, 33, 44, 55}; //使用通用的遍历格式 for(int x = 0; x < arr.length; x++) { System.out.println(arr[x]); } } }
3.2 数组最值
-
最大值获取:从数组的所有元素中找出最大值。
-
实现思路:
- 定义变量,保存数组0索引上的元素
- 遍历数组,获取出数组中的每个元素
- 将遍历到的元素和保存数组0索引上值的变量进行比较
- 如果数组元素的值大于了变量的值,变量记录住新的值
- 数组循环遍历结束,变量保存的就是数组中的最大值
- 代码实现:
public class ArrayTest02 {
public static void main(String[] args) {
//定义数组
int[] arr = {12, 45, 98, 73, 60};
//定义一个变量,用于保存最大值
//取数组中第一个数据作为变量的初始值
int max = arr[0];
//与数组中剩余的数据逐个比对,每次比对将最大值保存到变量中
for(int x=1; x<arr.length; x++) {
if(arr[x] > max) {
max = arr[x];
}
}
//循环结束后打印变量的值
System.out.println("max:" + max);
}
}
二维数组
动态初始化
格式:
数据类型[][] 数组名 = new 数据类型[m][n];
m表示这个二维数组,可以存放多少个一维数组
n表示每一个一维数组,可以存放多少个元素
示例:
int[][] arr = new int[2][3];
该数组可以存放2个一维数组,每个一维数组中可以存放3个int类型元素
静态初始化
格式:
数据类型[][] 数组名 = new 数据类型[][] {{元素1,元素2},{元素1, 元素2}};
示例:
int[][] arr = new int[][]{{11,22},{33,44}};
简化格式:
数据类型[][] 数组名 = {{元素1,元素2}, {元素1, 元素2}};
示例:
int[][] arr = {{11,22},{33,44}};
数组中的元素访问
格式:
数组名[索引][索引];
示例:
int[][] arr = new int[][]{{11,22},{33,44}};
arr[1][0]; // 表示访问第2个(索引为1)小数组的第1个(索引为0)元素
二维数组遍历
已知一个二维数组 arr = { {11 , 22 , 33} , {33 , 44 , 55} }; 遍历该数组,取出所有元素并打印。
实现思路:
①:遍历二维数组,取出里面每一个一维数组
②:在遍历的过程中,对每一个一维数组继续完成遍历,获取内部存储的每一个元素
public static void main(String[] args) {
int[][] arr = {{11 , 22 , 33}, {44 , 55 , 66}};
// 遍历二维数组,得到每个一维数组
for (int i = 0; i < arr.length; i++) {
// 继续遍历每个一维数组,得到每个元素
for (int j = 0; j < arr[i].length; j++) {
System.out.print(arr[i][j] + "\t");
}
// 每次打印完一个小数组,加换行
System.out.println();
}
}