java是值传递还是引用传递

java中到底是值传递还是引用传递呢?自开始编程就一直在讨论这个问题,从stackoverflow中查看热门问题,排名第3,所以决定翻译一下 原文链接,我个人比较倾向于第2位投票数的答案,所以翻译的也是他的答案,并给出了这位作者的网址。问题很简单,也没有什么背景需要介绍,就不翻译了。

1 (投票数第2的答案)

先贴下一段代码:

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");
foo(aDog);
if (aDog.getName().equals("Max")) { //true
System.out.println( "Java passes by value." );
} else if (aDog.getName().equals("Fifi")) {
System.out.println( "Java passes by reference." );
}
}
public static void foo(Dog d) {
d.getName().equals("Max"); // true
d = new Dog("Fifi");
d.getName().equals("Fifi"); // true
}

我刚刚意识到你有引用我的文章(这里值投票第1的答案,所以我才会选择这位作者的答案)

Java Spec说java中的一切都是通过值进行传递,在java中没有引用这一说。
理解这句话的关键是例如下面这段代码:
`Dog myDog;`
myDog并不是实际的对象,而是一个指向一只狗的指针(可以把狗理解为一栋房子,myDog其实是房子的钥匙,你每天上班不能背着个房子上班吧,但是你又要表明你有一栋房子,所以每天拿着钥匙就可以了,钥匙就是对房子的引用。),这就是说每当你写了下面的代码:

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

本质上你向foo方法传递的是指向Dog实际存储地址的一个引用。(我之所以说本质是因为java里面的指针并不是直接的地址,但是这样说更容易理解)。

假设Dog这一条狗住在内存42号,这意味着向foo传递的是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行处:
    1、someDog指向的还是42处的那只狗
    2、把住在42的那条狗的名字修改为”Max”
  • 在BBB行处:
    1、创建了一条新的狗,名字叫”Fifi”,假设住在内存74号。
    2、someDog被重新指向了74号地址。
  • 在CCC行处:
    1、现在someDog指向的是74号的狗。
    2、把住在74的那条狗的名字修改为”Rowlf”

调用了这个方法之后,让我们思考一下,最开始的那条狗:myDog,改变了吗?这是关键。其实没变,myDog的地址还是42号,它还是原来的那条狗,但是在AAA行,这条狗名字被修改为”Max”,但是狗还是原来的那条狗,没变。最后根据地址它有效的改变了狗的名字,但是并不改变狗的地址,还是42。

java的原理和C有点像,你可以给指针赋值,把指针传递给一个方法,根据指针指向的地址,改变这个地址上的元素的值,但是,没有改变指针所指向的地址。

在C++、 Ada、Pascal和一些其它的编程语言中,是有引用传递的,你可以真的改变传递到方法的变量所指向的值。

如果非要咬文嚼字的说java有引用传递,foo方法已经在BBB行把myDog指向的地址给修改为新的值了(实际上并没有改变,所以java是值传递)。引用的参数可以想象为参数的别名。

2 (投票数第3的答案)

投票数第3的答案也很好,但都是图示,大家自己去看原文链接

转载请标明出处:”http://hushengdong.com/2016/12/15/java是值传递还是引用传递/