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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/40480/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 07:31:14  来源:igfitidea点击:

Is Java "pass-by-reference" or "pass-by-value"?

javamethodsparameter-passingpass-by-referencepass-by-value

提问by user4315

I always thought Java was pass-by-reference.

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

However, I've seen a couple of blog posts (for example, this blog) that claim that it isn't.

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

I don't think I understand the distinction they're making.

我不认为我理解他们所做的区分。

What is the explanation?

解释是什么?

回答by SCdF

Java is always pass by value, with no exceptions, ever.

Java 总是按值传递,从来没有例外。

So how is it that anyone can be at all confused by this, and believe that Java is pass by reference, or think they have an example of Java acting as pass by reference? The key point is that Java neverprovides direct access to the values of objects themselves, in anycircumstances. The only access to objects is through a referenceto that object. Because Java objects are alwaysaccessed through a reference, rather than directly, it is common to talk about fields and variables and method argumentsas being objects, when pedantically they are only references to objects. The confusion stems from this (strictly speaking, incorrect) change in nomenclature.

那么怎么会有人对此感到困惑,并相信 Java 是通过引用传递的,或者认为他们有一个 Java 作为引用传递的示例?关键是在任何情况下,Java从不提供对对象本身值的直接访问。对对象的唯一访问是通过对该对象的引用。因为 Java 对象总是通过引用而不是直接访问,所以通常将字段和变量以及方法参数称为对象,而迂腐地它们只是对对象的引用混淆源于命名法的这种(严格来说,不正确)变化。

So, when calling a method

所以,当调用一个方法时

  • For primitive arguments (int, long, etc.), the pass by value is the actual valueof the primitive (for example, 3).
  • For objects, the pass by value is the value of the reference to the object.
  • 对于基元参数(intlong等),传递值是基元的实际值(例如 3)。
  • 对于对象,传值是对象引用的值。

So if you have doSomething(foo)and public void doSomething(Foo foo) { .. }the two Foos have copied referencesthat point to the same objects.

因此,如果您有doSomething(foo)并且public void doSomething(Foo foo) { .. }两个 Foo 复制了指向相同对象的引用

Naturally, passing by value a reference to an object looks very much like (and is indistinguishable in practice from) passing an object by reference.

自然地,通过值传递对对象的引用看起来非常像(并且在实践中与)通过引用传递对象。

回答by ScArcher2

Java passes references by value.

Java 按值传递引用。

So you can't change the reference that gets passed in.

所以你不能改变传入的引用。

回答by Hank Gay

Basically, reassigning Object parameters doesn't affect the argument, e.g.,

基本上,重新分配 Object 参数不会影响参数,例如,

private void foo(Object bar) {
    bar = null;
}

public static void main(String[] args) {
    String baz = "Hah!";
    foo(baz);
    System.out.println(baz);
}

will print out "Hah!"instead of null. The reason this works is because baris a copy of the value of baz, which is just a reference to "Hah!". If it were the actual reference itself, then foowould have redefined bazto null.

将打印出来"Hah!"而不是null. 这样做的原因是因为bar是 的值的副本baz,它只是对 的引用"Hah!"。如果它是实际引用本身,则将foo重新定义baznull.

回答by John Channing

Java passes references to objects by value.

Java 按值传递对对象的引用。

回答by erlando

Java is always pass-by-value. Unfortunately, when we pass the value of an object, we are passing the referenceto it. This is confusing to beginners.

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

It goes like this:

它是这样的:

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
}

In the example above aDog.getName()will still return "Max". The value aDogwithin mainis not changed in the function foowith the Dog"Fifi"as the object reference is passed by value. If it were passed by reference, then the aDog.getName()in mainwould return "Fifi"after the call to foo.

在上面的例子中aDog.getName()仍然会返回"Max". 值aDogmain未在功能改变fooDog"Fifi"作为对象基准由值来传递。如果它是通过引用传递的,那么aDog.getName()inmain"Fifi"在调用 之后返回foo

Likewise:

同样地:

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");
}

In the above example, Fifiis the dog's name after call to foo(aDog)because the object's name was set inside of foo(...). Any operations that fooperforms on dare such that, for all practical purposes, they are performed on aDog, but it is notpossible to change the value of the variable aDogitself.

在上面的例子中,Fifi是调用后的狗的名字,foo(aDog)因为对象的名字被设置在foo(...). 任何操作是foo执行上d是这样的,对于所有的实际目的,它们被执行的aDog,但它是不是可以改变变量的值aDog本身。

回答by Paul de Vrieze

To make a long story short, Javaobjects have some very peculiar properties.

长话短说,Java对象有一些非常奇特的属性。

