数组
此笔记参考黑马教程,仅学习使用,如有侵权,联系必删
文章目录
- 数组
- 1. 认识数组
- 2. 数组的定义和访问
- 2.1 静态初始化数组
- 2.1.1 数组的访问
- 2.1.1 定义
- 代码实现
- 总结
- 2.1.2 数组的遍历
- 2.1.2.1 定义
- 代码演示
- 总结
- 案例
- 代码实现
- 2.2 动态初始化数组
- 2.2.1 定义
- 2.2.2 动态初始化数组元素默认值规则
- 代码实现
- 总结
- 案例
- 代码实现
- 3. 数组在计算机中的执行原理
- 3.1 数组的执行原理,Java 程序的执行原理
- 3.1.1 Java 内存分配介绍
- 3.1.2 方法区
- 3.1.3 栈
- 3.1.4 堆
- 3.1.5 数组在计算机中的执行原理
- 总结
- 3.2 多个变量指向同一个数组的问题
- 3.2.1 使用数组时常见的一个问题
- 代码实现
- 总结
- 专项训练:数组常见案例
- 数组求最值
- 分析
- 代码实现
- 总结
- 数组反转
- 需求 - 分析
- 代码实现
- 总结
- 随机排名
- 代码实现
- 总结
- 补充知识:Debug 工具的使用
- 总结
1. 认识数组
-
数组就是一个容器,用来存储一批同种类型的数据
-
例子:
// 20, 10, 80, 60, 90
int[] arr = {20, 10, 80, 60, 90};
// 张三, 李四, 王五
String[] names = {"张三", "李四", "王五"};
Java 中有变量,为什么还要用数组?
-
eg:在一组名单中随机点名的话,根据以往方法可能要定义几十上百个变量,会使得代码繁琐、实现需求复杂。而用数组的话代码简洁、逻辑清晰
-
结论:遇到批量数据的存储和操作时,数组比变量更合适
2. 数组的定义和访问
2.1 静态初始化数组
- 定义数组的时候直接给数组赋值
静态初始化数组的格式:
// 完整格式
数据类型[] 数组名 = new 数据类型[]{元素1, 元素2, 元素3, ...};
int[] ages = new int[]{12, 24, 36};
double[] scores = new double[]{89.9, 99.5, 59.5, 88.0};
// 简化格式
数据类型[] 数组名 = {元素1, 元素2, 元素3, ...};
int[] ages = {12, 24, 36};
注意:
-
”数据类型[] 数组名“ 也可以写成 ”数据类型 数组名[]“
-
什么类型的数组只能存放什么类型的数据
-
数组在计算机中的基本原理
- 在计算机遇到代码之后,现在内存中开辟一块变量空间,暂时先不装东西。元素部分又开辟一个区域,每个元素各分为一块来存储数据,而这块区域是有自己的一个地址的,每个元素都有自己的编号(索引),然后将这个地址交给数组变量来存储
注意:数组变量名中存储的是数组在内存中的地址,数组是一种引用数据类型
- 代码演示
package e_ArrayApp;
public class a_ArrayDemo1 {
public static void main(String[] args) {
// 目标:掌握数组的定义方式一:静态初始化数组
// 1. 数据类型[] 数组名 = new 数据类型[]{元素1, 元素2, 元素3, ...};
int[] ages = new int[]{12, 24, 36};
double[] scores = new double[]{89.9, 99.5, 59.5, 88};
System.out.println(ages);
System.out.println(scores);
// 2. 简化写法
// 数据类型[] 数组名 = {元素1, 元素2, 元素3, ...};
int[] ages2 = {12, 24, 36};
double[] scores2 = {89.9, 99.5, 59.5, 88};
// 3. 数据类型[] 数组名 也可以写成 数据类型 数组名[]
int ages3[] = {12, 24, 36};
double scores3[] = {89.9, 99.5, 59.5, 88};
}
}
- 总结
- 数组的静态初始化的写法和特点是什么样的?
- 定义数组我们说了哪几个注意点?
- 什么类型的数组必须存放什么类型的数据
- 数据类型[] 数组名 也可以写成 数据类型 数组名[]
- 数组是属于什么类型,数组变量名中存储的是什么?
- 引用数据类型,存储的数组在内存中的地址信息
2.1.1 数组的访问
2.1.1 定义
数组名[索引]
- 数组的长度属性:length
// 获取数组的长度(就是数组元素的个数)
System.out.println(arr.length);
- 数组的最大索引怎么表示?
数组名.length - 1 // 前提:元素个数大于0
代码实现
package e_ArrayApp;
public class b_ArrayDemo {
public static void main(String[] args) {
// 目标:掌握数组的访问
int[] arr = {12, 24, 36};
// 1. 访问数组的全部数据
System.out.println(arr[0]); // 12
System.out.println(arr[1]); // 24
System.out.println(arr[2]); // 36
// 2. 修改数组中的数据
arr[0] = 66;
arr[2] = 100;
System.out.println(arr[0]); // 66
System.out.println(arr[1]); // 24
System.out.println(arr[2]); // 100
// 3. 访问数组的元素个数:数组名.length
System.out.println(arr.length); // 3
// 技巧:获取数组的最大索引:arr.length - 1(前提是数组中存在数据)
System.out.println(arr.length - 1); // 2
}
}
总结
- 如何访问数组的元素?
数组名[索引]
- 如何访问数组的长度?
- 数组名称.length
- 数组的最大索引是多少?
数组名.length - 1 // 前提:元素个数大于0
- 如果访问数组时,使用的索引超过了数组的最大索引会出什么问题?
- 执行程序时会出 bug,出现一个索引越界的异常提示
2.1.2 数组的遍历
2.1.2.1 定义
- 遍历:就是一个一个数据的访问
- 数组遍历:就是把数组的每个数据都取一遍出来
快速方法:
- 数组名.fori + Tab
代码演示
package e_arrayapp;
public class c_ArrayDemo3 {
public static void main(String[] args) {
// 目标:掌握数组的遍历
int[] ages = {12, 24, 36};
// System.out.println(ages[0]); // 12
// System.out.println(ages[1]); // 24
// System.out.println(ages[2]); // 36
for (int i = 0; i < ages.length; i++) {
// i = 0 1 2
System.out.println(ages[i]); // 12 24 36
}
}
}
总结
- 什么是遍历?
- 一个一个的访问一遍容器中的数据
- 如何遍历数组?
int[] ages = {20, 30, 40, 50};
for (int i = 0; i < ages.length; i++) {
// i = 0 1 2
System.out.println(ages[i]); // 12 24 36
}
案例
需求:
- 某部门5名员工的销售额分别是:16、26、36、6、100,请计算出他们部门的总销售额
- 分析:
- 把这5个数据拿到程序中去 —> 使用数组
int[] money = {16, 26, 36, 6, 100};
- 遍历数组中的每一个数据,然后在外面定义求和变量把他们累加起来
代码实现
package e_arrayapp;
public class d_ArrayTest4 {
public static void main(String[] args) {
// 目标:完成对数据的元素求和
// 1. 定义一个数组存储5名员工的销售额
int[] money = { 16, 26, 36, 6, 100 };
// 3. 定义一个变量用于累加求和
int count = 0;
// 2. 遍历这个数组中的每个数据
for (int i = 0; i < money.length; i++) {
// i = 0 1 2 3 4
count += money[i];
}
System.out.println("员工的销售额:" + count); // 184
}
}
2.2 动态初始化数组
2.2.1 定义
数组的动态初始化:
- 定义数组时先不存入具体的元素值,只确定数组存储的数据类型和数组的长度
数组的动态初始化格式:
数据类型[] 数组名 = new 数据类型[长度];
int[] arr = new int[3];
// 后赋值
arr[0] = 10;
System.out.println(arr[0]); // 10
- 在内存中分配一个变量空间,在这个变量里面存储的还是数组对象的地址,这个数组对象里面,一开始会存一些所谓的默认值,后期再往这个里面进行赋值
注意:
- 静态初始化和动态初始化数组的写法是独立的,不可以混用
2.2.2 动态初始化数组元素默认值规则
数据类型 | 明细 | 默认值 |
---|---|---|
基本类型 | byte、short、char、int、long | 0 |
float、double | 0.0 | |
boolean | false | |
引用类型 | 类、接口、数组、String | null |
代码实现
package e_arrayapp;
public class e_ArrayDemo5 {
public static void main(String[] args) {
// 目标:掌握定义数组的方式二:动态初始化数组
// 1. 数据类型[] 数组名 = new 数据类型[长度];
int[] ages = new int[3]; // ages = [0, 0, 0]
System.out.println(ages[0]); // 0
System.out.println(ages[1]); // 0
System.out.println(ages[2]); // 0
ages[0] = 12;
ages[1] = 18;
ages[2] = 32;
System.out.println(ages[0]); // 12
System.out.println(ages[1]); // 18
System.out.println(ages[2]); // 32
System.out.println("------------------------");
char[] chars = new char[3]; // [0, 0, 0]
System.out.println((int) chars[0]); // 0
System.out.println((int) chars[2]); // 0
double[] scores = new double[80];
System.out.println(scores[0]); // 0.0
System.out.println(scores[79]); // 0.0
boolean[] flags = new boolean[100];
System.out.println(flags[0]); // false
System.out.println(flags[99]); // false
String[] names = new String[80];
System.out.println(names[0]); // null
System.out.println(names[79]); // null
}
}
总结
- 动态数组的写法是什么样的?有什么特点?
数据类型[] 数组名 = new 数据类型[长度];
int[] ages = new int[4];
- 动态初始化数组后元素的默认值是什么样的?
- byte、short、int、char、long 类型数组的元素默认值都是0
- float、double 类型数组元素的默认值都是0.0
- boolean 类型数组的元素默认值是 false,String 类型数组的元素的默认值是 null
- 两种数组定义的方法各自适合什么业务场景?
- 动态初始化:适合开始不确定具体元素值,只知道元素个数的业务场景
- 静态初始化:适合一开始就知道要存入哪些元素值的业务场景
案例
需求:
- 某歌唱比赛,需要开发一个系统:可以录入6名评委的打分,录入完毕后立即输出平均分做为选手得分
- 分析:
- 6名评委的打分是后期录入的,一开始不知道具体的分数,因此定义一个动态初始化的数组存分数
double[] scores = new double[6];
- 遍历数组中的每个位置,每次提示用户录入一个评委的分数,并存入数组对应的位置
- 遍历数组中的每一个元素进行求和最终算出平均分打印出来即可
代码实现
package e_arrayapp;
import java.util.Scanner;
public class f_ArrayTest6 {
public static void main(String[] args) {
// 目标:完成评委打分的案例
// 1. 定义一个动态初始化的数组,负责存储6个评委的打分
double[] scores = new double[6];
Scanner sc = new Scanner(System.in);
// 2. 遍历数组中的每个位置,录入评委的分数,存入数组中去
for (int i = 0; i < scores.length; i++) {
// i = 0 1 2 3 4 5 6
System.out.println("请您输入当前第" + (i + 1) + "评委的分数");
double score = sc.nextDouble();
scores[i] = score;
}
// 3. 遍历数组中的每个元素进行求和
double sum = 0;
for (int i = 0; i < scores.length; i++) {
sum += scores[i];
}
System.out.println("选手最终得分是:" + sum / scores.length);
}
}
3. 数组在计算机中的执行原理
3.1 数组的执行原理,Java 程序的执行原理
前面我们知道,程序都是在计算机中的内存中执行的,那么 Java 程序编译后会产生一个 class 文件,然后这个 class 文件是提取到内存中正在运行的虚拟机里面去执行的。那么 Java 为了便于虚拟机执行这个 Java 程序,它将虚拟机中的这块内存区域进行了划分
3.1.1 Java 内存分配介绍
- 方法区
- 栈
- 堆
- 本地方法栈
- 程序计数器
3.1.2 方法区
- 定义:放我们编译以后的 class 文件的,也就是字节码文件
3.1.3 栈
- 定义:方法运行时所进入的内存,由于变量是在方法里面的,所以变量也在这块区域里
3.1.4 堆
- 定义:堆里面放的都是 new 出来的东西,它会在这块堆内存中开辟空间并产生地址,比如之前用的数组就是放在队里面的
ps:堆和栈在数据结构中应用广泛,具体可见 数据结构与算法
3.1.5 数组在计算机中的执行原理
把程序的 class 文件提取到方法区里面来,这里面会有一个 main 方法。接着它会把这个 main 方法加载到我们的栈里面来执行,接着它就会正式执行 main 方法的第一行代码,代码中基本类型变量就会在栈里面开辟空间。如果执行创建数组,就会先在栈里面开辟一个变量空间,一开始变量里面并没有存数据,紧接着执行等号右边的代码(new 一个数组对象),在堆内存中开辟一块空间,这块空间会分成 n 块等分的区域每个元素也有自己的索引,并且也会有一个地址,然后把这个地址赋值给左边的这个变量,再由变量指向这个数组对象
总结
- 运行一个 Java 程序,主要看 JVM 中包含的哪几部分内存区域?
- 方法区
- 栈内存
- 堆内存
- 简单说说 int a = 20; int[] arr = new int[3] 这两行代码的执行原理?
- a 是变量,直接放在栈中,a 变量存储的数据就是20这个值
- new int[3] 是创建一个数组对象,会在堆内存中开辟区域存储3个整数
- arr 是变量,在栈中,arr 中存储的是数组对象在堆内存中的地址值
3.2 多个变量指向同一个数组的问题
3.2.1 使用数组时常见的一个问题
- 如果某个数组变量存储的地址是 null,那么该变量将不再指向任何数组对象
arr2 = null; // 把null赋值给arr2
System.out.println(arr2); // null
System.out.println(arr2[0]); // 会出异常
System.out.println(arr2.length); // 会出异常
代码实现
package f_memory;
public class b_ArrayDemo2 {
public static void main(String[] args) {
// 目标:认识多个变量指向同一个数组对象的形式,并掌握其注意事项
int[] arr1 = {11, 22, 33};
// 把int类型的数组变量arr1赋值给int类型的数组变量arr2
int[] arr2 = arr1;
System.out.println(arr1); // [I@5caf905d
System.out.println(arr2); // [I@5caf905d
arr2[1] = 99;
System.out.println(arr1[1]); // 99
arr2 = null; // 拿到的数组变量中存储的值是null
System.out.println(arr2);
// System.out.println(arr2[0]);
System.out.println(arr2.length);
}
}
总结
- 多个数组变量,指向同一个数组对象的原因是什么?需要注意什么?
- 多个数组变量中存储的是同一个数组对象的地址
- 多个变量修改的都是同一个数组对象中的数据
- 如果某个数组变量中存储的 null,代表什么意思?需要注意什么?
- 代表这个数组变量没有指向数组对象
- 可以输出这个变量,但不能用这个数组变量去访问数据或者访问数组长度,会报空指针异常:NullPointerException
专项训练:数组常见案例
数组求最值
分析
实现步骤:
- 把数据拿到程序中去,用数组装起来
int[] socres = {15, 9000, 10000, 20000, 9500, -5};
- 定义一个变量用于记录最终的最大值
int[] max = socres[0]; // 建议存储数组的第一个元素值作为参照
- 从第二个位置开始:遍历数组的数据,如果遍历的当前数据大于 max 变量存储的数据,则替换变量存储的数据为当前数据
- 循环结束后输出 max 变量即可
代码实现
package g_demo;
public class a_Test1 {
public static void main(String[] args) {
// 目标:掌握数组元素求最值
// 1. 把数据拿到程序中来,用数组装起来
int[] scores = {15, 9000, 10000, 20000, 9500, -5};
// 2. 定义一个变量用于最终记住最大值
int max = scores[0];
// 3. 从数组的第二个位置开始遍历
for (int i = 0; i < scores.length; i++) {
// i = 1 2 3 4 5
// 判断一个当前遍历的这个数据,是否大于最大值变量max存储的数据,如果大于当前遍历的数据需要赋值给max
if (scores[i] > max) {
max = scores[i];
}
}
System.out.println("最大数据是:" + max); // 20000
}
}
总结
- 求数组中的元素最大值,我们是如何实现的?
- 把数据拿到程序中去,用数组装起来
- 定义一个变量 max 用于记录最大值,max 变量默认存储了第一个元素值作为参照物
- 从第二个位置开始遍历数组的数据,如果当前元素大于变量存储的数据,则替换变量存储的值为该元素
- 循环结束后输出 max 变量即可
数组反转
需求 - 分析
需求:
- 某个数组有5个数据:10,20,30,40,50,请将这个数组中的数据进行反转
[10, 20, 30, 40, 50] 反转后 [50, 40, 30, 20, 10]
分析:
- 数组的反转操作实际上就是:依次前后交换数据即可实现
代码实现
package g_demo;
public class b_Test2 {
public static void main(String[] args) {
// 目标:完成数组反转
// 1. 准备一个数组
int[] arr = {10, 20, 30, 40, 50};
// 2. 定义一个循环,设计2个变量,一个在前,一个在后
for (int i = 0, j = arr.length - 1; i < j; i++, j--) {
// arr[i] arr[j]
// 交换
// 1. 定义一个临时变量记住后一个位置处的值
int temp = arr[j];
// 2. 把前一个位置处的值赋值给后一个位置
arr[j] = arr[i];
// 3. 把临时变量中记住的后一个位置处的值赋值给前一个位置
arr[i] = temp;
}
// 3. 遍历数组中的每个数据,看是否反转成功了
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " "); // 50 40 30 20 10
}
}
}
总结
- 我们如何完成数组的反转的?
- 使用 for 循环,控制让数组的前后位置的元素,依次交换
- 数组如何实现前后元素交换的?
- 定义一个临时变量记住后一个位置处的元素值
- 再把前一个位置处的元素,赋值给后一个位置处
- 最后把临时变量记住的后一个位置的值赋值给前一个位置处
随机排名
需求:
- 某公司的开发部门有5名开发人员,要进行项目进展汇报演讲,现在采取随机排名后进行汇报。请依次录入5名员工的工号,然后展示出一组随机的排名顺序
测试用例:[22, 33, 35, 13, 88] —> [13, 35, 88, 33, 22]
分析:
- 在程序中录入5名员工的工号存储起来 —> 使用动态初始化数组的方式
- 依次遍历数组中的每个数据
- 每遍历到一个数据,都随机一个索引值出来,让当前数据与该索引位置处的数据进行交换
代码实现
package g_demo;
import java.util.Random;
import java.util.Scanner;
public class c_Test3 {
public static void main(String[] args) {
// 目标:完成随机排名
// 1. 定义一个动态初始化的数组用于存储5名员工的工号
int[] codes = new int[5];
// [0, 0, 0, 0, 0]
// 0 1 2 3 4
// 2. 提示用户录入5名员工的工号
Scanner sc = new Scanner(System.in);
for (int i = 0; i < codes.length; i++) {
// i = 0 1 2 3 4
System.out.println("请您输入当前第" + (i + 1) + "员工的工号");
int code = sc.nextInt();
codes[i] = code;
}
// 3. 打乱数组中的元素排序
Random r = new Random();
for (int i = 0; i < codes.length; i++) {
// codes[i]
// 每遍历到一个数据,都随机一个数组索引范围内的值,然后让当前遍历的数据与索引位置处的值交换
int index = r.nextInt(codes.length); // 0 - 4
// 定义一个临时变量记住index位置处的值
int temp = codes[index];
// 把i位置处的值赋值给index位置处
codes[index] = codes[i];
// 把index位置原来的值赋值给i位置处
codes[i] = temp;
}
// 4. 遍历数组中的工号输出即可
for (int i = 0; i < codes.length; i++) {
System.out.print(codes[i] + " ");
}
}
}
总结
- 我们是如何实现随即排名的?
- 定义一个动态初始化的数组用于录入员工的工号
- 遍历数组中的每个元素
- 每遍历到一个数据,都随机一个索引值出来,让当前数据与索引位置出的数据进行交换
补充知识:Debug 工具的使用
- IDEA(大部分 IDE 都有)自带的断点调试工具,可以控制代码从断点一行一行的执行,然后详细观看程序执行的情况
DEBUG 工具基本使用步骤
- 在需要控制的代码左侧,点击一下,形成断点
- 选择使用 DEBUG 方式启动程序,启动后程序会在断点暂停
- 控制代码一行一行的往下执行
断点:
用 Debug:
点击按钮,实现不同效果: