2.2 基本类型变量与引用类型变量的区别
基本数据类型创建的变量,称为基本变量,该变量空间中直接存放的是其所对应的值
引用数据类型创建的变量,一般称为对象的引用,其空间中存储的是对象所在空间的地址
public class Test { public static void main(String[] args) { int a = 10; int[] arr = new int[]{1,2,3}; } }
a 是基本数据类型,它直接在栈上开辟空间,然后把值存储在对应的空间里面
arr 是数组类型的引用变量,其内部保存的内容可以简单理解成是数组在堆空间中的首地址
引用变量并不直接存储对象本身,可以简单理解成存储的是对象在堆中空间的起始地址。通过该地址,引用变量便可以去操作对象
public class Test { public static void main(String[] args) { int a = 10; int[] arr = new int[]{1,2,3}; arr[0] = 99; } }
通过 arr 在栈中存储的地址,然后找到堆中对应地址的空间,去访问 arr[0],将其值改为 99
2.3 认识null
null 在 Java 中表示 "空引用" , 也就是一个不指向对象的引用
null 的作用类似于 C 语言中的 NULL (空指针), 都是表示一个无效的内存位置. 因此不能对这个内存进行任何读写操 作. 一旦尝试读写, 就会抛出 NullPointerException.
注意: Java 中并没有约定 null 和 0 号地址的内存有任何关联
2.4 参数传数组类型
我们之前说方法返回值最多只能有一个,如果想改变形参的同时也改变实参,那我们就可以把数组作为实参传给形参
那为什么把数组作为实参传给形参就可以改变实参咧?
因为我们开辟了一个数组,它一定是开辟在堆上的,栈上存的是堆上数组的首地址,把栈上存的地址传给了形参,那么形参此时里面存储的也是这个地址,当形参去读取的时候也找到了堆上这块空间了,所以它就会直接去操作这块空间。形参指向的是这块空间,实参也指向了这块空间,那形参改变了这块空间的内容,实参还是指向了这块空间的,所以当实参是数组传给形参的时候改变形参就会改变实参
public class Test { public static void fun1(int[] arr) { arr[0] = 99; } public static void main(String[] args) { int[] arr = new int[]{1,2,3}; System.out.println(arr[0]); fun1(arr); System.out.println(arr[0]); } }
打印结果:
当大家看到上面这幅图片分析,应该也就理解了为什么实参是数组时传给形参会改变实参了。
public class Test { public static void fun1(int[] arr) { arr = new int[7]; arr[0] = 99; } public static void main(String[] args) { int[] arr = new int[]{1,2,3}; System.out.println(arr[0]); fun1(arr); System.out.println(arr[0]); } }
打印结果:
当实参数组时,将实参栈中存的地址传给了形参,但是在形参中有重新开辟了一块空间,那么形参将会指向新的空间,当它改变新的空间的值时,将不会影响到实参,也不会改变实参数组里面的值。
注:数组不需要销毁,当没有数组变量指向它的时候就会自动销毁
总结: 所谓的 "引用" 本质上只是存了一个地址. Java 将数组设定成引用类型, 这样的话后续进行数组参数传参, 其实只是将数组的地址传入到函数形参中. 这样可以避免对整个数组的拷贝(数组可能比较长, 那么拷贝开销就会很大)
注:
传给形参基本数据类型的变量,相当于临时拷贝
传给形参引用数据类型的变量,相当于把变量在栈上存储的堆中数组的地址传给了形参,那么实参和形参同时指向同一块空间
2.5 作为函数的返回值
求斐波那契数列的前N项:
public class Test { 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(10); for (int i = 0; i < array.length; i++) { System.out.println(array[i]); } } }
方法中返回数组相当于返回了数组的地址。
3.二维数组
二维数组本质上也就是一维数组, 只不过每个元素又是一个一维数组
int[][] arr = new int[][]{{1,2,3},{4,5,6}};
看到这幅图相信就明白了二维数组了
二维数组的遍历:
public class Test { public static void main(String[] args) { int[][] array = new int[][]{{1,2,3},{4,5,6}}; for (int[] arr: array) { for (int x: arr) { System.out.println(x); } } } }
打印结果: