按引用传递对象 vs. 按值传递引用

8
我在这里读到了这个评论:Java中如何按引用传递字符串?

是的,这是一个误解。这是一个巨大而广泛的误解。它会导致我讨厌的面试问题:(“Java如何传递参数”)。我讨厌它,因为大约一半的面试官似乎想要错误的答案(“基本类型按值传递,对象按引用传递”)。正确的答案需要更长时间给出,并且似乎会使其中一些人感到困惑。他们不会被说服:我发誓我因为大学时期的CSMajor型筛选器听到了这个误解并将其视为福音而挂掉了技术屏幕。呸。- CPerkins 2009年8月13日下午2:34

有人可以用新手程序员能理解的术语解释一下以下说法之间的区别吗:

“在Java中,原始数据类型是按值传递,对象是按引用传递。”

和:

“在Java中,没有什么是按引用传递的,都是按引用传递的。”?

这两个说法在某种意义上都是正确的吗?我不想引起愤怒,但这听起来像一个非常重要的概念,而且我仍然不完全理解。


2
可能是重复问题:https://dev59.com/EXVD5IYBdhLWcg3wQJOT - assylias
2
在我看来,知道术语并没有太大的帮助。知道实际发生了什么才是重要的。几年前,我看到了一个非常方便的问题,涉及到StringBuffers ,这真正确定了程序员是否知道规则而无需知道术语。但愿我当时能够参考它(或者复制一份,我肯定要复制参考)。 - Tom Hawtin - tackline
2
投票关闭。与https://dev59.com/EXVD5IYBdhLWcg3wQJOT完全重复。 - dty
2
我知道这些问题的主体相似,但我也想知道为什么所有的教科书都提出了上面的说法,而所有有经验的程序员都提出了下面的说法……是否只是一个上下文的问题,两种说法都正确,还是教科书完全错了。 - Steve the Maker
2
@StevetheMaker,他们至少在一个方面是正确的,那就是对象不是通过复制对象来传递的。我相信大多数作者都会详细阐述为什么。只要他们正确定义了任何术语,他们就可以使用它。 - Johan Sjöberg
显示剩余5条评论
4个回答

12

我认为这个误解在于一个变量本身不能包含一个对象。如果你理解了这一点,那么很显然变量只能包含对对象(或原始值)的引用。从那里到意识到引用是按值传递的(就像原始值一样)的转变非常小。

有一个非常简单的测试可以用来确定一种语言是否支持按引用传递。问问自己是否可以在该语言中编写一个交换函数,即像下面这样的函数:

x == A, y == B

swap(x, y);

x == B, y == A
作为Java程序员,你很快就会意识到,你无法在Java中实现这一点,因此,你(正确地)得出结论:Java没有按引用传递。
回到你的原话:
- 在Java中,基本类型按值传递,对象按引用传递。
这是错误的。我认为你只能传递一个包含在变量中的东西,正如我上面所说,一个变量不能包含一个对象,因此在Java中根本不能传递对象。
- 在Java中,没有任何东西是按引用传递的,引用都是按值传递的。
这是正确的。

+1 因为想出一个简单、聪明且出人意料的例子。我很喜欢它。 - Platinum Azure

7

如图所示,这样的事情总是更容易理解。考虑以下两个变量,一个是基本类型,另一个是引用类型:

    int i    = 5;
    String s = "test";

在内存中,有一个名为i的条目,看起来像这样:

  i
-----
| 5 |
-----

同样地,内存中也有一个 s 的条目,但它引用堆上的一个位置,因为 s 是一个引用类型变量,而对象是存储在堆上的。
                            ----------- 
 s                 |------->|  "test" |
-----              |        |---------|
| --|--------------|        |         |
-----                       |         |
                            |         |
                            |---------|

因此,s的值是指向堆上的String对象的引用,所以如果将s传递给一个方法:

printString(s);

public void printString(String arg)
{
   System.out.println(arg);
}

实际上被复制到arg参数中的值是指向堆上s的引用:
                            ----------- 
 s                 |------->|  "test" |<-----|
-----              |        |---------|      |
| --|--------------|        |         |      |
-----                       |         |      |
                            |         |      |
                            |---------|      |
 arg                                         |
-----                                        |
| --|----------------------------------------- 
-----

希望这可以帮助您。

6

好问题。考虑以下示例:

void foo(Object obj) {
  obj = new Foo(); 
}

Object o = new Bar();
foo(o);
// is o Foo or Bar?
  • 如果使用引用传递,在调用foo之后o的引用可能会发生改变。
  • 如果使用按引用传递-by-value,在调用foo之后o的引用不会发生改变。

2
两个语句是互斥的,第一个语句是错误的。在Java中,即使是对象的引用也是按值传递的。混淆的原因在于当按值传递对象时,传递的是其引用的副本,如果该对象是可变的并在方法内部被修改,则更改将在调用它的外部点中可见。
让我举几个例子来解释。在这种情况下,x是按值传递的原始类型:
void m(int x) {
    x = 10;
}
// in some other place, the above method gets called
x = 5;
m(x);
// in here, x's value is still 5

在这种情况下,x 是一个对象类型,并且它的引用的副本按值传递:
void m(ArrayList<Integer> x) {
    x.add(10);
}
// in some other place, the above method gets called
ArrayList<Integer> x = new ArrayList<Integer>();
x.add(5);
m(x);
// in here, x contains [5, 10]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接