Java 按值传递和引用

Java Pass By Value and reference

我有以下代码。但是,我无法理解它关于按值传递和引用传递的行为。

class Dog{
    String name;
    int x=100;
    
    Dog(String name){
        this.name = name;
    }
    
    public String getName(){
        return this.name;
    }
}
class Demo{
    public static void main( String[] args ){
        Dog aDog = new Dog("Tom");
        foo(aDog);

        if (aDog.getName().equals("Tom")) { //true
            System.out.println( "Java passes by value."+ aDog.getName());
            System.out.println( aDog.x);

        } else if (aDog.getName().equals("Taz")) {
            System.out.println( "Java passes by reference." );
        }
    }

    public static void foo(Dog d) {
        ++d.x;
        d = new Dog("Taz");
        ++d.x;
    }
}

这将提供输出为

Java passes by value.Tom

101

为什么输出是101?我期待输出 102.

你增加 x 一次,在给方法 ("Tom") 的狗上。

然后你创造了一只 狗,叫做 Taz。

然后您为第二只狗递增 x。不影响原来的,两只狗都是101

至于为什么调用方法仍然引用 "Tom",即使您将局部变量更改为指向 "Taz":那是因为局部变量就是:局部于它所在的地方用来。调用者不关心你以后用它做什么,它自己的变量仍然指向"Tom".

因为Java不支持按引用传递或"out parameters",被调用函数无法更改调用函数中变量的值。

但请注意,对象不存储在变量中。只有指向它们的指针存储在那里。实际对象实例本身位于共享位置(程序堆内存)。所以调用的方法确实可以改变对象。但是它不能将不同的对象分配给调用函数。


要点:使局部变量 final,尤其是方法参数。这样你就不能为两个不同的东西重用同一个变量,代码变得不那么混乱了。

你增加了 x 两次,但在不同的狗身上。看这段代码:

public static void foo(Dog d) {
    ++d.x;
    d = new Dog("Taz");
    ++d.x;
}

最初,d指的是名字为Tom的狗,x=100。这不是原始对象的副本——它是对同一对象的引用。 Java 按值传递引用,这与按值传递对象 按引用传递对象不同。重要的是要了解 mainaDog 的值不是 Dog 对象 - 它是对 Dog 对象的引用。该引用的值按值传递给您的 foo 方法...因此 d 的初始值与 aDog 的值相同。 d 变量本身(而不是其值所指的对象)的进一步更改 不会 更改 aDog 变量。

所以,看看 foo 的其余部分:

  • ++d.x之后,d指的是名字为Tom的狗,与x=101.
  • d = new Dog("Taz")之后,d指的是一只狗,名字是Tazx=100.
  • ++d.x之后,d指的是名字为Taz的狗,与x=101.

调用代码只知道名字为 Tom 的狗,所以它打印出 Tom 101。

Java 始终是 按值传递:

主要():

Dog aDog = new Dog("Tom"); // {name="Tom", x=100}

foo():

++d.x;                     // {name="Tom", x=101}
d = new Dog("Taz");        // {name="Taz", x=100}
++d.x;                     // {name="Taz", x=101}

但是,您可以获得全新的副本,但您需要 return 它。

在 main() 中:更新此行

foo(aDog);

aDog = foo(aDog);

还将 foo() 更新为:

public static Dog foo(Dog d) {
  ...
  return d;
}

当您像这样使用 new 关键字时 d = new Dog("Taz");
你创建了一个新的 Dog,新的 Dog 叫做 Taz 并且有 x=101,所以你不会为同一个 Dog d.x 递增 2 次