目录
一、数组的定义
1.1 为什么要使用数组
1.2 什么是数组
1.3 数组的初始化
1.3.1 动态初始化
1.3.2 静态初始化
1.2.3 注意事项
三、遍历数组
3.1 用循环的方式遍历数组
3.2 用 for each 的方式遍历数组
3.3 用 Arrays.toString 的方式遍历数组
3.4 一些其他的补充
四、如何理解数组名
五、数组的存储
5.1 JVM内存分布
5.2 内置类型变量与引用类型变量的区别
5.3 再谈引用变量
5.4 认识 null
编辑
六、数组作为函数的参数
七、数组作为函数的返回值
八、数组练习
8.1 数组转字符串
8.2 数组拷贝
8.3 求数组中元素的平均值
8.4 查找数组中指定元素的下标(顺序查找)
8.5 查找数组中指定元素的下标(二分查找)
8.6 数组排序(冒泡排序)
8.7 数组逆序
8.8 判断数组内容是否一致
8.9 给数组中连续几个元素填充为同一个值
8.10 总结Arrays当前已经学过的方法(9个)
九、二维数组
一、数组的定义
1.1 为什么要使用数组
假设我们要存储20个学生的数学成绩,根据之前所学过数据类型和变量的知识,我们就需要创建20个整型变量,如果学生的人数是100、200的话那我们就的创建100、200个整型变量,这显然是非常的不方便,由此引入了数组这个概念。
1.2 什么是数组
1. 概念:数组是相同类型元素的一个集合。
2. 特点:
① 数组中的各元素类型保持一致。
② 数组中每个元素的内存是连续的。
③ 和C语言一样,可以根据下标访问数组中的每个元素,下标也是从0开始的。
1.3 数组的初始化
1.3.1 动态初始化
1. 概念:在创建数组时,直接指定数组中元素的个数。
2. 示例:int[] array = new int[10];
3. 动态初始化分步写,示例:
int[] array1;
array1 = new int[10];
4. 动态初始化,会自动为每个元素赋初始值,比如:数组中存储元素类型为byte、short、int、long类型,每个元素会被初始化为0;数组中存储元素类型为float类型,每个元素会被初始化为0.0f;数组中存储元素类型为double类型,每个元素会被初始化为0.0;数组中存储元素类型为char类型,每个元素会被初始化为/u0000;数组中存储元素类型为boolean类型,每个元素会被初始化为false;另外,如果数组中存储元素类型为引用类型,默认值为null。
1.3.2 静态初始化
1. 概念:在创建数组时不直接指定数组中元素的个数,编译器会根据{}中的元素个数确定。
2. 示例1:int[] array1 = {1,2,3,4,5}; -->通常使用这种省略版本,编译时会还原成未省略的。
3. 示例2:int[] array2 = new int[]{1,2,3,4,5};
4. 静态初始化分步写,示例:
int[] array;
array = new int[]{1,2,3,4,5};
//注意:省略版本不能采用分步初始化,否则编译失败
//int[] array;
//array = {1,2,3,4,5};//err
1.2.3 注意事项
① 数组类型int[]中的[]中不要写数字,new int[]中的[]在静态初始化时也不要写数字,否则会报错。
② Java中的数组的创建也可以写成 int array[]; 的形式,但并不推荐。
③ Java中的变量未被初始化是不能够被直接使用的,数组变量也一样,例如:
int[] array;
System.out.println(array);//err
//注意:动态初始化会每个数组元素赋值,所以动态初始化后使用数组没有问题,例如:
int[] array = new int[5];
System.out.println(array);//right
三、遍历数组
3.1 用循环的方式遍历数组
3.2 用 for each 的方式遍历数组
说明:for-each 是 for 循环的另外一种使用方式,for后面()中的格式是,冒号左边创建一个可临时接收数组元素的变量,注意要于数组元素的类型保持一致;冒号右边写数组名。
3.3 用 Arrays.toString 的方式遍历数组
3.4 一些其他的补充
补充一,关于数组越界访问:
数组越界访问编译器会报数组越界异常,如下图
补充二,关于程序中有多个错误,编译器是如何报错的:
下面的这段代码中,编译器只会报第一个错误,是因为后面的错误程序还未执行到那里
四、如何理解数组名
1. 编译如下代码,会在屏幕上打印出,[I@4eec7777
int[] array = new int[]{1,2,3,4,5};
System.out.println(array);
2. 如何理解 [I@4eec7777 ?下图中所说的地址是经过处理过的,也就是说不是真正的地址,但它具有唯一性,所以也可以广义的认为是地址;这个地址是数组首元素的地址,即数组名是数组首元素的地址,array不是一个基本数据类型的变量,而是一个引用数据类型的变量 (用来存地址的变量称为引用变量),简称array为引用,这个引用指向着一个对象(该数组开辟的内存空间);new关键字可以简单理解为用来创建对象的(专业术语:实例化对象)。
五、数组的存储
5.1 JVM内存分布
1. 问:内存为什么要划分几个区域?
答:内存是一段连续的存储空间,主要用来存储程序运行时数据的。比如:
① 程序运行时代码需要加载到内存
② 程序运行产生的中间数据要存放在内存
③ 程序中的常量也要保存
④ 有些数据可能需要长时间存储,而有些数据当方法运行结束后就要被销毁
如果对内存中存储的数据不加区分的随意存储,那对内存管理起来将会非常麻烦,因此JVM也对所使用的内存按照功能的不同进行了划分,以便于更好的管理数据:
① 虚拟机栈(JVM Stack):与方法调用相关的一些信息,每个方法在执行时,都会先创建一个栈帧,栈帧中包含有:局部变量表、操作数栈、动态链接、返回地址以及其他的一些信息,保存的都是与方法执行时相关的一 些信息。比如:局部变量。当方法运行结束后,栈帧就被销毁了,即栈帧中保存的数据也被销毁了。
② 本地方法栈(Native Method Stack):本地方法栈与虚拟机栈的作用类似,只不过保存的内容是Native方法的局部变量(提供给底层的C/C++代码使用)。 在有些版本的 JVM 实现中(例如HotSpot), 本地方法栈和虚拟机栈是一起的。
③ 堆(Heap):JVM所管理的最大内存区域。使用 new 创建的对象都是在堆上保存 (例如前面的 new int[]{1, 2, 3} ),堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还有在使用,就不会被销毁。
④ 程序计数器 (PC Register):只是一个很小的空间, 保存下一条执行的指令的地址。
⑤ 方法区(Method Area): 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,方法编译出的字节码就是保存在这个区域。
---------> 现在我们只简单关心堆 和 虚拟机栈这两块空间,后序JVM中还会更详细介绍。
5.2 内置类型变量与引用类型变量的区别
1. 区别在于部存储的数据意义不同:
基本数据类型创建的变量,称为基本变量,该变量空间中直接存放的是其所对应的值;
引用数据类型创建的变量,一般称为引用变量,其空间中存储的是它指向的对象所在内存空间的地址。
2. public static void func() {
int a = 10;
int b = 20;
int[] arr = new int[]{1,2,3};
}
3. 在上述代码中,a、b、arr,都是func方法内部的变量,因此其内存空间都在func方法对应的栈帧中分配。 a、b是内置类型的变量,因此其空间中保存的就是给该变量初始化的值。 array是数组类型的引用变量,其内部保存的内容可以理解成是数组在堆空间中数组首地址。
4. 从下图可以看到,引用变量并不直接存储对象本身,存储的是对象在堆中开辟空间的起始地址,通过该地址,引用变量便可以去操作对象。有点类似C语言中的指针,但是Java中引用要比指针的操作更简单。
5.3 再谈引用变量
下面将简要分析上面代码中的变量在JVM内存中的分布情况,以及在屏幕上打印的100 200 300 400 500的原因:
1. 在func方法栈帧中为array1这个引用开辟空间,并为array1这个引用所指向的对象在堆区开辟3个连续的整型空间,array1数组中的每个元素的值都被初始化为0,把该空间的起始地址0x112存入array1这个引用中。
2. 通过下标的方式将array1数组中下标为0、1、2的元素中的值修改为10、20、30。
3. 在func方法栈帧中为array2这个引用开辟空间,并为array2这个引用所指向的对象在堆区开辟5个连续的整型空间,array2数组中的每个元素的值都被初始化为0,把该空间的起始地址0x345存入array2这个引用中。
4. 通过下标的方式将array2数组中下标为0、1的元素中的值修改为100、200。
5. array1=array2,即让array1这个引用不再指向堆区地址为0x112的数组空间,而让它与array2这个引用指向同一个对象,这一步结束后,堆区地址为0x112的数组空间将被回收。
6. 通过下标的方式将array1这个引用所指向的对象,即堆区地址为0x345的数组空间中下标为2、3、4的元素中的值修改为300、400、500,此时数组array2中的数组元素依次为100、200、300、400、500。
7. 最后通过循环的方式遍历数组,打印数组array2中的每个元素。
通过上面这个例子也可以知道,一个引用不可以指向多个对象,而一个对象可以被多个引用所指。
5.4 认识 null
1. null 的作用类似于 C 语言中的 NULL (空指针),都是表示一个无效的内存位置,因此不能对这个内存进行任何读写操作,一旦尝试读写,就会抛出NullPointerException (空指针异常).
2. 注意: Java 中并没有约定 null 和 0 号地址的内存有任何关联。
六、数组作为函数的参数
七、数组作为函数的返回值
八、数组练习
8.1 数组转字符串
8.2 数组拷贝
我们点进 copy0f 的实现可以发现它其实是 System.arraycopy 的一个壳子,
点进 System.arraycopy 会发现看不到它代码的实现,native 其实已经告诉了我们为什么(如果一个方法被native修饰,证明这个方法的底层是C/C++代码实现的,这个方法的运行是运行在本地方法栈中的)
我们可以尝试使用一下 System.arraycopy 如下图。
总结一下,目前我们就有了3种拷贝数组的方式了
① for循环 ②Aarrays.copy0f ③ System.arraycopy
8.3 求数组中元素的平均值
8.4 查找数组中指定元素的下标(顺序查找)
8.5 查找数组中指定元素的下标(二分查找)
自己实现款:
利用Arrays自带的方法实现:
8.6 数组排序(冒泡排序)
8.7 数组逆序
8.8 判断数组内容是否一致
8.9 给数组中连续几个元素填充为同一个值
8.10 总结Arrays当前已经学过的方法(9个)
toSting、copyOf、copyOfRange、(System.arraycopy)、
length、sort、binarySearch、equals、fill
九、二维数组
1. Java中的二维数组在初始化时不能省略二维数组中每个元素(一维数组)的{},否则会报错。
2. 在遍历二维数组时,通常会采用下面代码图中遍历二维数组的方式二,即通过对二维数组中每个元素是一维数组的理解进行遍历。
3. Java中二维数组采用动态初始化时的列号可以省略, 此时二维数组中的每个一维数组的地址为null,因此不能对省略列号的数组直接进行使用;列号可以省略,提供给了程序员定义不规则二维数组的方式。
本篇文章已完结,谢谢支持哟 ^^ !!!