Java方法参数拼图

use*_*476 1 java arguments pass-by-reference

我偶然发现了Java的一个非常令人费解的功能(?).

似乎使用"new"关键字替换方法参数将该对象转移到不同的范围:

import java.util.ArrayList;

public class Puzzle {
    public static void main(String[] args) {
        ArrayList<Integer> outer = new ArrayList<Integer>();
        outer.add(17);
        Puzzle.change(outer);
        outer.add(6);
        System.out.println(outer);

        // excpected output:
        // [23]
        // [23, 6]
        //
        // actual output:
        // [23]
        // [17, 7, 6]
    }

    public static void change(ArrayList<Integer> inner) {
        inner.add(7);
        inner = new ArrayList<Integer>();
        inner.add(23);
        System.out.println(inner);
    }
}
Run Code Online (Sandbox Code Playgroud)

任何人都可以解释这种奇怪吗?我注意到了与分配相同的行为.

Yis*_*hai 8

这是Java初学者的经典之旅.部分问题在于我们缺乏识别这一点的共同语言.参数是按值传递的吗?对象通过引用传递?根据您与谁交谈,对于通过值传递的单词会有不同的反应并通过引用传递,即使每个人都想说同样的事情.

可视化的方法是理解java变量可以是两件事之一.它可以包含基元或对象的引用.将该引用视为指向内存中某个位置的数字.它不是C++中的指针,意味着它不指向特定的内存位置,它更像是一个句柄,因为它允许JVM查找可以找到对象的特定内存位置.但你可以这样形象化它:

   ArrayList<Integer> outer = @1234; //The first reference where the ArrayList was created.
Run Code Online (Sandbox Code Playgroud)

然后使用以下参数调用inner:

  Puzzle.change(@1234);
Run Code Online (Sandbox Code Playgroud)

请注意,如果不传递外部变量,则传递@ 1234的值.通过作为方法的参数,不能以任何方式更改外部.当我们说通过价值时,这就是我们的意思.传递该值,但它与外部变量断开连接.

内部拼图:

public static void change(ArrayList<Integer> inner) { // a new reference inner is created.
    //inner starts out as @1234
    inner.add(7);
    //now inner becomes @5678
    inner = new ArrayList<Integer>();
    //The object @5678 is changed.
    inner.add(23);
    //And printed.
    System.out.println(inner);
}
Run Code Online (Sandbox Code Playgroud)

但是外部仍然指向@ 1234,因为方法不能改变它,它从来没有外部变量,它只是有它的内容.但是,由于更改方法是在引用@ 1234时开始的,因此该方法确实可以更改该位置的对象,并且对@ 1234的任何其他引用都可以看到结果.

更改方法完成后,没有任何内容引用对象@ 5678,因此它有资格进行垃圾回收.


KLE*_*KLE 6

这是初学者的经典java问题之一.

内部参数按值传递(对于非基本对象,它是引用).如果您对它采取行动,它会影响与外部代码中相同的对象,因此您可以看到外部方法中的影响.

如果用新对象替换对象,它就不一样了.当您处理新的时,您不会更改上一个,并且您没有看到外部方法的影响.


更新:对不起引起很多评论的词汇错误.我现在纠正了我的回答.我相信这一点,但现在更清楚了.

  • 实际上,它是按值调用的,这就是为什么你不能改变外部引用的值的原因. (5认同)

Ale*_*äll 6

在java中,您可以将所有变量视为指向真实对象的指针.

这是怎么回事:

  1. 您创建一个列表.
  2. 添加17.
  3. 将列表发送到另一个具有指向列表的指针的函数.
  4. 将7添加到第一个列表.
  5. 创建一个新列表并将"内部"指针指向该列表.
  6. 将23添加到新列表中.
  7. 打印新列表并返回.
  8. 在原始函数中,您仍然将指针指向与之前相同的对象.
  9. 你在第一个列表中添加6.
  10. 并打印第一个列表.