int[] array1 = new int[10];
// 创建一个可以容纳10个int类型元素的数组
double[] array2 = new double[5];
// 创建一个可以容纳5个double类型元素的数组
String[] array3 = new double[3];
// 创建一个可以容纳3个字符串元素的数组
public class Test { public static void main(String[] args) { int[] array = {1,2,3,4}; fun1(array); System.out.println(Arrays.toString(array)); fun2(array); System.out.println(Arrays.toString(array)); } public static void fun1(int[] array){ array = new int[]{11,22,33,44}; } public static void fun2(int[] array){ array[0] = 99; } }
输出结果:
[1, 2, 3, 4] [99, 2, 3, 4]
可以看出来fun1没有起作用, 但是fun2起作用了
为什么fun1没有起作用呢
解释如下:
在Java中,参数传递是按值传递的,这意味着方法接收的是实际参数值的副本(将实际参数的值复制一份传递给方法),而不是参数本身。
在`fun1`方法中,对`array`参数进行了重新赋值,但这只会在`fun1`方法内部产生影响,不会影响到`main`方法中的`array`。
这一句array = new int[]{11,22,33,44}; 可以分为两个步骤来理解:
new int[]{11, 22, 33, 44}
: 这一部分在堆内存中创建了一个新的整数数组,并将数组的引用(地址)返回。这个数组是匿名的,因为没有指定数组的变量名。
array = ...
:将步骤1中创建的数组的引用赋值给名为array
的变量。现在,array
变量引用堆内存中创建的整数数组。具体来说,当调用`fun1(array)`时,`array`的副本被传递给了`fun1`方法,然后在`fun1`方法内部,重新给`array`赋值为一个新的数组。这个新的数组在堆内存中分配,但它只在`fun1`方法的作用域内有效。一旦`fun1`方法执行完毕,这个新数组的引用就丢失了,而`main`方法中的`array`仍然引用原来的数组。
画了幅图 理解一下:
相比之下,在`fun2`方法中,修改的是原数组中的元素,而不是创建一个新的数组。因此,这个修改是在原数组的内存地址上进行的,对于调用`fun2`的`main`方法中的`array`是可见的,因此`fun2`方法的修改在`main`方法中是起作用的,导致输出 `[99, 2, 3, 4]`。
(简单点说, 形参实参引用类型存储的都是同一个地址, 都是指向栈中地址为0x12的数组对象)
因此不是传了引用就能够修改实参的值
学过C语言的同学注意一下,c语言中分传值调用和传址调用, 但是java中,严格意义来说,只有传值调用, 有时候传的值为地址
引用不相当于地址, 在java中, 引用是引用变量的简称,
- 引用变量存的是地址,
- 引用指向对象
public static void main(String[] args) { int x = 10; System.out.println(x); } public static void func1(int x){ x = 20; }
栈上的变量的地址是取不出来的
对于基本数据类型的变量,其实际的数值直接存储在栈上,而不是在堆上分配内存,并且在栈上的变量地址是无法直接获取的。
堆上的对象,包括通过
new
关键字创建的对象,是由Java虚拟机动态分配和释放内存的,可以通过引用来访问堆上对象,但通常不能直接获取对象的地址。public static void main(String[] args) { int[] ret = func5(); System.out.println(Arrays.toString(ret)); } public static int[] func5() { int[] arr = {1,2,3,4,5}; return arr; }
先new了一个数组, 在形参arr中存储的是栈上的地址, 再return给ret ret也拿到了栈上的0x99地址, 指向了数组{1,2,3,4}
小应用:数组作为返回值的斐波那契数列
public static void main(String[] args) { int[] arr = fib(10); System.out.println(Arrays.toString(arr)); } public static int[] fib(int n) { if (n <= 0) { return null; } int[] arr = new int[n]; arr[0] = arr[1] = 1; for (int i=2; i
定义
int[][] arr = new int[][]{{1,2,3},{4,5,6}}; 或者 int[][] arr = {{1,2,3},{4,5,6}};
数组定义可以省略列不能省略行
因此可以定义不规则数组(即对于所有行,每一行的列数不一定相同)
int[][] arr1 = new int[2][];
对于arr1的每一行, 都是一个未初始化的一维数组, 每一行都可以单独new
遍历
对于arr.length 相当于arr的行数
这个数组arr[0].length相当于一维数组arr[0]的长度
因此遍历可以这样双重for循环
for (int i = 0; i < arr.length; i++) { for (int j = 0; j < arr[0].length; j++) { System.out.print(arr[i][j]+" "); } System.out.println(); }
那么如何使用for each呢
对于一维数组
int[] array = {1,2,3,4}; for (int x: array) { System.out.print(x+" "); }
对于二维数组
for (int[] intx: arr) { for (int x: intx) { System.out.print(x+" "); } System.out.println(); }
里面的是一维数组遍历, 那么外面同样是一维数组遍历,每个元素是二维数组的每一行