4

无法理解下面的代码中发生了什么。数组的行为cd我所期望的。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]
    }
}

根据这篇文章的可交换性cd表示按值传递:Java is Pass-by-Value, Dammit!. (我也看了java array pass by reference does not work?,但是我看不懂提问者的英文,方法调用遮盖了例子。)

请注意,该行已被d = tmp;注释掉,c并表现出与和d相同的奇怪行为。我仍然不知道该怎么做。ab

谁能解释如何用传递值来解释a和的行为?b

编辑:附录

事实证明,我帖子中的主要问题不是按值传递,而是别名。为了清楚按值传递和指针之间的区别,我在我的代码中添加了以下方法,并用它来(尝试)交换cd(由上面链接的 JavaDude 的文章链接的文章建议)。

static <T> void swap (T c, T d) {
    T tmp = c;
    c = d;
    d = tmp;
}

结果就是这样cd回来不变。如果 Java(如 C)传递指向c和指向d方法的指针,这将起作用,但它只是简单地传递它们的值,而原始变量保持不变。

更改a = ba = b.clone();或更改为a = Arrays.copyOf(b, b.length);我所期望的行为。此代码也适用:

    int[] tmp = new int[b.length];
    System.arraycopy( b, 0, tmp, 0, b.length );
    a = tmp;

此处描述的相对时间。

4

4 回答 4

11

这里没有什么“奇怪”的地方:数组变量是对实际数组的引用(在其他语言中也称为指针)。当您操作数组变量时,您所做的就是操作指针。

当您将一个数组变量分配给另一个变量时,您会为您分配的变量指向的数组创建一个别名,并使之前被分配的变量指向的数组符合垃圾回收的条件。因为赋值a = b产生a了 的别名b,所以填充数据的行为与填充b数据的行为完全相同a:一旦赋值完成,a并且b只是同一事物的两个不同名称。

按值传递而言,您的示例中没有发生任何事情:仅当您将对象作为参数传递给您调用的方法时,按值传递的概念才适用。在您的示例中,变量abcd不是方法参数,它们是局部变量。您确实通过对方法的引用传递它们toStringand fill(或者更准确地说,您通过值传递对对象的引用到toStringand fill,因为在 Java 中一切都是按值传递的),这就是为什么对数组所做的修改fill在返回时可见从方法上。

于 2012-06-07T00:02:45.697 回答
2

a = b;当您进行像, ifa和are not 原语之类的赋值时b,它们现在引用同一个对象。在您的情况下,它们现在引用相同的数组。您对其中任何一个所做的任何更改也会影响另一个(因为您只更新一件事,并且两者ab指向它)。

请注意,此行为与 Java 中的参数传递方式无关。

于 2012-06-07T00:04:52.270 回答
1

数组初始值设定项在后台调用 new(所以在这种情况下,它是句法 saccharine)。靠近顶部的交换只是交换引用,下面的交换完全按照您期望的引用执行。

链接的文章指的是参数...在Java中,所有参数都是按值传递的,只是引用本身是按值传递的,即对REFERENCE(而不是其取消引用的内容)的更改不会反映在子例程的范围之外。

于 2012-06-07T00:00:55.667 回答
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
    }
}
于 2017-02-16T05:44:53.397 回答