In general, Java has primitive types (int, bool, char, double, etc) that are passed directly by value. Then Java has objects (everything that derives from java.lang.Object). Objects are actually always handled through a reference (a reference being a pointer that you can't touch). That means that in effect, objects are passed by reference, as the references are normally not interesting. It does however mean that you cannot change which object is pointed to as the reference itself is passed by value.

一般来说,Java有原始类型(intboolchardouble,等等)由值直接传递。然后 Java 有对象(从 派生的所有东西java.lang.Object)。对象实际上总是通过引用处理(引用是您无法触摸的指针)。这意味着实际上,对象是通过引用传递的,因为引用通常并不有趣。但是,这确实意味着您无法更改指向的对象,因为引用本身是按值传递的。

Does this sound strange and confusing? Let's consider how C implements pass by reference and pass by value. In C, the default convention is pass by value. void foo(int x)passes an int by value. void foo(int *x)is a function that does not want an int a, but a pointer to an int: foo(&a). One would use this with the &operator to pass a variable address.

这听起来奇怪和混乱吗?让我们考虑一下 C 是如何实现按引用传递和按值传递的。在 C 中,默认约定是按值传递。void foo(int x)按值传递一个 int 值。void foo(int *x)是一个不需要 an 的函数int a,而是一个指向 int: 的指针foo(&a)。可以将其与&运算符一起使用来传递变量地址。

Take this to C++, and we have references. References are basically (in this context) syntactic sugar that hide the pointer part of the equation: void foo(int &x)is called by foo(a), where the compiler itself knows that it is a reference and the address of the non-reference ashould be passed. In Java, all variables referring to objects are actually of reference type, in effect forcing call by reference for most intends and purposes without the fine grained control (and complexity) afforded by, for example, C++.

把这个带到 C++,我们有参考。引用基本上是(在这种情况下)隐藏等式的指针部分的语法糖:void foo(int &x)被调用foo(a),编译器本身知道它是一个引用,并且a应该传递非引用的地址。在 Java 中,所有引用对象的变量实际上都是引用类型,实际上,大多数意图和目的都强制按引用调用,而没有 C++ 提供的细粒度控制(和复杂性)。

回答by shsteimer

The distinction, or perhaps just the way I remember as I used to be under the same impression as the original poster is this: Java is always pass by value. All objects( in Java, anything except for primitives) in Java are references. These references are passed by value.

区别,或者也许只是我记得的方式,因为我曾经与原始海报有相同的印象:Java 总是按值传递。Java 中的所有对象(在 Java 中,除了原语之外的任何对象)都是引用。这些引用是按值传递的。

回答by pek

As many people mentioned it before, Java is always pass-by-value

正如很多人之前提到的,Java 总是按值传递

Here is another example that will help you understand the difference (the classic swap example):

这是另一个可以帮助您理解差异的示例经典交换示例):

public class Test {
  public static void main(String[] args) {
    Integer a = new Integer(2);
    Integer b = new Integer(3);
    System.out.println("Before: a = " + a + ", b = " + b);
    swap(a,b);
    System.out.println("After: a = " + a + ", b = " + b);
  }

  public static swap(Integer iA, Integer iB) {
    Integer tmp = iA;
    iA = iB;
    iB = tmp;
  }
}

Prints:

印刷:

Before: a = 2, b = 3
After: a = 2, b = 3

之前:a = 2,b = 3
之后:a = 2,b = 3

This happens because iA and iB are new local reference variables that have the same value of the passed references (they point to a and b respectively). So, trying to change the references of iA or iB will only change in the local scope and not outside of this method.

发生这种情况是因为 iA 和 iB 是新的局部引用变量,它们与传递的引用具有相同的值(它们分别指向 a 和 b)。因此,尝试更改 iA 或 iB 的引用只会在本地范围内更改,而不会在此方法之外更改。

回答by SWD

I always think of it as "pass by copy". It is a copy of the value be it primitive or reference. If it is a primitive it is a copy of the bits that are the value and if it is an Object it is a copy of the reference.

我一直认为它是“通过副本”。它是值的副本,无论是原始值还是引用值。如果它是一个原语,它是作为值的位的副本,如果它是一个对象,它是引用的副本。

public class PassByCopy{
    public static void changeName(Dog d){
        d.name = "Fido";
    }
    public static void main(String[] args){
        Dog d = new Dog("Maxx");
        System.out.println("name= "+ d.name);
        changeName(d);
        System.out.println("name= "+ d.name);
    }
}
class Dog{
    public String name;
    public Dog(String s){
        this.name = s;
    }
}

output of java PassByCopy:

java PassByCopy 的输出:

name= Maxx
name= Fido

名称= Maxx
名称= Fido

Primitive wrapper classes and Strings are immutable so any example using those types will not work the same as other types/objects.

原始包装类和字符串是不可变的,因此使用这些类型的任何示例都不会与其他类型/对象一样工作。

回答by sven

I have created a thread devoted to these kind of questions for anyprogramming languages here.

我在此处针对任何编程语言创建了一个专门讨论此类问题的线程。

Java is also mentioned. Here is the short summary:

还提到了 Java。以下是简短摘要:

  • Java passes it parameters by value
  • "by value" is the only way in java to pass a parameter to a method
  • using methods from the object given as parameter will alter the object as the references point to the original objects. (if that method itself alters some values)
  • Java按值传递参数
  • “按值”是 Java 中将参数传递给方法的唯一方法
  • 使用作为参数给出的对象的方法将改变对象,因为引用指向原始对象。(如果该方法本身改变了一些值)