【问题标题】:Parameter Passing (ArrayList vs Integer) in JavaJava中的参数传递(ArrayList vs Integer)
【发布时间】:2019-09-13 06:16:39
【问题描述】:

Java 严格按值传递。例如,如果我将一个整数传递给一个更改它的值的方法并且不返回该值,那么 main 方法中的整数将不会被更改。

但是,当我将ArrayList 传递给将项目添加到列表的方法时,结果发现该方法更改了主方法中的 ArrayList。

public class test {
    public static void main(String[] args) {
        // create a array list
        ArrayList<String> item = new ArrayList<String>();
        addItems(item);
        System.out.println("Items in the main method are: ");
        for (int i = 0; i < item.size(); i++) {
            System.out.println(item.get(i));
        }
        System.out.println("\n***************************\n");
        int num = 0;
        plusOne(num);
        System.out.println("The value of num in the main method is: " + num);
    }

    //add two items to arrayList
    public static void addItems(ArrayList<String> item) {
        item.add("Item #1");
        item.add("Item #2");
        System.out.println("Items in the addItems method are: ");
        for (int i = 0; i < item.size(); i++) {
            System.out.println(item.get(i));
        }

    }

    //add one to num
    public static void plusOne(int num) {
        num = num +1;
        System.out.println("The value of num in the plusOne method is: " + num);

    }
}

这是输出:

Items in the addItems method are: 
Item #1
Item #2
Items in the main method are: 
Item #1
Item #2

***************************

The value of num in the plusOne method is: 1
The value of num in the main method is: 0

这令人困惑。 为什么 addItems() 改变了 itemplusOne() 没有改变 num ? 有人可以解释一下吗?谢谢!

【问题讨论】:

标签: java arraylist methods parameters


【解决方案1】:

您需要区分按值传递和按引用传递。

当您将对象(如您自己的自定义类的实例或List 实例)作为参数传递给您的方法时,它们是通过引用传递的。基本上,您仍然有一个指向原始对象的指针,并且您正在直接编辑该对象。这就是您在 List 中看到新项目的原因。

但是,在 Java 中,原始数据类型和不可变类(如 String)是按值传递的。换句话说,您在plusOne 方法内收到的num 与您在方法外收到的num 不同。这就是为什么您不会在方法范围之外看到您的更改。

只需查看String 的文档,您就会看到大量的方法采用String 并返回一个新的String。他们不会直接修改输入字符串。

【讨论】:

  • 在 Java 中,对象总是 按值传递。只是传递的值是引用。如果是pass-by-reference,那么在addItems 方法内调用item = new ArrayList&lt;&gt;() 也会将新实例分配给main 方法内的item。然而,这不会发生。但是引用的副本指向同一个对象实例,因此对该实例的操作对所述实例的所有引用都是可见的。此外,不可变对象在 Java 本身眼中并不特殊。
【解决方案2】:

它是按值传递的——在这种情况下,传递的值是一个地址(Java 调用这些引用)指向一个保存数组实例的内存块。

这在您的本地参数中分配:item。当你做item.add(...)时,它会尝试解引用——查找——本地item中的地址所指向的对象——并对其执行操作(add)。

为了说明这仍然是按值传递,请考虑以下(语法可能不完全有效):

ArrayList<String> x = null;
addItems(x);

void addItems(ArrayList<String> arr) {
   // arr will now have the null address
   // arr.add("xxx"); -> throws NullPointerException -> nothing to dereference

   // Consider another scenario, if here I do:
   arr = new ArrayList<>();

   // x outside won't see the change, because it had copied over its address, null
   // but not the "real" x symbol reference itself.
}

对于像int 这样的原语,值是内容本身,像3 这样的值是复制到本地参数的一系列位。

但对于非原始对象,任何从Object 下降的东西——基本上都是地址作为值——直到你应用. 运算符,它会查找变量引用的对象。

希望这更有意义。

【讨论】:

  • 作为旁注,值得学习指针的概念——这就是 Java 引用——一个非常受限且类型安全的版本——任意指针。大多数人在 C 和 C++ 中遇到过指针,但它们出现在其他语言中——关键是它们持有其他东西的地址。您取消引用一个指针以获取“其他”。
【解决方案3】:

这只是引用(如果您熟悉 C/C++,则为指针)。

在 Java 中,List/ Array ... 或任何对象都有引用,这意味着 Java 将它们存储在同一个内存单元中。

因此,当您将对象 String 传递给函数 addItems() 时,仍然会保留该对象的引用。并且函数内部对象的任何更改都会受到变量的影响,因为它存储在同一个内存单元中。

另一方面,plusOne()接收原始类型(int/long/double)的参数,它没有引用。因此函数内部的任何更改都不会受到影响。

如果您更改 plusOne(Integer num) 而不是 (int num) 并发送参数 í Integer,您会看到差异。

【讨论】:

    【解决方案4】:

    Java 是按引用传递还是按值传递,这是非常基本的概念?回答 Java 是按值传递的,在您传递原始类型的情况下,它是由该值的副本传递的(所有原始类型都不是引用类型),因此 num 在 main 方法中没有改变,并且在ArrayList 它作为对指向相同内存位置的 addItems() 方法的引用的副本传递。

    为了进一步了解,您可以参考此链接Is java pass by reference or pass by value的帖子

    【讨论】:

      猜你喜欢
      • 2011-07-09
      • 2021-07-22
      • 2015-06-07
      • 1970-01-01
      • 2014-05-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多