数组的定义和使用
文章目录
1.数组的基本概念
1.1 为什么要用数组
假设我们在要统计5个学生的成绩,并对其打印输出,可能会这样写代码
public class Student { public static void main(String[] args) { int stu1=90; int stu2=93; int stu3=85; int stu4=87; int stu5=80; System.out.println(stu1); System.out.println(stu2); System.out.println(stu3); System.out.println(stu4); System.out.println(stu5); } }
上述代码是没有一点问题的,但是有不好的就是:当我们需要统计大量同学的成绩时,比如20个、50个、100个……时,总不能连续创建这么多的变量,当所有学生的成绩需要加上5分时总不能一个一个的修改成绩吧,这样的效率简直太低了。通过观察我们发现:所有成绩类型都是int型的,因此我们将这些数据存储到一个容器中,方便统一管理。
容器概念
- 容器:是将多个数据存储到一起,每个数据称为该容器的元素。
- 生活中的容器:水杯,衣柜,常见的瓶瓶罐罐……
1.2什么是数组
数组:相同元素的一个集合。在内存中是一块连续的空间,比如我们常见的停车场:
数组好比停车场,车位数好比数组的长度,而汽车就是数组的元素。
因此:
- 数组存放的元素类型必须相同
- 数组的空间是连在一起的
- 每个数组空间有自己的编号,初始位置编号是0,即数组的下标。
1.3数组的创建及初始化
1.3.1数组的创建
T[]数组名=new T[N]
T:表示数组中元素的类型
T[]:表示数组的类型
N:表示数组的长度
1.3.2数组的初始化
在Java中数组初始化分两种:
1.动态初始化:在创建数组时,直接指定数组中元素的个数
int[] array=new int[5];
2.静态初始化:创建数组时不指定元素的个数,而直接将具体的数组内容进行指定
int[] array1=new int[]{1,2,3,4,5,6,7}; double[] array2=new double[]{1.1,2.2,3.3,4.4,5.5}; String[] array3={"hello","java"};//这里省略new String[]编译能通过吗?
静态初始化数组时可以省略new T[],但在编译器编译时还是会还原。
静态初始化时,{}中的数据类型必须与[]前的数据类型一致。
int arr[]={1,2,3};//这样初始化可以吗?
Java中数组初始化可以按照C语言中数组初始化的方式,但是这种方法不推荐,容易造成数组的类型就是int的误解。
- 如果没有对数组进行初始化,元素中有其默认值
类型 | 默认值 |
byte | 0 |
short | 0 |
int | 0 |
long | 0 |
float | 0.0f |
double | 0.0 |
char | /u0000 |
boolean | float |
应用类型 | null |
1.4数组的使用
1.4.1数组元素的访问
数组在内存中是一段连续存在的空间,空间编号都是从0开始,依次递增,编号是数组的下标,数组可以通过下标进行访问数组的元素。数组下标从0开始,介于[0,N)之间不包括N,N为元素个数,不能越界,否则会报错。
int[]array={1,2,3,4,5}; System.out.println(array[0]); System.out.println(array[1]); System.out.println(array[2]); System.out.println(array[3]); System.out.println(array[4]);
1.4.2遍历数组
所谓**“遍历”就是将数组中所有元素访问一遍,访问是指对数组的元素进行某种操作。**比如:打印
int[]array={1,2,3,4,5}; for(int i=0;i<array.length;i++){ System.out.println(array[i]); }
在数组中可以通过 数组名.length 获取数组的长度
也可以通过for-each遍历数组
int[]arr={1,2,3,4,5}; for(int e:arr){ System.out.println(e); }
for-each 是 for 循环的另外一种使用方式. 能够更方便的完成对数组的遍历. 可以避免循环条件和更新语句写错。
2.数组是引用类型
2.1 Java虚拟机(JVM)的内存分布
- 内存是一段连续的存储空间,主要用来存储程序运行时数据的。比如:
- 程序运行时代码需要加载到内存
- 程序运行产生的中间数据要存放在内存
- 程序中的常量也要保存
- 有些数据可能需要长时间存储,而有些数据当方法运行结束后就要被销毁
如果对内存中存储的数据不加区分的随意存储,那对内存管理起来将会非常麻烦。
因此JVM也对所使用的内存进行了划分:
- 程序计数器:是一块较小的内存空间,保存下一条执行的指令的地址,它可以看作是当前线程所执行的字节码的行号指示器。
- 虚拟机栈:Java方法执行的内存模型,每个方法执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用直至完成的过程,都对应着一个栈帧从入栈到出栈的过程。每当一个方法执行完成时,该栈帧就会弹出栈帧的元素作为这个方法的返回值,并且清除这个栈帧,Java栈的栈顶的栈帧就是当前正在执行的活动栈,也就是当前正在执行的方法。
- 本地虚拟机栈:本地方法栈和虚拟机栈所发挥的作用是很相似的,只不过保存的内容是Native方法的局部变量. 在有些版本的 JVM 实现中(例如HotSpot), 本地方法栈和虚拟机栈是一起的。
- 堆:JVM所管理的最大内存区域. 几乎所有用new 创建的对象都是在堆上保存 (例如前面的 new int[]{1, 2,3} ),堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还有在使用,就不会被销毁。
- 方法区:用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据. 方法编译出的的字节码就是保存在这个区域 。
2.2数组在内存中的存储
1.一个数组的内存图
public static void main(String[] args) { int[]arr=new int[]{1,2,3}; System.out.println(arr);//[I@1540e19d }
代码执行结果是**[I@1540e19d**,这是数组内容在内存中的地址。new的内容几乎都存储在堆中,方法中的变量arr保存的是数组的地址。
2.两个数组的内存图
3.数组作为函数参数
3.1参数传基本数据类型
数组内容交换:
public static void swapArray(int[]left,int[]right){ int[]tmp=left; left=right; right=tmp; } public static void method4(){ int[]array1={1,2,3,4,5}; int[]array2={10,20,30,40,50}; swapArray(array1,array2); System.out.println(Arrays.toString(array1)); System.out.println(Arrays.toString(array2)); } public static void main(String[] args) { method4(); }
执行结果:
数组内容未发生交换。在方法中,如果修改了形参的指向,实参的指向不发生变化。
3.2参数传数组类型(引用数据类型)
public static void swap(int[]num,int i,int j){ int tmp=num[i]; num[i]=num[j]; num[j]=tmp; } public static void method3(){ int[]array={10,20,30,40}; swap(array,0,3); System.out.println(Arrays.toString(array)); } public static void main(String[] args) { method3(); }
执行结果:
内存分布:
方法的参数为基本类型时,传递的是数据值. 方法的参数为引用类型时,传递的是地址值.
}
public static void main(String[] args) {
method3();
}
执行结果: [外链图片转存中...(img-u6uthkjy-1660910375540)] 内存分布: [外链图片转存中...(img-wdluWpRU-1660910375540)] > **方法的参数为基本类型时,传递的是数据值. 方法的参数为引用类型时,传递的是地址值.**