在了解以下代码的内容时遇到问题。数组c
和d
的行为是我所期望的。但a
和b
发生了什么? (我也尝试使用普通的标量变量,在任何一种情况下都不会发生任何意外。)
输出被复制到RH注释。
import java.util.Arrays;
public class ArraysParadox {
public static void main(String[] args) {
int[] c = {1, 2, 3};
int[] d = {6, 5, 4, 3};
System.out.print("c: ");
System.out.println(Arrays.toString(c)); // c: [1, 2, 3]
System.out.print("d: ");
System.out.println(Arrays.toString(d)); // d: [6, 5, 4, 3]
System.out.println("--- swap ---");
int[] tmp = c;
c = d;
d = tmp; // <----- Magic?
System.out.print("c' (=d): ");
System.out.println(Arrays.toString(c)); // c' (=d): [6, 5, 4, 3]
System.out.print("d' (=c): ");
System.out.println(Arrays.toString(d)); // d' (=c): [1, 2, 3]
System.out.println("--- c = 0 ---");
Arrays.fill(c, 0);
System.out.print("c (=0): ");
System.out.println(Arrays.toString(c)); // c (=0): [0, 0, 0, 0]
System.out.print("d (=c): ");
System.out.println(Arrays.toString(d)); // d (=c): [1, 2, 3]
System.out.println("--- d = 1 ---");
Arrays.fill(d, 1);
System.out.print("c (=d): ");
System.out.println(Arrays.toString(c)); // c (=d): [0, 0, 0, 0]
System.out.print("d (=1): ");
System.out.println(Arrays.toString(d)); // d (=1): [1, 1, 1]
System.out.println("================");
int[] a = {1, 2, 3};
int[] b = {6, 5, 4, 3};
System.out.print("a: ");
System.out.println(Arrays.toString(a)); // a: [1, 2, 3]
System.out.print("b: ");
System.out.println(Arrays.toString(b)); // b: [6, 5, 4, 3]
a = b;
System.out.print("a (=b): ");
System.out.println(Arrays.toString(a)); // a (=b): [6, 5, 4, 3]
System.out.println("--- α = 0 ---");
Arrays.fill(a, 0);
System.out.print("a (=0): ");
System.out.println(Arrays.toString(a)); // a (=0): [0, 0, 0, 0]
System.out.print("b (=a?): ");
System.out.println(Arrays.toString(b)); // b (=a?): [0, 0, 0, 0] ???
System.out.println("--- b = 1 ---");
Arrays.fill(b, 1);
System.out.print("b (=1): ");
System.out.println(Arrays.toString(b)); // b (=1): [1, 1, 1, 1]
System.out.print("a (=b?): ");
System.out.println(Arrays.toString(a)); // a (=b?): [1, 1, 1, 1]
}
}
c
和d
的可交换性表示根据以下帖子传递值:Java is Pass-by-Value, Dammit!。 (我也查看java array pass by reference does not work?,但我无法理解提问者的英语,方法调用模糊了这个例子。)
请注意,在d = tmp;
行被注释掉后,c
和d
表现出与a
和b
相同的奇怪行为。我仍然不知道该怎么做。
任何人都可以解释a
和b
的行为如何通过传值来解释?
事实证明,我的帖子中的主要问题不是传值,而是别名。为了清楚传值和指针之间的区别,我在代码中添加了以下方法并用它来(尝试)交换c
和d
(由{{3建议)通过上面链接的JavaDude文章链接。
static <T> void swap (T c, T d) {
T tmp = c;
c = d;
d = tmp;
}
结果是c
和d
恢复原状。如果Java(如C)将指向c
和d
的指针传递给方法,那么这将起作用,但它只是传递它们的值,保持原始变量不变。
将a = b
更改为a = b.clone();
或a = Arrays.copyOf(b, b.length);
会产生我期望的行为。此代码也有效:
int[] tmp = new int[b.length];
System.arraycopy( b, 0, tmp, 0, b.length );
a = tmp;
相对时间描述article。
答案 0 :(得分:11)
这里没有什么“怪异”:数组变量是引用到实际数组(在其他语言中也称为指针)。当你操纵数组变量时,你所做的就是操纵指针。
当您将数组变量分配给另一个变量时,可以为您指定的变量指向的数组创建别名,并使之前指向的变量指向的数组符合垃圾条件采集。由于作业a = b
使a
成为b
的别名,因此使用数据填充b
与使用数据填充a
完全相同:一旦作业完成,a
和b
只是两个不同的名称。
就传递值而言,在您的示例中没有进行任何操作:仅当您将对象作为参数传递给您调用的方法时,传递值的概念才适用。在您的示例中,变量a
,b
,c
和d
不是方法参数,它们是局部变量。您确实通过引用方法toString
和fill
来传递它们(或者更确切地说,您将对象的引用传递给toString
和fill
,因为在Java中一切是按值传递的,这就是为什么在从方法返回时可以看到由fill
完成对数组的修改。
答案 1 :(得分:2)
当您进行a = b;
之类的作业时,如果a
和b
不是基元,那么它们现在引用相同的对象。在您的情况下,他们现在引用相同的数组。您对其中任何一项所做的任何更改也会影响另一项(因为您只更新了一件事,a
和b
都指向它。)
请注意,此行为与参数在Java中的传递方式无关。
答案 2 :(得分:1)
数组初始化程序在引擎盖下调用new(所以在这种情况下它是语法糖精)。您在顶部附近的交换只是交换引用,下面的交换正好执行您期望引用执行的方式。
链接的文章引用参数...在Java中,所有参数都是按值的,只是引用本身通过值传递,即对REFERENCE(而不是其解除引用的内容)的更改不会反映在范围之外。子程序。
答案 3 :(得分:0)
在java类中通常没有教授或遗漏数组的重要一点。当数组传递给函数时,会为同一个数组创建另一个指针(永远不会传递相同的指针)。您可以使用两个指针来操作数组,但是一旦将第二个指针分配给被调用方法中的新数组并通过void返回调用函数,则原始指针仍保持不变。
您可以在此处直接运行代码:https://www.compilejava.net/
import java.util.Arrays;
public class HelloWorld
{
public static void main(String[] args)
{
int Main_Array[] = {20,19,18,4,16,15,14,4,12,11,9};
Demo1.Demo1(Main_Array);
// THE POINTER Main_Array IS NOT PASSED TO Demo1
// A DIFFERENT POINTER TO THE SAME LOCATION OF Main_Array IS PASSED TO Demo1
System.out.println("Main_Array = "+Arrays.toString(Main_Array));
// outputs : Main_Array = [20, 19, 18, 4, 16, 15, 14, 4, 12, 11, 9]
// Since Main_Array points to the original location,
// I cannot access the results of Demo1 , Demo2 when they are void.
// I can use array clone method in Demo1 to get the required result,
// but it would be faster if Demo1 returned the result to main
}
}
public class Demo1
{
public static void Demo1(int A[])
{
int B[] = new int[A.length];
System.out.println("B = "+Arrays.toString(B)); // output : B = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Demo2.Demo2(A,B);
System.out.println("B = "+Arrays.toString(B)); // output : B = [9999, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
System.out.println("A = "+Arrays.toString(A)); // output : A = [20, 19, 18, 4, 16, 15, 14, 4, 12, 11, 9]
A = B;
// A was pointing to location of Main_Array, now it points to location of B
// Main_Array pointer still keeps pointing to the original location in void main
System.out.println("A = "+Arrays.toString(A)); // output : A = [9999, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
// Hence to access this result from main, I have to return it to main
}
}
public class Demo2
{
public static void Demo2(int AAA[],int BBB[])
{
BBB[0] = 9999;
// BBB points to the same location as B in Demo1, so whatever I do
// with BBB, I am manipulating the location. Since B points to the
// same location, I can access the results from B
}
}