先复习一下方法的递归吧
程序能实现方法递归的核心原因:当在程序中调用另一个函数时,当前程序会暂缓执行,直到函数调用结束,当前程序才会继续执行。
在什么场景下能够使用方法递归呢?
- 大问题可以拆分为若干个子问题的解
- 拆分后的子问题与原问题除了数据规模不同,其解决思路完全相同
- 存在递归的终止条件(拆分是有终点的,不会无限制的拆分下去)
如何写出正确的代码:最核心的问题就是关注这个方法的语义(功能)(深思这句话)
我假设这个方法已经实现好了,你就调用别人写好的函数来辅助你解决问题。
前面的章节知识
目录
7 数组的定义与使用
7.1 什么是数组
数组本质上就是让我们能够“批量“创建相同类型的变量。
一次定义N个相同类型的变量,我们就把这种结构称之为数组。
7.2 数组的创建与动态初始化
数组的动态初始化有两种方法:
- 数据类型[] 数组名称 = new 数组类型[]{初始化数据}
- 数据类型[] 数组名称 = new 数据类型[num] num 表示当前数组的最大元素个数
int[] arr1 = new int[]{1,2,3,4};
int[] arr2 = new int[4];
还有数组的静态初始化:
数据类型[] 数组名称 = {初始化数据};
int[] arr3 = {1,2,3,4};
注意:数组的静态初始化是语法糖,javac 编译之后就是动态初始化
.
7.3 数组的使用
我们要使用一个数组,首先要知道这个数组可以存储多少个元素,就要知道这个数组的长度,那怎样得到数组的长度呢?
获得一个数组的长度(最多保存的元素个数):
直接使用数组名称.length
int[] arr1 = new int[]{1,2,3,4,5};
int[] arr2 = new int[5];
arr1.length; //得到arr1的长度
arr2.length; //得到arr2的长度
那如何访问数组元素呢?
数组名称[要访问的元素相比较与第一个元素的偏移量]
数组名称[元素的索引]
解释一下元素的索引:数组的索引从0
开始,最后一个元素的索引是arr.length - 1
为什么索引要从0开始呢?
索引其实就是”偏移量“,相较于数组的第一个元素的单位长度,第一个元素的偏移量不就是0么
数组在内存中存储时,每个元素之间都是顺序存储的,保存的数组就是数组的首元素的地址,要找到其他元素,只要知道其他元素相较于第一个元素的距离就能找到
int[] arr = new int[]{1,2,3,4,5};
System.out.println(arr[0]);//数组的第一个数
System.out.println(arr[4]);//数组的最后一个数
遍历数组的每个元素,可以有两种方法:
- 使用for循环,索引数组每个元素的下标,实现遍历数组的每个元素
- 使用 JDK1.5引入的for-each循环,增强型for循环
第一种方法:
int[] arr = new int[]{1,2,3,4,5,6};
for(int i = 0; i < arr.length; i++){
System.out.println(arr[i] + ",");
}
这种方法遍历的数组,arr[i] 是真正拿到了数组的每个元素,修改的话,也是直接修改数组。
第二种方法:
int[] arr = new int[]{1,2,3,4,5,6};
for(int i:arr){
//i指的是从数组的第一个元素开始取值,第一次把第一元素的值复制一份给i
//第二次循环把第二个元素的值复制一份给i,
//依次类推,直到整个数组都遍历结束
System.out.println(i + ",");
}
for-each循环遍历数组,只能读取数组的元素值,无法进行修改!!
i 是原数组每个元素的值拷贝
,并不是实实在在的数组元素
7.4 数组与方法之间的关系
数组是引用数据类型,具体就是整型数组的引用
7.4.1 数组作为方法的参数
创建一个方法,接收任意类型的整型数组并打印
public static void prinArr(int[] num){};
在实际代码中,在主方法中调用的参数是实参,在普通方法中调用的是形参,只不过拷贝的是实参的地址,具体的往下看
7.4.2 关于引用数据类型的理解问题
JVM把数据内存划分为6个区域,在这里就只说”栈区“ 和 ”堆区“。
方法的调用就是在栈区进行的,每个方法的调用过程。就是一个栈帧的入栈以及出栈过程。
“栈” —先进后出的结构。方法中的局部变量和形参都在栈中存储,当方法调用结束出栈时,临时变量都会被销毁。
就像一个碗一样,先加的饭在下面,后加的饭在上面,先吃掉的一定是后加的饭
JVM 的另一块内存区域称为“堆区”,所有对象都在堆区存取数组对象,类的实例化对象,接口的对象。
public static void main(String[] args){
int[] arr = new int[]{10,20};
swapArr(arr);
System.out.println("arr[0]= " + arr[0] + "arr[1]= " + arr[1]);
}
public static void swapArr(int[] arr){
int temp = arr[0];
arr[0] = arr[1];
arr[1] = temp;
}
我们来具体看这个代码:
int[] arr = new int[]{10,20}; //程序的执行都是从右到左
//等号右边表示:数组对象,在堆上开辟内存空间进行存储
//等号左边表示:数组的引用
引用就是起了一个“别名”,保存的数值就是该对象的地址。
对于数组对象来说,数组引用实际上是保存了数组的首元素地址。
swapArr(arr);//arr是实参
//实参到形参的传递仍然满足值传递,只是把main -> arr存储的数组地址拷贝一份复制
//给swapArr->arr,这样他们在栈中存储的变量就指向了堆中的同一个地址,也就是数组首元素的地址
在swapArr执行:
public static void swapArr(int[] arr){
int temp = arr[0];
arr[0] = arr[1];
arr[1] = temp;
}
在swapArr
中,对于arr的元素进行了交换,因为arr存储了数组的地址,与main->arr
是同一个地址,所以在swapArr
中进行的数组元素交换对于main->arr是可见的。
本质上来说,main->arr
和swapArr->arr
指向了堆中的同一块内存区域!!
System.out.println("arr[0]= " + arr[0] + "arr[1]= " + arr[1]);
此时 swapArr方法已经销毁,
在程序中看见new
关键字,就一定在堆中开辟了新的空间,要操作堆中的对象,必须要通过引用来进行,必须知道这个对象的名字才能进行操作。
7.5 数组的工具类
在JDK中看到某些类后加s,这种类都是工具类,提供了大量有用的方法,直接调用就可以。
Arrays - 数组的工具类,包含数组转字符串的方法,数组排序的方法,等等操作数组的各种方法都在这个类中,我们直接通过类名称来调用
Collections - 集合的工具类
将数组对象转为字符串对象: Arrays.toString
int[] arr = {1,2,3};
String str = Arrays.toString(arr);
System.out.println(str);
拷贝数组:Arrays.copyOf
Arrays.copyOf(int[] original, int newLength);
//返回拷贝的新数组
//复制指定的数组,用零截取或填充(如有必要),以便复制具有指定的长度。
注意:
- 若新数组长度 < 原数组长度, 部分拷贝。从原数组的第一个元素开始复制值,直到元素个数达到新数组的长度停止。
- 若新数组的长度 = 原数组的长度。 全部拷贝
- 新数组的长度 > 原数组长度。全拷贝,剩余的元素用该数据类型的默认值来补。
在运行程序时,出现ArrayIndexOutOfBoundsException 报错,说明是数组越界了。
总结:
数组对象就是实实在在的在堆中保存数据的实体,new出来的都在堆中存储。
数组的引用就是给这块数组对象起了个名字,保存这个数组对象的首地址而已。
要是对大家有所帮助的话,请帮我点个赞吧。