提问者:小点点

Java是“按引用传递”还是“按值传递”?


我一直以为Java是个过来人。

然而,我看到一些博客文章(例如这个博客)声称它不是。

我想我不明白他们在做什么。

有什么解释呢?


共3个答案

匿名用户

Java总是被忽略。 不幸的是,当我们传递一个对象的值时,我们是在传递对它的引用。 这对初学者来说是很困惑的。

它是这样的:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    Dog oldDog = aDog;

    // we pass the object to foo
    foo(aDog);
    // aDog variable is still pointing to the "Max" dog when foo(...) returns
    aDog.getName().equals("Max"); // true
    aDog.getName().equals("Fifi"); // false
    aDog == oldDog; // true
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // change d inside of foo() to point to a new Dog instance "Fifi"
    d = new Dog("Fifi");
    d.getName().equals("Fifi"); // true
}

在上面的示例中,adog.getName()仍将返回“max”。 在函数foo中,main中的值adog不会因对象引用通过值传递而改变,函数中的dog“fifi”带有dog。 如果它是通过引用传递的,那么main中的adog.getName()将在调用foo之后返回“fifi”

同样:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    Dog oldDog = aDog;

    foo(aDog);
    // when foo(...) returns, the name of the dog has been changed to "Fifi"
    aDog.getName().equals("Fifi"); // true
    // but it is still the same dog:
    aDog == oldDog; // true
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // this changes the name of d to be "Fifi"
    d.setName("Fifi");
}

在上面的示例中,fifi是调用foo(aDog)之后的狗的名称,因为对象的名称是在foo(...)中设置的。 food执行的任何操作都是在ADOG上执行的,但不可能更改变量ADOG本身的值。

匿名用户

我刚注意到你引用了我的文章。

Java的规范说,Java的一切都是通过价值传递的。 Java不存在“过来人”的说法。

理解这一点的关键是

Dog myDog;

不是狗; 其实是指向狗的指针。

这意味着,当你

Dog myDog = new Dog("Rover");
foo(myDog);

实际上是将创建的doG对象的地址传递给foO方法。

(我之所以这么说,主要是因为Java指针不是直接地址,但这样想最容易。)

假设dog对象驻留在内存地址42。 这意味着我们将42传递给方法。

如果方法定义为

public void foo(Dog someDog) {
    someDog.setName("Max");     // AAA
    someDog = new Dog("Fifi");  // BBB
    someDog.setName("Rowlf");   // CCC
}

让我们看看发生了什么。

  • 参数someDog设置为值42
  • 在“AAA”行
    • someDoG后面跟着doG它指向的doG对象(地址42处的doG对象)
    • 那只(地址42的那只)被要求把它的名字改成Max
    • 将创建一个新的。 假设他在74
    • 我们将参数someDog分配给74
    • someDog后面跟着dog它指向的dog对象(地址74处的dog对象)
    • 那个(地址74处的那个)被要求把他的名字改成rowlf

    现在让我们思考一下方法之外会发生什么:

    MyDog是否更改?

    钥匙在那儿。

    请记住,mydog是一个指针,而不是一个实际的dog,答案是否定的。 mydog仍然具有值42; 它仍然指向最初的(但是注意,由于行“aaa”,它的名称现在是“max”--仍然是同一个狗;mydog的值没有改变。)

    跟随地址并更改地址末尾的内容是完全有效的; 但是,这不会改变变量。

    Java的工作原理与C完全相同。您可以分配一个指针,将指针传递给一个方法,跟随方法中的指针并更改所指向的数据。 但是,您不能更改该指针指向的位置。

    在C++,Ada,Pascal和其他支持按引用传递的语言中,您实际上可以更改传递的变量。

    如果Java具有按引用传递语义,那么我们上面定义的foo方法在BBB行分配someDog时,将会改变myDog所指向的位置。

    将引用参数视为传入变量的别名。 当该别名被赋值时,传入的变量也被赋值。

匿名用户

Java总是按值传递参数,而不是按引用传递参数。

让我通过一个例子来说明这一点:

public class Main {

     public static void main(String[] args) {
          Foo f = new Foo("f");
          changeReference(f); // It won't change the reference!
          modifyReference(f); // It will modify the object that the reference variable "f" refers to!
     }

     public static void changeReference(Foo a) {
          Foo b = new Foo("b");
          a = b;
     }

     public static void modifyReference(Foo c) {
          c.setAttribute("c");
     }

}

我将分步骤解释这一点:

>

  • 声明一个名为f,类型为foo的引用,并为其分配一个具有属性“f”的类型为foo的新对象。

    Foo f = new Foo("f");
    

    在方法端,声明了一个类型为foo,名称为a的引用,并且最初为它分配了null

    public static void changeReference(Foo a)
    

    当您调用方法ChangeReference时,引用a将被分配作为参数传递的对象。

    changeReference(f);
    

    声明一个类型为foo的名为b的引用,并为它分配一个类型为foo的新对象,该对象带有一个属性“b”

    Foo b = new Foo("b");
    

    a=b对其属性为“b”的对象的引用a而不是f进行新赋值。

    当您调用ModifyReference(Foo c)方法时,将创建一个引用c,并将其分配给具有属性“f”的对象。

    C.SetAttribute(“C”);将更改引用C指向它的对象的属性,以及引用F指向它的对象的属性。

    我希望您现在明白了在Java中传递对象作为参数是如何工作的:)