文章目录
- 一、数组的概念
- 1. 什么是数组
- 2. 数组的创建及初始化
- 3. 数组的使用
- 3.1 数组中元素的访问
- 3.2 遍历数组
- 二、数组是引用类型
- 1. 初始JVM的内存分布
- 2. 基本类型变量与引用变量的区别
- 3. 引用变量
- 4. 认识null
- 三、数组的应用场景
- 1. 保存数据
- 2. 作为函数的参数
- 2.1 参数传基本类型
- 2.2 参数传数组类型(引用数据类型)
- 3. 作为函数的返回值
- 四、二维数组
一、数组的概念
假设存5个学生的考试成绩,并进行输出,我们用之前掌握的知识点来写:
public class TestStudent{
public static void main(String[] args){
int score1 = 70;
int score2 = 80;
int score3 = 85;
int score4 = 60;
int score5 = 90;
System.out.println(score1);
System.out.println(score2);
System.out.println(score3);
System.out.println(score4);
System.out.println(score5);
}
}
假如存20个、100个同学的成绩呢?不仅非常麻烦,代码也非常冗余,我们仔细观察可以发现:所有成绩的类型都是相同的, 那就引出我们的数组。
1. 什么是数组
数组是用来存储一组相同类型的数据的,在内存中是一段连续的空间。
数组的存储:
1. 1. 数组中存放的元素其类型相同 ;
2. 数组的空间是连在一起的;
3. 每个空间有自己的编号,起始位置的编号为0,即数组的下标/索引。
2. 数组的创建及初始化
【数组的创建】
//方式一:定义数组,同时进行初始化(用的最多!!!)
int[] array = {1,2,3,4,5,6};
//方式二:定义数组,new开辟内存同时进行初始化。
int[] array = new int[]{1,2,3,4,5,6,};
//方式三:只是分配了内存,没有进行初始化。大小是10,内容是0
int[] array = new int[10];
int[] array = new int[]{1,2,3,4,5,6,};
注意:
- 一般通过 new关键字 实例化对象;
- Java当中的数组,其实就是一个对象,所以Java中:一些皆对象;
- 在new的时候会生成一块内存,放12345,里面的编号是多少。
【数组的初始化】
- 动态初始化: 在创建数组时,直接指定数组中元素的个数。
int[] array = new int[10];
- 静态初始化: 在创建数组时不直接指定数据元素个数,而直接将具体的数据内容进行指定。
T[] 数组名称 = {data1, data2, data3, ..., datan};
int[] array1 = new int[]{0,1,2,3,4,5,6,7,8,9};
double[] array2 = new double[]{1.0, 2.0, 3.0, 4.0, 5.0};
String[] array3 = new String[]{"hell", "Java", "!!!"};
//也可以这样写
int[] array;
array = new int[]{1,2,3,4};
//不可以这样写,会报错
int[] array;
array = {1,2,3,4};
如果没有对数组进行初始化,数组中元素有其默认值
-
如果数组中储存元素为基类类型,默认值为基类类型对应的默认值,如:
-
如果数组元素存储元素类型为引用类型,默认值为null
3. 数组的使用
3.1 数组中元素的访问
int[] array = {1,2,3,4,5};
array[0] = 99; //修改第一个值
System.out.println(array[0]); //99
System.out.println(array[6]); //越界或负数,都会报数组越界异常ArrayIndexOutOfBoundsException
System.out.println(array.length); //5 数组的长度
【注意事项】
- 数组是一段连续的内存空间,因此支持随机访问,即通过下标访问快速访问数组中任意位置的元素;
- 下标从0开始,介于[0, N)之间不包含N,N为元素个数,不能越界,否则会报出下标越界异常。
3.2 遍历数组
- 可以一个一个的输出进行遍历
- 也可以用for循环
【方法1 】
【代码】
int[] array = {1,2,3,4,5,6};
//方法1:通过下标来访问
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
【输出结果】
【方法2】
【代码】
//方法2:for-each:和下标没关系,数组中每个元素的类型的变量
for (int x : array) {
System.out.println(x);
}
【输出结果】
【方法3】
Java中有个类Arrays,是专门操作数组的。
【代码】
//方法3:用定义好的类
String ret = Arrays.toString(array); //有String返回值,所以要接收一下,就在=左边定义String
System.out.println(ret);
【输出结果】
其中:
在所有遍历方法的前面加上Arrays.sort(array);
即可对数组里面的值进行排序.
二、数组是引用类型
1. 初始JVM的内存分布
程序最终都是保存在JVM里的,内存是一段连续的存储空间,用来存储程序运行时数据的.比如:
- 程序运行时代码需要加载到内存;
- 程序运行产生的中间数据要存放在内存;
- 程序中的常量也要保存;
- 有些数据可能需要长时间存储,而有些数据当方法运行结束后就要被销毁.
其中,JVM 也对使用的内存按照功能的不同进行了划分:
其中:
- Java虚拟机栈(JVM Stack) :就是我们平常所说的栈.与方法调用相关的一些信息,每个方法在执行时,都会先创建一个栈帧,栈帧中包含:局部变量表、操作数栈、动态链接、返回地址以及其他的一些信息,保存的都是与方法执行时相关的写一些信息.比如:局部变量.当方法运行结束后,栈帧就被销毁了,即栈帧中保存的数据也被销毁了.
- 堆: JVM所管理的最大内存区域,使用 new 创建的对象都是在对上保存,堆是对着程序开始而创建,随着程序的退出而销毁,堆中的数据只要还有在使用,就不会被销毁.
- 方法区: 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据.方法编译出的字节码就是保存在这个区域.
- 本地方法栈: 本地方法栈与虚拟机栈的作用类似,之不报保存的内容是Native方法的局部变量.有些版本的JVM实现中(例如HotSpot),本地方法栈和虚拟机栈是一起的.
- 程序计数器:只是一个很小的空间,保存下一条执行的指令地址.
关于数组为什么是引用类型变量?
为什么说数组array是引用类型的变量,因为他存了数组的地址
int[] array = {1,2,3};
- 首先,有两块内存,一个是栈(Java虚拟机栈),一个是堆;
- 只要是new什么,就是在堆里开辟内存,并且在堆里面开辟了一块内存,存储数组中new出来的内容(1,2,3);
- array是局部变量,局部变量的内存在栈上,其中array存的是变量的地址0x111,我们把存了地址的变量叫做引用变量 .
【代码】
int[] array = {1,2,3};
System.out.println(array);
【运行结果】
其中:
① " I " 代表是一个整型数组;
② " @ " 符号作为分割;
③ 后面是一个16进制的数字,这个数字暂且代表地址.
2. 基本类型变量与引用变量的区别
基本数据类型变量:又称为基本变量,该变量空间中直接存放的是其所对应的值;
**引用数据类型变量:**一般称为对象的引用,其空间中存储的是对象所在空间的地址。
引用变量并不直接存储对象本身,可以简单理解成存储的是对象在堆中空间的起始地址。通过该地址,引用变量便可以去操作对象。 有点类似C语言中的指针,但是Java中引用要比指针的操作更简单。
3. 引用变量
int[] array2 = array1;
代表array2这个引用指向了array1这个引用所指向的对象!- 两个引用同时指向了一个对象
- 通过其中任何一个引用,修改这个对象的值,另一个引用去访问的时候,也是被改变的
例1:
int[] array1 = {1,2,3,4};
array1[0] = 99;
int[] array2 = array1;
array2[0] = 100;
System.out.println(Arrays.toString(array1)); //100 2 3 4
System.out.println(Arrays.toString(array2)); //100 2 3 4
如图:
由此可以看出:
int[] array2 = array1;
代表 array2这个引用 指向了array1这个引用 所指向的对象, array1 和 array2 二者指向一个对象.
例2:
public static void main(String[] args) {
int[] array1 = {1,2,3,4};
int[] array2 = {11,22,33,44};
array1 = array2;
array1[0] = 1888;
System.out.println(Arrays.toString(array1)); // 1888 22 33 44
System.out.println(Arrays.toString(array2)); // 1888 22 33 44
}
由图可知:
①array1 = array2;
代表array1这个引用,指向了array2这个引用所指向的对象。array1之前的对象就没有任何变量用了,被系统回收;
②此时在0x1230中,两个引用同时指向一个对象;
③通过其中任何一个引用,修改0x123对象的值,另一个引用去访问的时候,也是会被改变的。
4. 认识null
null 在Java中表示“空引用”,也就是一个不指向对象的引用
int[] arr = null; //此时arr这个引用,不指向任何对象
System.out.println(arr[0]);
// 执行结果
Exception in thread "main" java.lang.NullPointerException
at Test.main(Test.java:6)
null 的作用类似于 C 语言中的 NULL (空指针), 都是表示一个无效的内存位置. 因此不能对这个内存进行任何读写操作。 一旦尝试读写, 就会抛出 NullPointerException.
注意:Java中并没有约定 null 和 0 号地址有关联。
三、数组的应用场景
1. 保存数据
//存储 1 2 3,并把1 2 3打印出来
int[] array = {1,2,3};
for(int i = 0;i <arr.length; i++){
System.out.println(array[i] + " ");
}
2. 作为函数的参数
2.1 参数传基本类型
形参的改变,不会影响到实参的值。
public static void main(String[] args) {
int num = 0;
func(num);
System.out.println("num = " + num);
}
public static void func(int x) {
x = 10;
System.out.println("x = " + x);
}
// 执行结果
x = 10
num = 0
执行过程如图:
我们发现在func方法中修改形参 x 的值, 不影响实参的 num 值。
2.2 参数传数组类型(引用数据类型)
public static void func1(int[] array) {
array = new int[]{22,33,44};
}
public static void func2(int[] array) {
array[0] = 999;
}
public static void main(String[] args) {
int[] array1 = {1,2,3,4};
func1(array1);
System.out.println(Arrays.toString(array1)); //[1, 2, 3, 4]
int[] array2 = {1, 2, 3, 4};
func2(array2);
System.out.println(Arrays.toString(array2)); //[999, 2, 3, 4]
}
}
//执行结果
[1, 2, 3, 4]
[999, 2, 3, 4]
执行过程:
当数组作为参数进行传递的时候,其实还是按值传递,只不过此时的值是一个地址!!那么就会出现两种情况:
关于func1():形参修改指向
所以此代码,只是改变了形参的指向,并没有改变形参的指向,最后结果打印的是实参。
关于func2():形参修改指向对象的值
此时,传递的是引用,我们通过引用修改了原来的值。
3. 作为函数的返回值
如:将数组的每个元素都扩大2倍。
方法:调用grow()方法,会给grow方法开辟一块内存
//将数组的每个元素都扩大2倍!
public static int[] grow(int[] array) {
//遍历数组中的每个元素
for (int i = 0; i < array.length; i++) {
array[i] = array[i]*2;
}
return array;
}
public static void main(String[] args) {
int[] array = {1,2,3,5,9};
int[] ret = grow(array);
System.out.println(Arrays.toString(array)); //[2, 4, 6, 10, 18]
System.out.println(Arrays.toString(ret)); //[2, 4, 6, 10, 18]
}
相当于在同一个对象上进行了修改。
四、二维数组
二维数组本质上也就是一堆数组,只不过每个元素 又是一个一维数组。
可以理解是两行。
【格式】
int[][] array = {{1,2,3} , {4,5,6}};
两行三列
int[][] array = new int[][]{{1,2,3} , {4,5,6}};
int[][] array = new int[2][3];
不规则的二维数组:int[][] array = new int[2][];
指定了是两行,没有指定列。
【代码】
//for循环的三组打印方式
public static void main(String[] args) {
int[][] array = {{1,2,3},{4,5,6}};
for (int i = 0;i < 2,i++) {
for (int j = 0;j <3;j++) {
System.out.println(array[i][j]);
}
System.out.println();
}
}
二维数组的用法和一维数组并没有明显差别,因此我们不再赘述。
同理, 还存在 “三维数组”,“四维数组” 等更复杂的数组, 只不过出现频率都很低。