关于方法:Java是”通过引用”还是”按值传递”?

Is Java “pass-by-reference” or “pass-by-value”?

我一直认为Java是通过引用传递的。

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

我想我不明白他们的区别。

解释是什么?


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

就像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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"main中的值aDog在函数foo中没有改变,因为对象引用是通过值传递的。如果是通过引用传递的,那么main中的aDog.getName()会在调用foo之后返回"Fifi"

同样地:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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中的所有东西都是通过值传递的。在爪哇,没有所谓的"通行证"。

理解这一点的关键是

1
Dog myDog;

不是狗,而是指向狗的指针。

这意味着,当你

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

您实际上是将创建的Dog对象的地址传递给foo方法。

(我说的基本上是因为Java指针不是直接地址,但是用这种方法最容易想到它们)

假设Dog对象位于内存地址42。这意味着我们将42传递给方法。

如果方法定义为

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

让我们看看发生了什么。

  • 参数someDog设置为值42。
  • 在"AAA"线上
    • someDogDog指向(地址42的Dog对象)
    • 要求Dog(地址42的那个)把他的名字改成Max
  • 在线"BBB"
    • 创建新的Dog。假设他在74号地址
    • 我们将参数someDog指定为74。
  • 在线"CCC"
    • 一些狗被跟踪到Dog,它指向(地址74处的Dog对象)
    • 要求Dog(地址74)改为Rowlf
  • 然后,我们回来

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

myDog有变化吗?

有钥匙。

记住,myDog是一个指针,而不是实际的Dog,答案是:myDog仍然有值42;它仍然指向原始的Dog(但请注意,由于行"a a a",它的名称现在是"max"—仍然是同一只狗;myDog的值没有改变。)

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

Java可以像C一样工作,可以分配一个指针,将指针传递给一个方法,跟随方法中的指针,并改变指向的数据。但是,您不能更改指针指向的位置。

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

如果Java具有通过引用语义,则上面定义的EDCOX1 OR 1方法将在EDOCX1×13"Ed"被指派给在线BBB的情况下发生改变。

将引用参数视为传入变量的别名。当分配别名时,传入的变量也是如此。


Java总是不passes××arguments参考值。 >

让我解释这一年的实例: >

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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");
     }
}

我会解释这步骤: >

declaring命名的Foo参考f型和新的IT assign面向属性"f"Foo带型。 >

1
Foo f = new Foo("f");

enter image description here >

  • 从法学院Foo端,参考型与adeclared name is initially null和它的分配。 >

    1
    public static void changeReference(Foo a)
  • enter image description here >

  • changeReference呼叫的方法,将指定的对象a参考,是我过去一年的争论。 >

    1
    changeReference(f);