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

问题

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

不过,我见过一些博客文章(例如this blog),声称它不是。

我不理解它们的区别,能解释一下吗?


#1 热门回答(4651 赞)

Java永远是传递值.不幸的是,他们决定将对象的位置称为“引用”。当我们传递一个对象的值时,我们将它传递给它。这对初学者来说很混乱。

它是这样的:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    // 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, java passes by value
    aDog.getName().equals("Fifi"); // false 
}

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中不会被'Dog'“Fifi”改变。如果通过引用传递,那么main中的aDog.getName()在调用foo后会返回'Fifi'`。

同样:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    foo(aDog);
    // when foo(...) returns, the name of the dog has been changed to "Fifi"
    aDog.getName().equals("Fifi"); // 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本身上执行的(除非d被改变为指向不同的Dog实例,比如d = new狗(“拳击手”))。


#2 热门回答(2601 赞)

我只是注意到你引用了my article

Java Spec说,Java中的所有东西都是按值传递的。 Java中没有“传递参考”这样的东西。

理解这一点的关键是类似的东西

Dog myDog;

isnota狗;它实际上是一只狗。

这意味着什么,当你有

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”行,有人跟踪到它指向的Dog(地址42处的Dog对象),Dog(地址为42的那个)被要求将他的名字改为Max
  • 在“BBB”行创建一个新的狗。假设他在地址74,我们将参数someDog分配给74
  • 在“CCC”线上,有人跟踪它指向的Dog(地址为74的Dog对象),Dog(地址为74的那个)被要求将他的名字改为Rowlf
  • 然后,我们回来

现在让我们考虑一下在该方法之外发生的事情:
myDog改变了吗?
有关键。

记住myDog是apointer,而不是实际的Dog,答案是NO。 myDog仍然有值42;它仍然指向原来的(但请注意,因为行“AAA”,它的名字现在是“最大” - 仍然是狗;myDog的值没有改变。)

这是完全有效的tofollowan地址,并改变它的结尾;但不会改变变量。

Java的工作方式与C完全一样。您可以指定一个指针,将指针传递给方法,跟随方法中的指针并更改指向的数据。但是,您无法更改指针指向的位置。

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

如果Java具有传引用语义,那么我们上面定义的foo方法会在'myDog指向它在BBB行分配'someDog时指向的地方。

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


#3 热门回答(1374 赞)

Java总是通过值传递参数而不是通过引用。

让我通过example解释这一点:

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”);
  • 从方法的角度来说,名为a的Foo类型的引用被声明,并且它最初被赋值为null。 public static void changeReference(Foo a)
  • 在调用方法changeReference时,引用a将被分配给作为参数传递的对象。 changeReference(F);
  • 声明一个名为b的Foo类型的引用,并将其赋值给一个具有属性“b”的Foo类型的新对象。 Foo b =新Foo(“b”);
  • a = b将引用a NOT f重新分配给其属性为“b”的对象。
  • 在调用modifyReference(Foo c)方法时,会创建一个引用c并将其分配给具有属性“f”的对象。
  • c.setAttribute(“c”);将改变引用c点的对象的属性,并且引用f指向它的是同一个对象。

我希望你现在能理解如何将对象作为参数传递给Java。