2.3 通过方法更深刻理解引用变量
有了上面的认识,我们就要来理解下面这两个方法的作用了,相信你看完会有更深刻的认识:
是不是结果可能跟你想得有点不一样呢?不用担心,我们来一个个分析下:
首先我们执行的是 func1 我们知道 array 变量中存的是一个对象的地址,那么通过传参给 func1 的 arr1,首先要建立栈帧,把 array 存的地址拷贝到 arr1 当中,这样一来就相当于 arr1 也指向了那个变量,但是我们又 new 了一个对象,并把新对象的地址赋值给了 arr1,相当于 arr1 原来存放的地址已经被替换了,也就说 arr1 指向了一个新的对象,因为只是把 array 存的地址拷贝给了 arr1 所以执行完 func1 这个方法之后,array 并不会受任何影响,当方法结束,arr2 变量销毁,因为arr2 销毁之后没有变量接着引用在 func1 中 new 的新对象,所以此时新的对象就被 JVM 回收了!
当我们执行完 func1 时,就是我们说的结果,所以 array 的值不受任何影响!
我们接着再来看执行 func2 之后的结果,首先前半部分与 func1 一样,都是传递的 array 指向对象的地址,但是 func2 里面语句是直接对 arr2 中对象的地址进行下标访问,修改了 [1] 下标处的值,因为本质 array 和 arr2 引用的都是同一个对象,当 arr2 修改了对象的值,所以当函数结束后接着打印 array 指向对象的值肯定也被修改了!方法结束,arr2 被销毁,但是 arr2 指向的对象仍然被 array 指向着,所以JVM不会回收此时的对象!
看到这,你肯定有了更深刻的理解,前面有疑问的地方肯定得到了解决,所谓的 "引用" 本质上只是存了一个地址,Java将数组设定成引用类型,这样后续进行数组传参,其实只是将数组的地址传入到形参当中,这样就可以避免对整个数组的拷贝!
在我们目前认识中,如果对象没有被引用,则会自动回收,所以不用考虑内存泄漏的问题
2.4 数组作为函数返回值
假设这里我们有一个题,需要实现一个方法,这个方法需要获取斐波那契数列的前 n 项,需要返回一个数组回来,本质其实就是返回数组的地址,如何实现呢?
public static int[] fib(int n) { if (n <= 0) { return null; } int[] array = new int[n]; array[0] = array[1] = 1; for (int i = 2; i < n; i++) { array[i] = array[i - 1] + array[i - 2]; } return array; } public static void main(String[] args) { int[] array = fib(8); System.out.println(Arrays.toString(array)); }
当然这个方法如果 n 为1 就会越界,这个下来可以自己优化下,来到这里我们就来介绍下Arrays这个工具类里面的一些方法了: 像一些将数组转换成字符串,数组二分查找,数组排序等等,可以下来查阅下帮助手册,这里我就不细说了,交给大家自己去扩展了,如果以后用到,我会进行说明。
3、二维数组
3.1 二维数组的概念和内存布局
这里我们一定要有一个概念,二维数组是一个特殊的一维数组,如何理解呢?用文字来说,二维数组的每个元素是一维数组,也就是说,二维数组的每个元素里面放的是一维数组的地址!
相信听完上面的话,大家可能不是很理解,那我们就定义一个二维数组,并画图:
public static void main(String[] args) { int[][] array = { {1,2,3}, {4,5,6} }; }
这里就很清晰明了了,图中也能看到,二维数组本质就是一个一维数组,只不过这个一维数组的每个元素存的是地址而已!
3.2 二维数组的定义和初始化
当然,二维数组和一维数组一样,同样的三种定义方式:
int[][] array1 = { {1,2,3}, {4,5,6} }; //两行三列的二维数组 int[][] array2 = new int[][]{ {1,2,3}, {4,5,6} }; int[][] array3 = new int[2][3];
如果只是单纯的 int[][] array; 这样数组里面没有任何引用对象,并不能直接使用未引用对象的数组,如果不引用最好置null
3.3 二维数组遍历
for 循环打印:
public class TestDemo { public static void main(String[] args) { int[][] array1 = { {1,2,3}, {4,5,6} }; //两行三列的二维数组 for (int i = 0; i < array1.length; i++) { for (int j = 0; j < array1[i].length; j++) { System.out.print(array1[i][j] + " "); } System.out.println(); } System.out.println(); } }
这里第一个 for 里面的长度求的是 array1.length,因为它本身就是一个特殊一维数组,这样求出的就是它的行,第二个for array1[i].length,自然求得就是每一行一维数组长度了
for-each 循环打印:
public class TestDemo { public static void main(String[] args) { int[][] array1 = { {1,2,3}, {4,5,6} }; //两行三列的二维数组 for (int[] arr : array1) { //二维数组的每个元素是一维数组,所以每个元素的类型是int[] for (int a : arr) { //arr是一维数组,每个数组的元素的int System.out.print(a + " "); } System.out.println(); } } }
Arrays 工具类有个方法也可以打印二维数组,是Arrays.deepToString,这个方法也是打印二维数组的但只能在一行显示,感兴趣的可以下来自己实验下。
3.4 不规则的二维数组
在Java中,二维数组的列可以省略,也就是把二维数组看成一个特殊的一维数组的每个元素指向的一维数组是可以元素个数不同的,如何理解这句话呢?我们还是一样通过一小段并且以画图的形式给大家讲解:
int[][] array = new int[2][]; array[0] = new int[3]; array[1] = new int[2];
当然,他的遍历方法是跟普通二维数组一样的, 这里我就不多说,自己摸索,实在不懂可以问下博主。