Java是“旁路”还是“按值”?

时间:2021-04-13 10:13:17

I always thought Java was pass-by-reference, however I've seen a couple of blog posts (For example, this blog) that claim it isn't. I don't think I understand the distinction they're making.

我一直认为Java是一个附带引用,但是我看到过一些博客文章(例如,这个博客)声称它不是。我不理解他们的区别。

What is the explanation?

解释是什么?

79 个解决方案

#1


4482  

Java is always pass-by-value. Unfortunately, they decided to call the location of an object a "reference". When we pass the value of an object, we are passing the reference to it. This is confusing to beginners.

Java总是按值传递。不幸的是,他们决定将对象的位置称为“引用”。当我们传递一个对象的值时,我们将引用它。这让初学者很困惑。

It goes like this:

它是这样的:

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
}

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

在上面的例子中,aDog.getName()仍然返回“Max”。main中的值aDog在函数foo中没有改变,而Dog“Fifi”作为对象引用是通过值传递的。如果它是通过引用传递的,那么main中的aDog.getName()将在调用foo之后返回“Fifi”。

Likewise:

同样的:

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

In the above example, Fifi is the dog's name after call to foo(aDog) because the object's name was set inside of foo(...). Any operations that foo performs on d are such that, for all practical purposes, they are performed on aDog itself (except when d is changed to point to a different Dog instance like d = new Dog("Boxer")).

在上面的例子中,Fifi是在调用foo(aDog)之后的狗的名字,因为对象的名称是在foo(…)中设置的。foo在d上执行的任何操作都是这样的,出于所有实际目的,它们都是在aDog本身上执行的(除了d被更改为指向一个不同的Dog实例,比如d = new Dog(“Boxer”))。

#2


2534  

I just noticed you referenced my article.

我刚注意到你引用了我的文章。

The Java Spec says that everything in Java is pass-by-value. There is no such thing as "pass-by-reference" in Java.

Java规范说,Java中的一切都是按值传递的。在Java中没有所谓的“附带引用”。

The key to understanding this is that something like

理解这一点的关键是。

Dog myDog;

is not a Dog; it's actually a pointer to a Dog.

不是一条狗;它实际上是一个指向狗的指针。

What that means, is when you have

这意味着什么,当你拥有!

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

you're essentially passing the address of the created Dog object to the foo method.

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

(I say essentially because Java pointers aren't direct addresses, but it's easiest to think of them that way)

(我之所以说,本质上是因为Java指针不是直接地址,但最容易想到的是它们)

Suppose the Dog object resides at memory address 42. This means we pass 42 to the method.

假设Dog对象驻留在内存地址42中。这意味着我们把42传递给这个方法。

if the Method were defined as

如果方法被定义为。

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

let's look at what's happening.

让我们看看发生了什么。

  • the parameter someDog is set to the value 42
  • someDog的参数设置为42。
  • at line "AAA"
    • someDog is followed to the Dog it points to (the Dog object at address 42)
    • 有些狗被跟踪到它指向的狗(狗的对象在地址42)
    • that Dog (the one at address 42) is asked to change his name to Max
    • 那只狗(地址是42)被要求把他的名字改成马克斯。
  • 在“AAA”级的狗狗被跟踪到它指向的狗(地址是42)那条狗(地址为42)被要求将他的名字改为马克斯。
  • at line "BBB"
    • a new Dog is created. Let's say he's at address 74
    • 一条新狗诞生了。假设他在地址74。
    • we assign the parameter someDog to 74
    • 我们将参数someDog指定为74。
  • 在“BBB”线上,一条新狗诞生了。假设他在地址74,我们将参数someDog分配给74。
  • at line "CCC"
    • someDog is followed to the Dog it points to (the Dog object at address 74)
    • 有些狗被跟踪到它指向的狗(狗的对象在地址74)
    • that Dog (the one at address 74) is asked to change his name to Rowlf
    • 那只狗(地址为74)被要求把他的名字改为Rowlf。
  • 在“CCC”这条狗狗的后面,它指向狗狗(地址为74)那条狗(地址为74)被要求将他的名字改为Rowlf。
  • then, we return
  • 然后,我们返回

Now let's think about what happens outside the method:

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

Did myDog change?

myDog改变吗?

There's the key.

的关键。

Keeping in mind that myDog is a pointer, and not an actual Dog, the answer is NO. myDog still has the value 42; it's still pointing to the original Dog (but note that because of line "AAA", its name is now "Max" - still the same Dog; myDog's value has not changed.)

记住,我的狗是一个指针,而不是一只真正的狗,答案是否定的。myDog仍然有值42;它仍然指向原来的狗(但请注意,因为“AAA”,它的名字现在是“Max”——仍然是同一只狗;myDog的价值没有改变。

It's perfectly valid to follow an address and change what's at the end of it; that does not change the variable, however.

遵循一个地址并改变它的结尾是完全有效的;然而,这并不会改变变量。

Java works exactly like C. You can assign a pointer, pass the pointer to a method, follow the pointer in the method and change the data that was pointed to. However, you cannot change where that pointer points.

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

In C++, Ada, Pascal and other languages that support pass-by-reference, you can actually change the variable that was passed.

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

If Java had pass-by-reference semantics, the foo method we defined above would have changed where myDog was pointing when it assigned someDog on line BBB.

如果Java具有传递引用的语义,那么我们在上面定义的foo方法将会在myDog的位置上发生变化。

Think of reference parameters as being aliases for the variable passed in. When that alias is assigned, so is the variable that was passed in.

考虑引用参数作为传入变量的别名。当这个别名被分配时,传入的变量也是。

#3


1324  

Java always passes arguments by value NOT by reference.

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


Let me explain this through an 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");
     }
}

I will explain this in steps:

我将在以下步骤中解释:

  1. Declaring a reference named f of type Foo and assign it to a new object of type Foo with an attribute "f".

    声明一个名为Foo类型的引用,并将其分配给Foo类型的新对象,并带有一个属性“f”。

    Foo f = new Foo("f");
    

    Java是“旁路”还是“按值”?

  2. From the method side, a reference of type Foo with a name a is declared and it's initially assigned to null.

    从方法方面来说,声明了一个名称为a的Foo类型的引用,它最初被赋值为null。

    public static void changeReference(Foo a)
    

    Java是“旁路”还是“按值”?

  3. As you call the method changeReference, the reference a will be assigned to the object which is passed as an argument.

    当您调用方法changeReference时,引用a将被分配给作为参数传递的对象。

    changeReference(f);
    

    Java是“旁路”还是“按值”?

  4. Declaring a reference named b of type Foo and assign it to a new object of type Foo with an attribute "b".

    声明一个名为Foo类型的引用,并将其分配给一个带有“b”属性的Foo类型的新对象。

    Foo b = new Foo("b");
    

    Java是“旁路”还是“按值”?

  5. a = b is re-assigning the reference a NOT f to the object whose its attribute is "b".

    a = b重新分配对其属性为“b”的对象的引用a而非f。

    Java是“旁路”还是“按值”?


  6. As you call modifyReference(Foo c) method, a reference c is created and assigned to the object with attribute "f".

    当您调用modifyReference(Foo c)方法时,将创建一个引用c,并将其分配给具有属性“f”的对象。

    Java是“旁路”还是“按值”?

  7. c.setAttribute("c"); will change the attribute of the object that reference c points to it, and it's same object that reference f points to it.

    c.setAttribute(“c”);将会改变引用c指向它的对象的属性,它与引用f指向它的对象相同。

    Java是“旁路”还是“按值”?

I hope you understand now how passing objects as arguments works in Java :)

我希望您现在能够理解如何将对象作为参数在Java中工作:)

#4


631  

This will give you some insights of how Java really works to the point that in your next discussion about Java passing by reference or passing by value you'll just smile :-)

这将给您提供一些关于Java如何真正起作用的见解,在您下次讨论Java通过引用或传递值时,您只需微笑:-)

Step one please erase from your mind that word that starts with 'p' "_ _ _ _ _ _ _", especially if you come from other programming languages. Java and 'p' cannot be written in the same book, forum, or even txt.

第一步,请从你的脑海中抹去“p”开头的单词“_ _ _ _ _ _”,尤其是你来自其他编程语言的时候。Java和“p”不能写在同一本书、论坛甚至txt中。

Step two remember that when you pass an Object into a method you're passing the Object reference and not the Object itself.

第二步记住,当你将一个对象传递给一个方法时,你传递的是对象引用而不是对象本身。

  • Student: Master, does this mean that Java is pass-by-reference?
  • 学生:大师,这是否意味着Java是间接引用?
  • Master: Grasshopper, No.
  • 主:蚱蜢,没有。

Now think of what an Object's reference/variable does/is:

现在想想对象的引用/变量是什么

  1. A variable holds the bits that tell the JVM how to get to the referenced Object in memory (Heap).
  2. 一个变量包含告诉JVM如何到达内存中的引用对象的位(堆)。
  3. When passing arguments to a method you ARE NOT passing the reference variable, but a copy of the bits in the reference variable. Something like this: 3bad086a. 3bad086a represents a way to get to the passed object.
  4. 当将参数传递给方法时,您不会传递引用变量,而是引用变量中的位的副本。这样的:3 bad086a。3bad086a代表一种到达被传递对象的方法。
  5. So you're just passing 3bad086a that it's the value of the reference.
  6. 你只是通过了3bad086a这是参考值。
  7. You're passing the value of the reference and not the reference itself (and not the object).
  8. 您正在传递引用的值,而不是引用本身(而不是对象)。
  9. This value is actually COPIED and given to the method.
  10. 这个值实际上是被复制的,并被赋予了这个方法。

In the following (please don't try to compile/execute this...):

在下面(请不要试图编译/执行这个…):

1. Person person;
2. person = new Person("Tom");
3. changeName(person);
4.
5. //I didn't use Person person below as an argument to be nice
6. static void changeName(Person anotherReferenceToTheSamePersonObject) {
7.     anotherReferenceToTheSamePersonObject.setName("Jerry");
8. }

What happens?

会发生什么呢?

  • The variable person is created in line #1 and it's null at the beginning.
  • 变量person是在第1行创建的,在开始时是null。
  • A new Person Object is created in line #2, stored in memory, and the variable person is given the reference to the Person object. That is, its address. Let's say 3bad086a.
  • 在第2行中创建了一个新的Person对象,该对象存储在内存中,而变量Person将被指定给Person对象。也就是说,它的地址。假设3 bad086a。
  • The variable person holding the address of the Object is passed to the function in line #3.
  • 持有对象地址的变量被传递给第3行中的函数。
  • In line #4 you can listen to the sound of silence
  • 在第4行,你可以听到寂静的声音。
  • Check the comment on line #5
  • 检查第5行的注释。
  • A method local variable -anotherReferenceToTheSamePersonObject- is created and then comes the magic in line #6:
    • The variable/reference person is copied bit-by-bit and passed to anotherReferenceToTheSamePersonObject inside the function.
    • 变量/引用人被逐位复制,并传递给函数内部的其他引用。
    • No new instances of Person are created.
    • 没有创建新的Person实例。
    • Both "person" and "anotherReferenceToTheSamePersonObject" hold the same value of 3bad086a.
    • “person”和“anotherReferenceToTheSamePersonObject”都具有相同的3bad086a的值。
    • Don't try this but person==anotherReferenceToTheSamePersonObject would be true.
    • 不要尝试这个,但是人==另一个人是正确的。
    • Both variables have IDENTICAL COPIES of the reference and they both refer to the same Person Object, the SAME Object on the Heap and NOT A COPY.
    • 两个变量都具有相同的引用副本,它们都引用相同的Person对象,即堆上的相同对象,而不是副本。
  • 一个方法局部变量-另一个引用的personobject -是被创建的,然后在第6行中出现了魔术:变量/引用人被复制,并传递给在函数内部的另一个personobject。没有创建新的Person实例。“person”和“anotherReferenceToTheSamePersonObject”都具有相同的3bad086a的值。不要尝试这个,但是人==另一个人是正确的。两个变量都具有相同的引用副本,它们都引用相同的Person对象,即堆上的相同对象,而不是副本。

A picture is worth a thousand words:

一幅画胜过千言万语:

Java是“旁路”还是“按值”?

Note that the anotherReferenceToTheSamePersonObject arrows is directed towards the Object and not towards the variable person!

请注意,另一个对personobject箭头的引用指向对象,而不是指向变量person!

If you didn't get it then just trust me and remember that it's better to say that Java is pass by value. Well, pass by reference value. Oh well, even better is pass-by-copy-of-the-variable-value! ;)

如果你没有得到它,那就相信我,记住,最好说Java是通过值传递的。好,通过参考值传递。哦,好吧,甚至更好的是,一种变化的价值!,)

Now feel free to hate me but note that given this there is no difference between passing primitive data types and Objects when talking about method arguments.

现在可以随意地讨厌我,但是请注意,在讨论方法参数时,传递原始数据类型和对象之间没有区别。

You always pass a copy of the bits of the value of the reference!

您总是通过一个副本的值的引用!

  • If it's a primitive data type these bits will contain the value of the primitive data type itself.
  • 如果是原始数据类型,这些位将包含原始数据类型本身的值。
  • If it's an Object the bits will contain the value of the address that tells the JVM how to get to the Object.
  • 如果它是一个对象,位元将包含告诉JVM如何到达对象的地址的值。

Java is pass-by-value because inside a method you can modify the referenced Object as much as you want but no matter how hard you try you'll never be able to modify the passed variable that will keep referencing (not p _ _ _ _ _ _ _) the same Object no matter what!

Java是按值传递,因为在一个方法可以修改引用对象多达你想但无论你怎样努力尝试你永远无法修改将引用传递的变量(而不是p _ _ _ _ _ _ _)相同的对象,无论什么!


The changeName function above will never be able to modify the actual content (the bit values) of the passed reference. In other word changeName cannot make Person person refer to another Object.

上面的changeName函数永远无法修改传递的引用的实际内容(比特值)。换句话说,changeName不能使Person引用另一个对象。


Of course you can cut it short and just say that Java is pass-by-value!

当然,您可以简短地说,Java是按值的!

#5


544  

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 never provides direct access to the values of objects themselves, in any circumstances. The only access to objects is through a reference to that object. Because Java objects are always accessed through a reference, rather than directly, it is common to talk about fields and variables and method arguments as 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 value of the primitive (for example, 3).
  • 对于原始参数(int, long,等等),传递值是原语的实际值(例如,3)。
  • For objects, the pass by value is the value of the reference to the object.
  • 对于对象,通过值的传递是对对象的引用的值。

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

所以如果你有doSomething(foo)和public void doSomething(foo foo){。两个Foos复制指向相同对象的引用。

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

很自然地,引用一个对象的值看起来很像(实际上是无法区分的)通过引用传递一个对象。

#6


266  

Java passes references by value.

Java通过值传递引用。

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

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

#7


189  

I feel like arguing about "pass-by-reference vs pass-by-value" is not super-helpful.

我觉得争论“旁路和旁路”并不是很有帮助。

If you say, "Java is pass-by-whatever (reference/value)", in either case, you're not provide a complete answer. Here's some additional information that will hopefully aid in understanding what's happening in memory.

如果您说,“Java是按任意(引用/值)传递的”在这两种情况下,你都不能给出一个完整的答案。这里有一些额外的信息,希望有助于理解记忆中发生的事情。

Crash course on stack/heap before we get to the Java implementation: Values go on and off the stack in a nice orderly fashion, like a stack of plates at a cafeteria. Memory in the heap (also known as dynamic memory) is haphazard and disorganized. The JVM just finds space wherever it can, and frees it up as the variables that use it are no longer needed.

在我们进入Java实现之前,在堆栈/堆上的速成课程:值以一种有序的方式在堆栈上进行,就像在自助餐厅的一堆盘子一样。堆中的内存(也称为动态内存)是随意和无序的。JVM可以在任何地方找到空间,并将其释放为不再需要使用它的变量。

Okay. First off, local primitives go on the stack. So this code:

好吧。首先,本地原语进入堆栈。所以这段代码:

int x = 3;
float y = 101.1f;
boolean amIAwesome = true;

results in this:

结果:

Java是“旁路”还是“按值”?

When you declare and instantiate an object. The actual object goes on the heap. What goes on the stack? The address of the object on the heap. C++ programmers would call this a pointer, but some Java developers are against the word "pointer". Whatever. Just know that the address of the object goes on the stack.

当您声明并实例化一个对象时。实际对象在堆上。堆栈上有什么?堆上对象的地址。c++程序员将此称为指针,但是一些Java开发人员反对“指针”这个词。无论什么。只需知道对象的地址在堆栈上。

Like so:

像这样:

int problems = 99;
String name = "Jay-Z";

Java是“旁路”还是“按值”?

An array is an object, so it goes on the heap as well. And what about the objects in the array? They get their own heap space, and the address of each object goes inside the array.

数组是一个对象,所以它也在堆上。那么数组中的对象呢?它们有自己的堆空间,每个对象的地址都在数组中。

JButton[] marxBros = new JButton[3];
marxBros[0] = new JButton("Groucho");
marxBros[1] = new JButton("Zeppo");
marxBros[2] = new JButton("Harpo");

Java是“旁路”还是“按值”?

So, what gets passed in when you call a method? If you pass in an object, what you're actually passing in is the address of the object. Some might say the "value" of the address, and some say it's just a reference to the object. This is the genesis of the holy war between "reference" and "value" proponents. What you call it isn't as important as that you understand that what's getting passed in is the address to the object.

那么,当你调用一个方法时,会传入什么?如果传入一个对象,实际上传入的是对象的地址。有些人可能会说地址的“值”,有些人说这只是对对象的引用。这是“参考”和“价值”支持者之间的神圣战争的起源。你所称的它并不重要,因为你知道传递进来的是对象的地址。

private static void shout(String name){
    System.out.println("There goes " + name + "!");
}

public static void main(String[] args){
    String hisName = "John J. Jingleheimerschmitz";
    String myName = hisName;
    shout(myName);
}

One String gets created and space for it is allocated in the heap, and the address to the string is stored on the stack and given the identifier hisName, since the address of the second String is the same as the first, no new String is created and no new heap space is allocated, but a new identifier is created on the stack. Then we call shout(): a new stack frame is created and a new identifier, name is created and assigned the address of the already-existing String.

一个字符串创建在堆中分配空间,和地址的字符串存储在堆栈和考虑到标识符姓名,因为第二个字符串的地址是一样的,没有新的字符串创建和新分配的堆空间,但是一项新的标识符是在栈上创建。然后我们调用shout():创建一个新的堆栈帧,并创建一个新的标识符,名称被创建,并分配已经存在的字符串的地址。

Java是“旁路”还是“按值”?

So, value, reference? You say "potato".

因此,价值,参考?你说的“土豆”。

#8


157  

Just to show the contrast, compare the following C++ and Java snippets:

为了显示对比,请比较以下c++和Java代码片段:

In C++: Note: Bad code - memory leaks! But it demonstrates the point.

在c++中:注意:糟糕的代码-内存泄漏!但它证明了这一点。

void cppMethod(int val, int &ref, Dog obj, Dog &objRef, Dog *objPtr, Dog *&objPtrRef)
{
    val = 7; // Modifies the copy
    ref = 7; // Modifies the original variable
    obj.SetName("obj"); // Modifies the copy of Dog passed
    objRef.SetName("objRef"); // Modifies the original Dog passed
    objPtr->SetName("objPtr"); // Modifies the original Dog pointed to 
                               // by the copy of the pointer passed.
    objPtr = new Dog("newObjPtr");  // Modifies the copy of the pointer, 
                                   // leaving the original object alone.
    objPtrRef->SetName("objRefPtr"); // Modifies the original Dog pointed to 
                                    // by the original pointer passed. 
    objPtrRef = new Dog("newObjPtrRef"); // Modifies the original pointer passed
}

int main()
{
    int a = 0;
    int b = 0;
    Dog d0 = Dog("d0");
    Dog d1 = Dog("d1");
    Dog *d2 = new Dog("d2");
    Dog *d3 = new Dog("d3");
    cppMethod(a, b, d0, d1, d2, d3);
    // a is still set to 0
    // b is now set to 7
    // d0 still have name "d0"
    // d1 now has name "objRef"
    // d2 now has name "objPtr"
    // d3 now has name "newObjPtrRef"
}

In Java,

在Java中,

public static void javaMethod(int val, Dog objPtr)
{
   val = 7; // Modifies the copy
   objPtr.SetName("objPtr") // Modifies the original Dog pointed to 
                            // by the copy of the pointer passed.
   objPtr = new Dog("newObjPtr");  // Modifies the copy of the pointer, 
                                  // leaving the original object alone.
}

public static void main()
{
    int a = 0;
    Dog d0 = new Dog("d0");
    javaMethod(a, d0);
    // a is still set to 0
    // d0 now has name "objPtr"
}

Java only has the two types of passing: by value for built-in types, and by value of the pointer for object types.

Java只有两种类型的传递:通过对内置类型的值和对象类型指针的值。

#9


136  

Java passes references to objects by value.

Java通过值将引用传递给对象。

#10


132  

I can't believe that nobody mentioned Barbara Liskov yet. When she designed CLU in 1974, she ran into this same terminology problem, and she invented the term call by sharing (also known as call by object-sharing and call by object) for this specific case of "call by value where the value is a reference".

我真不敢相信没人提到过芭芭拉·利斯科夫。当她在1974年设计出了这个术语时,她遇到了同样的术语问题,她通过共享(也称为对象共享和对象调用)来创建术语调用,用于“值为引用的值的调用”。

#11


119  

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

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

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 bar is a copy of the value of baz, which is just a reference to "Hah!". If it were the actual reference itself, then foo would have redefined baz to null.

将打印出“哈!”而不是NULL。之所以这么做是因为bar是baz值的一个拷贝,这只是“哈!”的一个参考。如果它是实际的引用本身,那么foo就会将baz重新定义为null。

#12


87  

The crux of the matter is that the word reference in the expression "pass by reference" means something completely different from the usual meaning of the word reference in Java.

问题的关键在于,在“通过引用”这个表达式中的“引用”这个词的含义完全不同于Java中“引用”这个词的通常含义。

Usually in Java reference means a a reference to an object. But the technical terms pass by reference/value from programming language theory is talking about a reference to the memory cell holding the variable, which is something completely different.

通常在Java引用中,指的是对对象的引用。但是,从编程语言理论中引用/价值的技术术语是指对保存变量的内存单元的引用,这是完全不同的。

#13


71  

In java everything is reference, so when you have something like: Point pnt1 = new Point(0,0); Java does following:

在java中,一切都是引用,所以当你有类似的东西时:Point pnt1 = new Point(0,0);Java做如下:

  1. Creates new Point object
  2. 创建新的点对象
  3. Creates new Point reference and initialize that reference to point (refer to) on previously created Point object.
  4. 创建新的点引用并初始化该引用指向先前创建的Point对象。
  5. From here, through Point object life, you will access to that object through pnt1 reference. So we can say that in Java you manipulate object through its reference.
  6. 从这里,通过点对象生命,您将通过pnt1引用访问该对象。所以我们可以说,在Java中,你通过它的引用来操作对象。

Java是“旁路”还是“按值”?

Java doesn't pass method arguments by reference; it passes them by value. I will use example from this site:

Java不通过引用来传递方法参数;它通过值传递它们。我将用这个网站的例子:

public static void tricky(Point arg1, Point arg2) {
  arg1.x = 100;
  arg1.y = 100;
  Point temp = arg1;
  arg1 = arg2;
  arg2 = temp;
}
public static void main(String [] args) {
  Point pnt1 = new Point(0,0);
  Point pnt2 = new Point(0,0);
  System.out.println("X1: " + pnt1.x + " Y1: " +pnt1.y); 
  System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);
  System.out.println(" ");
  tricky(pnt1,pnt2);
  System.out.println("X1: " + pnt1.x + " Y1:" + pnt1.y); 
  System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);  
}

Flow of the program:

流的程序:

Point pnt1 = new Point(0,0);
Point pnt2 = new Point(0,0);

Creating two different Point object with two different reference associated. Java是“旁路”还是“按值”?

创建两个不同引用关联的不同的Point对象。

System.out.println("X1: " + pnt1.x + " Y1: " +pnt1.y); 
System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);
System.out.println(" ");

As expected output will be:

预期的产出将是:

X1: 0     Y1: 0
X2: 0     Y2: 0

On this line 'pass-by-value' goes into the play...

在这一行中,“pass-by-value”进入游戏……

tricky(pnt1,pnt2);           public void tricky(Point arg1, Point arg2);

References pnt1 and pnt2 are passed by value to the tricky method, which means that now yours references pnt1 and pnt2 have their copies named arg1 and arg2.So pnt1 and arg1 points to the same object. (Same for the pnt2 and arg2) Java是“旁路”还是“按值”?

引用pnt1和pnt2的值传递给复杂的方法,这意味着现在您的引用pnt1和pnt2有它们的副本,名为arg1和arg2。所以pnt1和arg1指向同一个对象。(pnt2和arg2相同)

In the tricky method:

在复杂的方法:

 arg1.x = 100;
 arg1.y = 100;

Java是“旁路”还是“按值”?

Next in the tricky method

接下来是棘手的方法。

Point temp = arg1;
arg1 = arg2;
arg2 = temp;

Here, you first create new temp Point reference which will point on same place like arg1 reference. Then you move reference arg1 to point to the same place like arg2 reference. Finally arg2 will point to the same place like temp.

在这里,您首先创建一个新的temp点引用,它将指向与arg1引用相同的位置。然后你移动参考arg1指向相同的地方,如arg2引用。最后,arg2将指向与temp相同的地方。

Java是“旁路”还是“按值”?

From here scope of tricky method is gone and you don't have access any more to the references: arg1, arg2, temp. But important note is that everything you do with these references when they are 'in life' will permanently affect object on which they are point to.

从这里开始,复杂的方法已经消失了,你再也无法访问参考文献:arg1, arg2, temp,但重要的是,当这些引用在“生命中”时,你所做的每件事都会永久地影响它们指向的对象。

So after executing method tricky, when you return to main, you have this situation: Java是“旁路”还是“按值”?

所以在执行完方法之后,当你回到main,你会遇到这样的情况:

So now, completely execution of program will be:

所以现在,程序的完全执行将是:

X1: 0         Y1: 0
X2: 0         Y2: 0
X1: 100       Y1: 100
X2: 0         Y2: 0

#14


61  

A reference is always a value when represented, no matter what language you use.

Getting an outside of the box view, let's look at Assembly or some low level memory management. At the CPU level a reference to anything immediately becomes a value if it gets written to memory or to one of the CPU registers. (That is why pointer is a good definition. It is a value, which has a purpose at the same time).

在框视图之外,让我们看看程序集或一些低级内存管理。在CPU级别,如果将其写入内存或CPU寄存器之一,则对任何内容的引用都会立即成为一个值。这就是为什么指针是一个很好的定义。它是一个有目的的价值。

Data in memory has a Location and at that location there is a value (byte,word, whatever). In Assembly we have a convenient solution to give a Name to certain Location (aka variable), but when compiling the code, the assembler simply replaces Name with the designated location just like your browser replaces domain names with IP addresses.

内存中的数据有一个位置,在那个位置有一个值(字节、单词或其他)。在程序集中,我们有一个方便的解决方案,可以将名称指定到某个位置(也就是变量),但是在编译代码时,汇编器只是将名称替换为指定的位置,就像您的浏览器用IP地址替换域名一样。

Down to the core it is technically impossible to pass a reference to anything in any language without representing it (when it immediately becomes a value).

从本质上说,从技术上讲,在任何语言中传递一个引用都是不可能的(当它立即变成一个值的时候)。

Lets say we have a variable Foo, its Location is at the 47th byte in memory and its Value is 5. We have another variable Ref2Foo which is at 223rd byte in memory, and its value will be 47. This Ref2Foo might be a technical variable, not explicitly created by the program. If you just look at 5 and 47 without any other information, you will see just two Values. If you use them as references then to reach to 5 we have to travel:

假设我们有一个变量Foo,它的位置在内存中的第47个字节,它的值是5。我们还有另一个变量Ref2Foo,它的内存是223字节,它的值是47。这个Ref2Foo可能是一个技术变量,不是由程序显式创建的。如果你只看5和47没有其他信息,你只会看到两个值。如果你把它们当作参考,那么我们就得去旅行。

(Name)[Location] -> [Value at the Location]
---------------------
(Ref2Foo)[223]  -> 47
(Foo)[47]       -> 5

This is how jump-tables work.

这就是跳跃表的工作原理。

If we want to call a method/function/procedure with Foo's value, there are a few possible way to pass the variable to the method, depending on the language and its several method invocation modes:

如果我们想用Foo的值调用方法/函数/过程,有几种可能的方法将变量传递给方法,这取决于语言和它的几种方法调用模式:

  1. 5 gets copied to one of the CPU registers (ie. EAX).
  2. 将5复制到一个CPU寄存器(ie)。EAX)。
  3. 5 gets PUSHd to the stack.
  4. 5被推到堆栈。
  5. 47 gets copied to one of the CPU registers
  6. 47被复制到一个CPU寄存器。
  7. 47 PUSHd to the stack.
  8. 47推到堆栈。
  9. 223 gets copied to one of the CPU registers.
  10. 223被复制到一个CPU寄存器。
  11. 223 gets PUSHd to the stack.
  12. 223被推到堆栈。

In every cases above a value - a copy of an existing value - has been created, it is now upto the receiving method to handle it. When you write "Foo" inside the method, it is either read out from EAX, or automatically dereferenced, or double dereferenced, the process depends on how the language works and/or what the type of Foo dictates. This is hidden from the developer until she circumvents the dereferencing process. So a reference is a value when represented, because a reference is a value that has to be processed (at language level).

在一个值之上的每一个案例中,都已经创建了一个现有值的副本,现在它是接收方法来处理它。当您在方法中写入“Foo”时,它不是从EAX中读出,或者是自动取消引用,或者是double dereferenced,这个过程取决于语言的工作方式和/或Foo命令的类型。这是隐藏于开发人员的,直到她绕过了取消引用的过程。因此,引用是在表示时的值,因为引用是必须处理的值(在语言级别)。

Now we have passed Foo to the method:

现在我们已经将Foo传递给方法:

  • in case 1. and 2. if you change Foo (Foo = 9) it only affects local scope as you have a copy of the Value. From inside the method we cannot even determine where in memory the original Foo was located.
  • 在案例1中。和2。如果您更改Foo (Foo = 9),它只会影响本地范围,因为您有一个值的副本。从这个方法中,我们甚至不能确定原来的Foo在内存中的位置。
  • in case 3. and 4. if you use default language constructs and change Foo (Foo = 11), it could change Foo globally (depends on the language, ie. Java or like Pascal's procedure findMin(x, y, z: integer;var m: integer);). However if the language allows you to circumvent the dereference process, you can change 47, say to 49. At that point Foo seems to have been changed if you read it, because you have changed the local pointer to it. And if you were to modify this Foo inside the method (Foo = 12) you will probably FUBAR the execution of the program (aka. segfault) because you will write to a different memory than expected, you can even modify an area that is destined to hold executable program and writing to it will modify running code (Foo is now not at 47). BUT Foo's value of 47 did not change globally, only the one inside the method, because 47 was also a copy to the method.
  • 在例3中。和4。如果使用默认语言构造和更改Foo (Foo = 11),它可以在全局上更改Foo(取决于语言)。Java或类似Pascal的程序findMin(x, y, z: integer;var m: integer);然而,如果该语言允许您绕过取消引用的过程,您可以更改47,比如说49。在这一点上,Foo似乎已经被改变了,如果你读它,因为你已经改变了它的本地指针。如果你在方法中修改这个Foo (Foo = 12),你很可能会把程序的执行(也就是,也就是你的程序)。由于您将写入与预期不同的内存,您甚至可以修改一个区域,该区域注定要保存可执行程序,并写入它将修改运行代码(Foo现在不是47)。但是Foo的值47并没有改变全局,只有方法中的一个,因为47也是方法的副本。
  • in case 5. and 6. if you modify 223 inside the method it creates the same mayhem as in 3. or 4. (a pointer, pointing to a now bad value, that is again used as a pointer) but this is still a local problem, as 223 was copied. However if you are able to dereference Ref2Foo (that is 223), reach to and modify the pointed value 47, say, to 49, it will affect Foo globally, because in this case the methods got a copy of 223 but the referenced 47 exists only once, and changing that to 49 will lead every Ref2Foo double-dereferencing to a wrong value.
  • 例5。和6。如果你在方法中修改223,它会造成和3一样的混乱。或4。(指针指向现在的坏值,再次被用作指针),但这仍然是一个本地问题,因为223被复制了。然而如果你能废弃Ref2Foo(即223),达到和修改指出价值47岁说,49,它将影响全球Foo,因为在这种情况下,方法得到一份223但引用47只存在一次,和改变49将每个Ref2Foo double-dereferencing错误的值。

Nitpicking on insignificant details, even languages that do pass-by-reference will pass values to functions, but those functions know that they have to use it for dereferencing purposes. This pass-the-reference-as-value is just hidden from the programmer because it is practically useless and the terminology is only pass-by-reference.

对于无关紧要的细节,即使是传递引用的语言也会将值传递给函数,但是这些函数知道它们必须使用它来进行解除引用。这个传递引用的值只是被程序员隐藏了,因为它实际上是无用的,而术语只是一个引用。

Strict pass-by-value is also useless, it would mean that a 100 Mbyte array should have to be copied every time we call a method with the array as argument, therefore Java cannot be stricly pass-by-value. Every language would pass a reference to this huge array (as a value) and either employs copy-on-write mechanism if that array can be changed locally inside the method or allows the method (as Java does) to modify the array globally (from the caller's view) and a few languages allows to modify the Value of the reference itself.

严格的传递值也是无用的,这意味着每次我们调用一个以数组为参数的方法时,必须复制100个Mbyte数组,因此Java不能严格地按值传递。每一种语言能通过引用这个巨大的数组(值),要么使用即写即拷机制如果本地数组可以改变在方法或允许方法(如Java)修改数组全局(来自调用者的观点)和一些语言允许修改引用本身的价值。

So in short and in Java's own terminology, Java is pass-by-value where value can be: either a real value or a value that is a representation of a reference.

因此,简而言之,在Java自己的术语中,Java是一个按值传递的值,其中的值可以是:一个实值或一个表示引用的值。

#15


49  

No, it's not pass by reference.

不,它不是通过引用传递的。

Java is pass by value according to the Java Language Specification:

Java是按Java语言规范的值传递的:

When the method or constructor is invoked (§15.12), the values of the actual argument expressions initialize newly created parameter variables, each of the declared type, before execution of the body of the method or constructor. The Identifier that appears in the DeclaratorId may be used as a simple name in the body of the method or constructor to refer to the formal parameter.

当调用方法或构造函数时(15.12),实际的参数表达式的值在方法或构造函数的主体执行之前初始化新创建的参数变量,每一个声明类型。在声明语句中出现的标识符可以作为方法或构造函数的一个简单名称来引用形式参数。

#16


48  

As far as I know, Java only knows call by value. This means for primitive datatypes you will work with an copy and for objects you will work with an copy of the reference to the objects. However I think there are some pitfalls; for example, this will not work:

据我所知,Java只知道按值调用。这意味着对于原始数据类型,您将使用一个副本和对象,您将使用对对象的引用的副本工作。然而,我认为有一些陷阱;例如,这将不起作用:

public static void swap(StringBuffer s1, StringBuffer s2) {
    StringBuffer temp = s1;
    s1 = s2;
    s2 = temp;
}


public static void main(String[] args) {
    StringBuffer s1 = new StringBuffer("Hello");
    StringBuffer s2 = new StringBuffer("World");
    swap(s1, s2);
    System.out.println(s1);
    System.out.println(s2);
}

This will populate Hello World and not World Hello because in the swap function you use copys which have no impact on the references in the main. But if your objects are not immutable you can change it for example:

这将填充Hello World,而不是World Hello,因为在交换函数中,您使用的是copys,它对main中的引用没有影响。但是如果你的对象不是不可变的,你可以改变它,例如:

public static void appendWorld(StringBuffer s1) {
    s1.append(" World");
}

public static void main(String[] args) {
    StringBuffer s = new StringBuffer("Hello");
    appendWorld(s);
    System.out.println(s);
}

This will populate Hello World on the command line. If you change StringBuffer into String it will produce just Hello because String is immutable. For example:

这将在命令行中填充Hello World。如果你将StringBuffer改为String,它会生成Hello,因为String是不可变的。例如:

public static void appendWorld(String s){
    s = s+" World";
}

public static void main(String[] args) {
    String s = new String("Hello");
    appendWorld(s);
    System.out.println(s);
}

However you could make a wrapper for String like this which would make it able to use it with Strings:

但是你可以为字符串做一个包装这样它就能使用字符串:

class StringWrapper {
    public String value;

    public StringWrapper(String value) {
        this.value = value;
    }
}

public static void appendWorld(StringWrapper s){
    s.value = s.value +" World";
}

public static void main(String[] args) {
    StringWrapper s = new StringWrapper("Hello");
    appendWorld(s);
    System.out.println(s.value);
}

edit: i believe this is also the reason to use StringBuffer when it comes to "adding" two Strings because you can modifie the original object which u can't with immutable objects like String is.

编辑:我认为这也是在“添加”两个字符串时使用StringBuffer的原因,因为您可以修改原始对象,而不能使用不可变对象,比如String。

#17


45  

Let me try to explain my understanding with the help of four examples. Java is pass-by-value, and not pass-by-reference

让我用四个例子来解释我的理解。Java是按值传递的,而不是通过引用传递的。

/**

/ * *

Pass By Value

按值传递

In Java, all parameters are passed by value, i.e. assigning a method argument is not visible to the caller.

在Java中,所有参数都是通过值传递的,即分配一个方法参数对调用者来说是不可见的。

*/

* /

Example 1:

示例1:

public class PassByValueString {
    public static void main(String[] args) {
        new PassByValueString().caller();
    }

    public void caller() {
        String value = "Nikhil";
        boolean valueflag = false;
        String output = method(value, valueflag);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'value' and 'valueflag'
         */
        System.out.println("output : " + output);
        System.out.println("value : " + value);
        System.out.println("valueflag : " + valueflag);

    }

    public String method(String value, boolean valueflag) {
        value = "Anand";
        valueflag = true;
        return "output";
    }
}

Result

结果

output : output
value : Nikhil
valueflag : false

Example 2:

示例2:

/** * * Pass By Value * */

/** ** ** **

public class PassByValueNewString {
    public static void main(String[] args) {
        new PassByValueNewString().caller();
    }

    public void caller() {
        String value = new String("Nikhil");
        boolean valueflag = false;
        String output = method(value, valueflag);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'value' and 'valueflag'
         */
        System.out.println("output : " + output);
        System.out.println("value : " + value);
        System.out.println("valueflag : " + valueflag);

    }

    public String method(String value, boolean valueflag) {
        value = "Anand";
        valueflag = true;
        return "output";
    }
}

Result

结果

output : output
value : Nikhil
valueflag : false

Example 3:

示例3:

/** This 'Pass By Value has a feeling of 'Pass By Reference'

/**这个“价值传递”有“通过参考”的感觉

Some people say primitive types and 'String' are 'pass by value' and objects are 'pass by reference'.

有些人说,原始类型和“String”是“通过值传递”,对象是“通过引用传递”。

But from this example, we can understand that it is infact pass by value only, keeping in mind that here we are passing the reference as the value. ie: reference is passed by value. That's why are able to change and still it holds true after the local scope. But we cannot change the actual reference outside the original scope. what that means is demonstrated by next example of PassByValueObjectCase2.

但是从这个例子中,我们可以理解它实际上是通过值传递的,记住这里我们传递的是引用作为值。ie:引用是按值传递的。这就是为什么能够改变,并且在局部范围之后仍然是正确的。但我们不能改变原范围之外的实际引用。下一个PassByValueObjectCase2示例演示了这意味着什么。

*/

* /

public class PassByValueObjectCase1 {

    private class Student {
        int id;
        String name;
        public Student() {
        }
        public Student(int id, String name) {
            super();
            this.id = id;
            this.name = name;
        }
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        @Override
        public String toString() {
            return "Student [id=" + id + ", name=" + name + "]";
        }
    }

    public static void main(String[] args) {
        new PassByValueObjectCase1().caller();
    }

    public void caller() {
        Student student = new Student(10, "Nikhil");
        String output = method(student);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'student'
         */
        System.out.println("output : " + output);
        System.out.println("student : " + student);
    }

    public String method(Student student) {
        student.setName("Anand");
        return "output";
    }
}

Result

结果

output : output
student : Student [id=10, name=Anand]

Example 4:

示例4:

/**

/ * *

In addition to what was mentioned in Example3 (PassByValueObjectCase1.java), we cannot change the actual reference outside the original scope."

除了Example3 (PassByValueObjectCase1.java)中提到的之外,我们不能更改原始范围之外的实际引用。

Note: I am not pasting the code for private class Student. The class definition for Student is same as Example3.

注意:我不是在粘贴私有类学生的代码。学生的阶级定义和考试一样。

*/

* /

public class PassByValueObjectCase2 {

    public static void main(String[] args) {
        new PassByValueObjectCase2().caller();
    }

    public void caller() {
        // student has the actual reference to a Student object created
        // can we change this actual reference outside the local scope? Let's see
        Student student = new Student(10, "Nikhil");
        String output = method(student);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'student'
         */
        System.out.println("output : " + output);
        System.out.println("student : " + student); // Will it print Nikhil or Anand?
    }

    public String method(Student student) {
        student = new Student(20, "Anand");
        return "output";
    }

}

Result

结果

output : output
student : Student [id=10, name=Nikhil]

#18


42  

You can never pass by reference in Java, and one of the ways that is obvious is when you want to return more than one value from a method call. Consider the following bit of code in C++:

您不能在Java中通过引用传递,而显而易见的方法之一是,当您希望从方法调用返回多个值时。考虑一下c++中的以下代码:

void getValues(int& arg1, int& arg2) {
    arg1 = 1;
    arg2 = 2;
}
void caller() {
    int x;
    int y;
    getValues(x, y);
    cout << "Result: " << x << " " << y << endl;
}

Sometimes you want to use the same pattern in Java, but you can't; at least not directly. Instead you could do something like this:

有时候你想在Java中使用相同的模式,但是你不能;至少不是直接。你可以这样做:

void getValues(int[] arg1, int[] arg2) {
    arg1[0] = 1;
    arg2[0] = 2;
}
void caller() {
    int[] x = new int[1];
    int[] y = new int[1];
    getValues(x, y);
    System.out.println("Result: " + x[0] + " " + y[0]);
}

As was explained in previous answers, in Java you're passing a pointer to the array as a value into getValues. That is enough, because the method then modifies the array element, and by convention you're expecting element 0 to contain the return value. Obviously you can do this in other ways, such as structuring your code so this isn't necessary, or constructing a class that can contain the return value or allow it to be set. But the simple pattern available to you in C++ above is not available in Java.

正如前面的答案所解释的那样,在Java中,您将一个指针传递给数组作为getValues的值。这就足够了,因为该方法然后修改了数组元素,并且按照约定,您期望元素0包含返回值。显然,您可以通过其他方式来实现这一点,比如构造代码,这样就没有必要了,或者构造一个可以包含返回值或允许它被设置的类。

#19


41  

I thought I'd contribute this answer to add more details from the Specifications.

我想我应该贡献这个答案来从规范中添加更多的细节。

First, What's the difference between passing by reference vs. passing by value?

首先,通过引用传递和传递值之间有什么区别?

Passing by reference means the called functions' parameter will be the same as the callers' passed argument (not the value, but the identity - the variable itself).

通过引用传递函数的参数将与调用者传递的参数相同(不是值,而是标识-变量本身)。

Pass by value means the called functions' parameter will be a copy of the callers' passed argument.

通过值的传递意味着调用函数的参数将是调用者传递参数的副本。

Or from wikipedia, on the subject of pass-by-reference

或者从*上,以旁证的方式。

In call-by-reference evaluation (also referred to as pass-by-reference), a function receives an implicit reference to a variable used as argument, rather than a copy of its value. This typically means that the function can modify (i.e. assign to) the variable used as argument—something that will be seen by its caller.

在对引用的评价(也称为传递引用)中,函数接收一个隐式引用作为参数的变量,而不是它的值的副本。这通常意味着函数可以修改(即分配给)用作参数的变量,这是它的调用者可以看到的。

And on the subject of pass-by-value

以及关于传递价值的问题。

In call-by-value, the argument expression is evaluated, and the resulting value is bound to the corresponding variable in the function [...]. If the function or procedure is able to assign values to its parameters, only its local copy is assigned [...].

在callby -value中,参数表达式被求值,结果值被绑定到函数[…]中的相应变量。如果函数或过程能够为其参数赋值,则只分配其本地副本[…]。

Second, we need to know what Java uses in its method invocations. The Java Language Specification states

其次,我们需要知道Java在方法调用中使用了什么。Java语言规范声明。

When the method or constructor is invoked (§15.12), the values of the actual argument expressions initialize newly created parameter variables, each of the declared type, before execution of the body of the method or constructor.

当调用方法或构造函数时(15.12),实际的参数表达式的值在方法或构造函数的主体执行之前初始化新创建的参数变量,每一个声明类型。

So it assigns (or binds) the value of the argument to the corresponding parameter variable.

因此,它将参数的值分配给相应的参数变量。

What is the value of the argument?

这个论证的价值是什么?

Let's consider reference types, the Java Virtual Machine Specification states

让我们考虑引用类型,Java虚拟机规范。

There are three kinds of reference types: class types, array types, and interface types. Their values are references to dynamically created class instances, arrays, or class instances or arrays that implement interfaces, respectively.

有三种类型的引用类型:类类型、数组类型和接口类型。它们的值是对动态创建的类实例、数组或实现接口的类实例或数组的引用。

The Java Language Specification also states

Java语言规范也说明了这一点。

The reference values (often just references) are pointers to these objects, and a special null reference, which refers to no object.

引用值(通常只是引用)是指向这些对象的指针,以及一个特殊的空引用,它引用没有对象。

The value of an argument (of some reference type) is a pointer to an object. Note that a variable, an invocation of a method with a reference type return type, and an instance creation expression (new ...) all resolve to a reference type value.

参数(某些引用类型)的值是指向对象的指针。注意,一个变量、一个方法的调用和一个引用类型返回类型,以及一个实例创建表达式(new…)都解析为一个引用类型值。

So

所以

public void method (String param) {}
...
String var = new String("ref");
method(var);
method(var.toString());
method(new String("ref"));

all bind the value of a reference to a String instance to the method's newly created parameter, param. This is exactly what the definition of pass-by-value describes. As such, Java is pass-by-value.

将引用的值绑定到方法的新创建的参数param上。这就是所谓的“按值”的定义。因此,Java是按值传递的。

The fact that you can follow the reference to invoke a method or access a field of the referenced object is completely irrelevant to the conversation. The definition of pass-by-reference was

您可以遵循引用来调用方法或访问引用对象的字段,这与对话完全无关。旁路的定义是。

This typically means that the function can modify (i.e. assign to) the variable used as argument—something that will be seen by its caller.

这通常意味着函数可以修改(即分配给)用作参数的变量,这是它的调用者可以看到的。

In Java, modifying the variable means reassigning it. In Java, if you reassigned the variable within the method, it would go unnoticed to the caller. Modifying the object referenced by the variable is a different concept entirely.

在Java中,修改变量意味着重新分配它。在Java中,如果在方法中重新分配变量,那么调用者就不会注意到它。修改变量引用的对象是完全不同的概念。


Primitive values are also defined in the Java Virtual Machine Specification, here. The value of the type is the corresponding integral or floating point value, encoded appropriately (8, 16, 32, 64, etc. bits).

在这里,Java虚拟机规范中也定义了原始值。类型的值是相应的整数或浮点值,适当地编码(8、16、32、64等位)。

#20


36  

Java is always pass by value, not pass by reference

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

First of all, we need to understand what pass by value and pass by reference are.

首先,我们需要理解什么是通过值传递,然后通过引用传递。

Pass by value means that you are making a copy in memory of the actual parameter's value that is passed in. This is a copy of the contents of the actual parameter.

通过值传递意味着您正在创建一个副本,以记录传入的实际参数的值。这是实际参数的内容的副本。

Pass by reference (also called pass by address) means that a copy of the address of the actual parameter is stored.

通过引用传递(也称为传递通过地址)意味着存储实际参数的地址的副本。

Sometimes Java can give the illusion of pass by reference. Let's see how it works by using the example below:

有时,Java可以通过引用来产生传递的错觉。让我们来看看它是如何工作的:

public class PassByValue {
    public static void main(String[] args) {
        Test t = new Test();
        t.name = "initialvalue";
        new PassByValue().changeValue(t);
        System.out.println(t.name);
    }

    public void changeValue(Test f) {
        f.name = "changevalue";
    }
}

class Test {
    String name;
}

The output of this program is:

这个程序的输出是:

changevalue

Let's understand step by step:

让我们一步一步来理解:

Test t = new Test();

As we all know it will create an object in the heap and return the reference value back to t. For example, suppose the value of t is 0x100234 (we don't know the actual JVM internal value, this is just an example) .

我们都知道,它将在堆中创建一个对象,并将引用值返回给t。

Java是“旁路”还是“按值”?

new PassByValue().changeValue(t);

When passing reference t to the function it will not directly pass the actual reference value of object test, but it will create a copy of t and then pass it to the function. Since it is passing by value, it passes a copy of the variable rather than the actual reference of it. Since we said the value of t was 0x100234, both t and f will have the same value and hence they will point to the same object.

当将引用t传递给函数时,它不会直接传递对象测试的实际引用值,但它会创建一个t的副本,然后将其传递给函数。由于它是通过值传递的,所以它传递的是变量的副本,而不是它的实际引用。因为我们说t的值是0x100234,所以t和f都有相同的值,因此它们会指向同一个对象。

Java是“旁路”还是“按值”?

If you change anything in the function using reference f it will modify the existing contents of the object. That is why we got the output changevalue, which is updated in the function.

如果使用引用f改变函数中的任何内容,它将修改对象的现有内容。这就是为什么我们得到了输出changevalue,它是在函数中更新的。

To understand this more clearly, consider the following example:

为了更清楚地理解这一点,请考虑下面的例子:

public class PassByValue {
    public static void main(String[] args) {
        Test t = new Test();
        t.name = "initialvalue";
        new PassByValue().changeRefence(t);
        System.out.println(t.name);
    }

    public void changeRefence(Test f) {
        f = null;
    }
}

class Test {
    String name;
}

Will this throw a NullPointerException? No, because it only passes a copy of the reference. In the case of passing by reference, it could have thrown a NullPointerException, as seen below:

这会抛出NullPointerException吗?不,因为它只传递引用的副本。在通过引用的情况下,它可以抛出一个NullPointerException,如下所示:

Java是“旁路”还是“按值”?

Hopefully this will help.

希望这将帮助。

#21


34  

Java is a call by value.

Java是一个值调用。

How it works.

它是如何工作的。

  • You always pass a copy of the bits of the value of the reference!

    您总是通过一个副本的值的引用!

  • If it's a primitive data type these bits contain the value of the primitive data type itself, That's why if we change the value of header inside the method then it does not reflect the changes outside.

    如果是原始数据类型,这些位包含原始数据类型本身的值,这就是为什么如果我们在方法中更改header的值,那么它不会反映外部的变化。

  • If it's an object data type like Foo foo=new Foo() then in this case copy of the address of the object passes like file shortcut , suppose we have a text file abc.txt at C:\desktop and suppose we make shortcut of the same file and put this inside C:\desktop\abc-shortcut so when you access the file from C:\desktop\abc.txt and write 'Stack Overflow' and close the file and again you open the file from shortcut then you write ' is the largest online community for programmers to learn' then total file change will be 'Stack Overflow is the largest online community for programmers to learn' which means it doesn't matter from where you open the file , each time we were accessing the same file , here we can assume Foo as a file and suppose foo stored at 123hd7h(original address like C:\desktop\abc.txt ) address and 234jdid(copied address like C:\desktop\abc-shortcut which actually contains the original address of the file inside) .. So for better understanding make shortcut file and feel...

    如果它是一个对象数据类型,比如Foo Foo =new Foo(),那么在这个例子中,对象地址的副本就像文件快捷方式一样传递,假设我们有一个文本文件abc。txt在C:\desktop,假设我们在同一个文件中抄近路,并把它放在C:\desktop\ C -快捷方式中,所以当你从C:\desktop\abc访问文件时。txt和写“Stack Overflow”并关闭文件再一次你打开文件快捷方式然后你写的是最大的在线社区为程序员学习”然后总文件变化将是堆栈溢出是最大的网上社区程序员学习”这意味着不管从你打开文件,每一次我们都访问同一个文件,在这里我们可以假设Foo作为一个文件,并假设Foo储存在123 hd7h(原地址像C:\桌面\ abc。地址和234jdid(复制地址如C:\desktop\abc-快捷方式,实际上包含文件的原始地址)..所以为了更好的理解,制作快捷文件和感觉…

#22


33  

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中,除了原语以外的任何东西)都是引用。这些引用是按值传递的。

#23


28  

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的引用只会在本地范围内发生变化,而不是在此方法之外。

#24


28  

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

name = Maxx的名字=狗

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

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

#25


25  

Java has only pass by value. A very simple example to validate this.

Java只通过值传递。一个非常简单的例子来验证这一点。

public void test() {
    MyClass obj = null;
    init(obj);
    //After calling init method, obj still points to null
    //this is because obj is passed as value and not as reference.
}
private void init(MyClass objVar) {
    objVar = new MyClass();
}

#26


25  

In Java only references are passed and are passed by value:

在Java中,只有引用被传递并通过值传递:

Java arguments are all passed by value (the reference is copied when used by the method) :

Java参数都是通过值传递的(当方法使用时,引用被复制):

In the case of primitive types, Java behaviour is simple: The value is copied in another instance of the primitive type.

在基本类型的情况下,Java行为很简单:值是在原始类型的另一个实例中复制的。

In case of Objects, this is the same: Object variables are pointers (buckets) holding only Object’s address that was created using the "new" keyword, and are copied like primitive types.

在对象的情况下,这是相同的:对象变量是指针(bucket),只持有用“new”关键字创建的对象的地址,并且被复制为原始类型。

The behaviour can appear different from primitive types: Because the copied object-variable contains the same address (to the same Object) Object's content/members might still be modified within a method and later access outside, giving the illusion that the (containing) Object itself was passed by reference.

这种行为可能与原始类型不同:因为复制的对象变量包含相同的地址(对同一个对象),对象的内容/成员可能仍然会在方法中被修改,然后在外部访问,这就产生了一个错觉,认为(包含)对象本身是通过引用传递的。

"String" Objects appear to be a perfect counter-example to the urban legend saying that "Objects are passed by reference":

“String”对象似乎是都市传说中的一个完美的反例,它说“对象通过引用传递”:

In effect, within a method you will never be able, to update the value of a String passed as argument:

实际上,在一个方法中,您将永远无法更新作为参数传递的字符串的值:

A String Object, holds characters by an array declared final that can't be modified. Only the address of the Object might be replaced by another using "new". Using "new" to update the variable, will not let the Object be accessed from outside, since the variable was initially passed by value and copied.

一个字符串对象,由一个被声明为无法修改的数组中的字符来保存字符。只有对象的地址可以用“new”替换。使用“new”来更新变量,将不会让对象从外部访问,因为变量最初是通过值传递并复制的。

#27


23  

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

我已经为这里的任何编程语言创建了一个用于此类问题的线程。

Java is also mentioned. Here is the short summary:

Java也提到过。以下是简短的总结:

  • Java passes it parameters by value
  • Java通过值传递参数。
  • "by value" is the only way in java to pass a parameter to a method
  • “按值”是java中唯一将参数传递给方法的方法。
  • 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)
  • 使用对象的方法作为参数将会改变对象,因为引用指向原始对象。(如果该方法本身改变了一些值)

#28


23  

To make a long story short, Java objects 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具有直接通过值传递的基本类型(int、bool、char、double等)。然后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)是一个函数,它不需要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 a should 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++,我们有参考资料。引用基本上(在这种情况下)语法sugar隐藏了等式的指针部分:void foo(int &x)被foo(a)调用,编译器本身知道它是一个引用,而非引用a的地址应该被传递。在Java中,引用对象的所有变量实际上都是引用类型,实际上是在没有细粒度控制(和复杂性)的情况下对大多数意图和目的进行调用,例如c++。

#29


22  

A few corrections to some posts.

对一些帖子做了一些修改。

C does NOT support pass by reference. It is ALWAYS pass by value. C++ does support pass by reference, but is not the default and is quite dangerous.

C不支持通过引用传递。它总是通过价值传递。c++支持通过引用传递,但不是默认值,而且非常危险。

It doesn't matter what the value is in Java: primitive or address(roughly) of object, it is ALWAYS passed by value.

在Java中值是什么并不重要:原始或地址(大致),它总是通过值传递。

If a Java object "behaves" like it is being passed by reference, that is a property of mutability and has absolutely nothing to do with passing mechanisms.

如果一个Java对象“行为”就像它被引用传递一样,那么它就是一个可变性的属性,与传递机制完全没有关系。

I am not sure why this is so confusing, perhaps because so many Java "programmers" are not formally trained, and thus do not understand what is really going on in memory?

我不知道为什么这是如此令人困惑,也许是因为许多Java“程序员”没有经过正式的培训,因此不理解内存中到底发生了什么?

#30


17  

Java copies the reference by value. So if you change it to something else (e.g, using new) the reference does not change outside the method. For native types, it is always pass by value.

Java通过值复制引用。所以如果你把它换成别的东西(e)g,使用新的)引用在方法之外不会改变。对于本机类型,它总是按值传递。

#1


4482  

Java is always pass-by-value. Unfortunately, they decided to call the location of an object a "reference". When we pass the value of an object, we are passing the reference to it. This is confusing to beginners.

Java总是按值传递。不幸的是,他们决定将对象的位置称为“引用”。当我们传递一个对象的值时,我们将引用它。这让初学者很困惑。

It goes like this:

它是这样的:

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
}

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

在上面的例子中,aDog.getName()仍然返回“Max”。main中的值aDog在函数foo中没有改变,而Dog“Fifi”作为对象引用是通过值传递的。如果它是通过引用传递的,那么main中的aDog.getName()将在调用foo之后返回“Fifi”。

Likewise:

同样的:

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

In the above example, Fifi is the dog's name after call to foo(aDog) because the object's name was set inside of foo(...). Any operations that foo performs on d are such that, for all practical purposes, they are performed on aDog itself (except when d is changed to point to a different Dog instance like d = new Dog("Boxer")).

在上面的例子中,Fifi是在调用foo(aDog)之后的狗的名字,因为对象的名称是在foo(…)中设置的。foo在d上执行的任何操作都是这样的,出于所有实际目的,它们都是在aDog本身上执行的(除了d被更改为指向一个不同的Dog实例,比如d = new Dog(“Boxer”))。

#2


2534  

I just noticed you referenced my article.

我刚注意到你引用了我的文章。

The Java Spec says that everything in Java is pass-by-value. There is no such thing as "pass-by-reference" in Java.

Java规范说,Java中的一切都是按值传递的。在Java中没有所谓的“附带引用”。

The key to understanding this is that something like

理解这一点的关键是。

Dog myDog;

is not a Dog; it's actually a pointer to a Dog.

不是一条狗;它实际上是一个指向狗的指针。

What that means, is when you have

这意味着什么,当你拥有!

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

you're essentially passing the address of the created Dog object to the foo method.

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

(I say essentially because Java pointers aren't direct addresses, but it's easiest to think of them that way)

(我之所以说,本质上是因为Java指针不是直接地址,但最容易想到的是它们)

Suppose the Dog object resides at memory address 42. This means we pass 42 to the method.

假设Dog对象驻留在内存地址42中。这意味着我们把42传递给这个方法。

if the Method were defined as

如果方法被定义为。

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

let's look at what's happening.

让我们看看发生了什么。

  • the parameter someDog is set to the value 42
  • someDog的参数设置为42。
  • at line "AAA"
    • someDog is followed to the Dog it points to (the Dog object at address 42)
    • 有些狗被跟踪到它指向的狗(狗的对象在地址42)
    • that Dog (the one at address 42) is asked to change his name to Max
    • 那只狗(地址是42)被要求把他的名字改成马克斯。
  • 在“AAA”级的狗狗被跟踪到它指向的狗(地址是42)那条狗(地址为42)被要求将他的名字改为马克斯。
  • at line "BBB"
    • a new Dog is created. Let's say he's at address 74
    • 一条新狗诞生了。假设他在地址74。
    • we assign the parameter someDog to 74
    • 我们将参数someDog指定为74。
  • 在“BBB”线上,一条新狗诞生了。假设他在地址74,我们将参数someDog分配给74。
  • at line "CCC"
    • someDog is followed to the Dog it points to (the Dog object at address 74)
    • 有些狗被跟踪到它指向的狗(狗的对象在地址74)
    • that Dog (the one at address 74) is asked to change his name to Rowlf
    • 那只狗(地址为74)被要求把他的名字改为Rowlf。
  • 在“CCC”这条狗狗的后面,它指向狗狗(地址为74)那条狗(地址为74)被要求将他的名字改为Rowlf。
  • then, we return
  • 然后,我们返回

Now let's think about what happens outside the method:

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

Did myDog change?

myDog改变吗?

There's the key.

的关键。

Keeping in mind that myDog is a pointer, and not an actual Dog, the answer is NO. myDog still has the value 42; it's still pointing to the original Dog (but note that because of line "AAA", its name is now "Max" - still the same Dog; myDog's value has not changed.)

记住,我的狗是一个指针,而不是一只真正的狗,答案是否定的。myDog仍然有值42;它仍然指向原来的狗(但请注意,因为“AAA”,它的名字现在是“Max”——仍然是同一只狗;myDog的价值没有改变。

It's perfectly valid to follow an address and change what's at the end of it; that does not change the variable, however.

遵循一个地址并改变它的结尾是完全有效的;然而,这并不会改变变量。

Java works exactly like C. You can assign a pointer, pass the pointer to a method, follow the pointer in the method and change the data that was pointed to. However, you cannot change where that pointer points.

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

In C++, Ada, Pascal and other languages that support pass-by-reference, you can actually change the variable that was passed.

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

If Java had pass-by-reference semantics, the foo method we defined above would have changed where myDog was pointing when it assigned someDog on line BBB.

如果Java具有传递引用的语义,那么我们在上面定义的foo方法将会在myDog的位置上发生变化。

Think of reference parameters as being aliases for the variable passed in. When that alias is assigned, so is the variable that was passed in.

考虑引用参数作为传入变量的别名。当这个别名被分配时,传入的变量也是。

#3


1324  

Java always passes arguments by value NOT by reference.

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


Let me explain this through an 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");
     }
}

I will explain this in steps:

我将在以下步骤中解释:

  1. Declaring a reference named f of type Foo and assign it to a new object of type Foo with an attribute "f".

    声明一个名为Foo类型的引用,并将其分配给Foo类型的新对象,并带有一个属性“f”。

    Foo f = new Foo("f");
    

    Java是“旁路”还是“按值”?

  2. From the method side, a reference of type Foo with a name a is declared and it's initially assigned to null.

    从方法方面来说,声明了一个名称为a的Foo类型的引用,它最初被赋值为null。

    public static void changeReference(Foo a)
    

    Java是“旁路”还是“按值”?

  3. As you call the method changeReference, the reference a will be assigned to the object which is passed as an argument.

    当您调用方法changeReference时,引用a将被分配给作为参数传递的对象。

    changeReference(f);
    

    Java是“旁路”还是“按值”?

  4. Declaring a reference named b of type Foo and assign it to a new object of type Foo with an attribute "b".

    声明一个名为Foo类型的引用,并将其分配给一个带有“b”属性的Foo类型的新对象。

    Foo b = new Foo("b");
    

    Java是“旁路”还是“按值”?

  5. a = b is re-assigning the reference a NOT f to the object whose its attribute is "b".

    a = b重新分配对其属性为“b”的对象的引用a而非f。

    Java是“旁路”还是“按值”?


  6. As you call modifyReference(Foo c) method, a reference c is created and assigned to the object with attribute "f".

    当您调用modifyReference(Foo c)方法时,将创建一个引用c,并将其分配给具有属性“f”的对象。

    Java是“旁路”还是“按值”?

  7. c.setAttribute("c"); will change the attribute of the object that reference c points to it, and it's same object that reference f points to it.

    c.setAttribute(“c”);将会改变引用c指向它的对象的属性,它与引用f指向它的对象相同。

    Java是“旁路”还是“按值”?

I hope you understand now how passing objects as arguments works in Java :)

我希望您现在能够理解如何将对象作为参数在Java中工作:)

#4


631  

This will give you some insights of how Java really works to the point that in your next discussion about Java passing by reference or passing by value you'll just smile :-)

这将给您提供一些关于Java如何真正起作用的见解,在您下次讨论Java通过引用或传递值时,您只需微笑:-)

Step one please erase from your mind that word that starts with 'p' "_ _ _ _ _ _ _", especially if you come from other programming languages. Java and 'p' cannot be written in the same book, forum, or even txt.

第一步,请从你的脑海中抹去“p”开头的单词“_ _ _ _ _ _”,尤其是你来自其他编程语言的时候。Java和“p”不能写在同一本书、论坛甚至txt中。

Step two remember that when you pass an Object into a method you're passing the Object reference and not the Object itself.

第二步记住,当你将一个对象传递给一个方法时,你传递的是对象引用而不是对象本身。

  • Student: Master, does this mean that Java is pass-by-reference?
  • 学生:大师,这是否意味着Java是间接引用?
  • Master: Grasshopper, No.
  • 主:蚱蜢,没有。

Now think of what an Object's reference/variable does/is:

现在想想对象的引用/变量是什么

  1. A variable holds the bits that tell the JVM how to get to the referenced Object in memory (Heap).
  2. 一个变量包含告诉JVM如何到达内存中的引用对象的位(堆)。
  3. When passing arguments to a method you ARE NOT passing the reference variable, but a copy of the bits in the reference variable. Something like this: 3bad086a. 3bad086a represents a way to get to the passed object.
  4. 当将参数传递给方法时,您不会传递引用变量,而是引用变量中的位的副本。这样的:3 bad086a。3bad086a代表一种到达被传递对象的方法。
  5. So you're just passing 3bad086a that it's the value of the reference.
  6. 你只是通过了3bad086a这是参考值。
  7. You're passing the value of the reference and not the reference itself (and not the object).
  8. 您正在传递引用的值,而不是引用本身(而不是对象)。
  9. This value is actually COPIED and given to the method.
  10. 这个值实际上是被复制的,并被赋予了这个方法。

In the following (please don't try to compile/execute this...):

在下面(请不要试图编译/执行这个…):

1. Person person;
2. person = new Person("Tom");
3. changeName(person);
4.
5. //I didn't use Person person below as an argument to be nice
6. static void changeName(Person anotherReferenceToTheSamePersonObject) {
7.     anotherReferenceToTheSamePersonObject.setName("Jerry");
8. }

What happens?

会发生什么呢?

  • The variable person is created in line #1 and it's null at the beginning.
  • 变量person是在第1行创建的,在开始时是null。
  • A new Person Object is created in line #2, stored in memory, and the variable person is given the reference to the Person object. That is, its address. Let's say 3bad086a.
  • 在第2行中创建了一个新的Person对象,该对象存储在内存中,而变量Person将被指定给Person对象。也就是说,它的地址。假设3 bad086a。
  • The variable person holding the address of the Object is passed to the function in line #3.
  • 持有对象地址的变量被传递给第3行中的函数。
  • In line #4 you can listen to the sound of silence
  • 在第4行,你可以听到寂静的声音。
  • Check the comment on line #5
  • 检查第5行的注释。
  • A method local variable -anotherReferenceToTheSamePersonObject- is created and then comes the magic in line #6:
    • The variable/reference person is copied bit-by-bit and passed to anotherReferenceToTheSamePersonObject inside the function.
    • 变量/引用人被逐位复制,并传递给函数内部的其他引用。
    • No new instances of Person are created.
    • 没有创建新的Person实例。
    • Both "person" and "anotherReferenceToTheSamePersonObject" hold the same value of 3bad086a.
    • “person”和“anotherReferenceToTheSamePersonObject”都具有相同的3bad086a的值。
    • Don't try this but person==anotherReferenceToTheSamePersonObject would be true.
    • 不要尝试这个,但是人==另一个人是正确的。
    • Both variables have IDENTICAL COPIES of the reference and they both refer to the same Person Object, the SAME Object on the Heap and NOT A COPY.
    • 两个变量都具有相同的引用副本,它们都引用相同的Person对象,即堆上的相同对象,而不是副本。
  • 一个方法局部变量-另一个引用的personobject -是被创建的,然后在第6行中出现了魔术:变量/引用人被复制,并传递给在函数内部的另一个personobject。没有创建新的Person实例。“person”和“anotherReferenceToTheSamePersonObject”都具有相同的3bad086a的值。不要尝试这个,但是人==另一个人是正确的。两个变量都具有相同的引用副本,它们都引用相同的Person对象,即堆上的相同对象,而不是副本。

A picture is worth a thousand words:

一幅画胜过千言万语:

Java是“旁路”还是“按值”?

Note that the anotherReferenceToTheSamePersonObject arrows is directed towards the Object and not towards the variable person!

请注意,另一个对personobject箭头的引用指向对象,而不是指向变量person!

If you didn't get it then just trust me and remember that it's better to say that Java is pass by value. Well, pass by reference value. Oh well, even better is pass-by-copy-of-the-variable-value! ;)

如果你没有得到它,那就相信我,记住,最好说Java是通过值传递的。好,通过参考值传递。哦,好吧,甚至更好的是,一种变化的价值!,)

Now feel free to hate me but note that given this there is no difference between passing primitive data types and Objects when talking about method arguments.

现在可以随意地讨厌我,但是请注意,在讨论方法参数时,传递原始数据类型和对象之间没有区别。

You always pass a copy of the bits of the value of the reference!

您总是通过一个副本的值的引用!

  • If it's a primitive data type these bits will contain the value of the primitive data type itself.
  • 如果是原始数据类型,这些位将包含原始数据类型本身的值。
  • If it's an Object the bits will contain the value of the address that tells the JVM how to get to the Object.
  • 如果它是一个对象,位元将包含告诉JVM如何到达对象的地址的值。

Java is pass-by-value because inside a method you can modify the referenced Object as much as you want but no matter how hard you try you'll never be able to modify the passed variable that will keep referencing (not p _ _ _ _ _ _ _) the same Object no matter what!

Java是按值传递,因为在一个方法可以修改引用对象多达你想但无论你怎样努力尝试你永远无法修改将引用传递的变量(而不是p _ _ _ _ _ _ _)相同的对象,无论什么!


The changeName function above will never be able to modify the actual content (the bit values) of the passed reference. In other word changeName cannot make Person person refer to another Object.

上面的changeName函数永远无法修改传递的引用的实际内容(比特值)。换句话说,changeName不能使Person引用另一个对象。


Of course you can cut it short and just say that Java is pass-by-value!

当然,您可以简短地说,Java是按值的!

#5


544  

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 never provides direct access to the values of objects themselves, in any circumstances. The only access to objects is through a reference to that object. Because Java objects are always accessed through a reference, rather than directly, it is common to talk about fields and variables and method arguments as 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 value of the primitive (for example, 3).
  • 对于原始参数(int, long,等等),传递值是原语的实际值(例如,3)。
  • For objects, the pass by value is the value of the reference to the object.
  • 对于对象,通过值的传递是对对象的引用的值。

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

所以如果你有doSomething(foo)和public void doSomething(foo foo){。两个Foos复制指向相同对象的引用。

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

很自然地,引用一个对象的值看起来很像(实际上是无法区分的)通过引用传递一个对象。

#6


266  

Java passes references by value.

Java通过值传递引用。

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

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

#7


189  

I feel like arguing about "pass-by-reference vs pass-by-value" is not super-helpful.

我觉得争论“旁路和旁路”并不是很有帮助。

If you say, "Java is pass-by-whatever (reference/value)", in either case, you're not provide a complete answer. Here's some additional information that will hopefully aid in understanding what's happening in memory.

如果您说,“Java是按任意(引用/值)传递的”在这两种情况下,你都不能给出一个完整的答案。这里有一些额外的信息,希望有助于理解记忆中发生的事情。

Crash course on stack/heap before we get to the Java implementation: Values go on and off the stack in a nice orderly fashion, like a stack of plates at a cafeteria. Memory in the heap (also known as dynamic memory) is haphazard and disorganized. The JVM just finds space wherever it can, and frees it up as the variables that use it are no longer needed.

在我们进入Java实现之前,在堆栈/堆上的速成课程:值以一种有序的方式在堆栈上进行,就像在自助餐厅的一堆盘子一样。堆中的内存(也称为动态内存)是随意和无序的。JVM可以在任何地方找到空间,并将其释放为不再需要使用它的变量。

Okay. First off, local primitives go on the stack. So this code:

好吧。首先,本地原语进入堆栈。所以这段代码:

int x = 3;
float y = 101.1f;
boolean amIAwesome = true;

results in this:

结果:

Java是“旁路”还是“按值”?

When you declare and instantiate an object. The actual object goes on the heap. What goes on the stack? The address of the object on the heap. C++ programmers would call this a pointer, but some Java developers are against the word "pointer". Whatever. Just know that the address of the object goes on the stack.

当您声明并实例化一个对象时。实际对象在堆上。堆栈上有什么?堆上对象的地址。c++程序员将此称为指针,但是一些Java开发人员反对“指针”这个词。无论什么。只需知道对象的地址在堆栈上。

Like so:

像这样:

int problems = 99;
String name = "Jay-Z";

Java是“旁路”还是“按值”?

An array is an object, so it goes on the heap as well. And what about the objects in the array? They get their own heap space, and the address of each object goes inside the array.

数组是一个对象,所以它也在堆上。那么数组中的对象呢?它们有自己的堆空间,每个对象的地址都在数组中。

JButton[] marxBros = new JButton[3];
marxBros[0] = new JButton("Groucho");
marxBros[1] = new JButton("Zeppo");
marxBros[2] = new JButton("Harpo");

Java是“旁路”还是“按值”?

So, what gets passed in when you call a method? If you pass in an object, what you're actually passing in is the address of the object. Some might say the "value" of the address, and some say it's just a reference to the object. This is the genesis of the holy war between "reference" and "value" proponents. What you call it isn't as important as that you understand that what's getting passed in is the address to the object.

那么,当你调用一个方法时,会传入什么?如果传入一个对象,实际上传入的是对象的地址。有些人可能会说地址的“值”,有些人说这只是对对象的引用。这是“参考”和“价值”支持者之间的神圣战争的起源。你所称的它并不重要,因为你知道传递进来的是对象的地址。

private static void shout(String name){
    System.out.println("There goes " + name + "!");
}

public static void main(String[] args){
    String hisName = "John J. Jingleheimerschmitz";
    String myName = hisName;
    shout(myName);
}

One String gets created and space for it is allocated in the heap, and the address to the string is stored on the stack and given the identifier hisName, since the address of the second String is the same as the first, no new String is created and no new heap space is allocated, but a new identifier is created on the stack. Then we call shout(): a new stack frame is created and a new identifier, name is created and assigned the address of the already-existing String.

一个字符串创建在堆中分配空间,和地址的字符串存储在堆栈和考虑到标识符姓名,因为第二个字符串的地址是一样的,没有新的字符串创建和新分配的堆空间,但是一项新的标识符是在栈上创建。然后我们调用shout():创建一个新的堆栈帧,并创建一个新的标识符,名称被创建,并分配已经存在的字符串的地址。

Java是“旁路”还是“按值”?

So, value, reference? You say "potato".

因此,价值,参考?你说的“土豆”。

#8


157  

Just to show the contrast, compare the following C++ and Java snippets:

为了显示对比,请比较以下c++和Java代码片段:

In C++: Note: Bad code - memory leaks! But it demonstrates the point.

在c++中:注意:糟糕的代码-内存泄漏!但它证明了这一点。

void cppMethod(int val, int &ref, Dog obj, Dog &objRef, Dog *objPtr, Dog *&objPtrRef)
{
    val = 7; // Modifies the copy
    ref = 7; // Modifies the original variable
    obj.SetName("obj"); // Modifies the copy of Dog passed
    objRef.SetName("objRef"); // Modifies the original Dog passed
    objPtr->SetName("objPtr"); // Modifies the original Dog pointed to 
                               // by the copy of the pointer passed.
    objPtr = new Dog("newObjPtr");  // Modifies the copy of the pointer, 
                                   // leaving the original object alone.
    objPtrRef->SetName("objRefPtr"); // Modifies the original Dog pointed to 
                                    // by the original pointer passed. 
    objPtrRef = new Dog("newObjPtrRef"); // Modifies the original pointer passed
}

int main()
{
    int a = 0;
    int b = 0;
    Dog d0 = Dog("d0");
    Dog d1 = Dog("d1");
    Dog *d2 = new Dog("d2");
    Dog *d3 = new Dog("d3");
    cppMethod(a, b, d0, d1, d2, d3);
    // a is still set to 0
    // b is now set to 7
    // d0 still have name "d0"
    // d1 now has name "objRef"
    // d2 now has name "objPtr"
    // d3 now has name "newObjPtrRef"
}

In Java,

在Java中,

public static void javaMethod(int val, Dog objPtr)
{
   val = 7; // Modifies the copy
   objPtr.SetName("objPtr") // Modifies the original Dog pointed to 
                            // by the copy of the pointer passed.
   objPtr = new Dog("newObjPtr");  // Modifies the copy of the pointer, 
                                  // leaving the original object alone.
}

public static void main()
{
    int a = 0;
    Dog d0 = new Dog("d0");
    javaMethod(a, d0);
    // a is still set to 0
    // d0 now has name "objPtr"
}

Java only has the two types of passing: by value for built-in types, and by value of the pointer for object types.

Java只有两种类型的传递:通过对内置类型的值和对象类型指针的值。

#9


136  

Java passes references to objects by value.

Java通过值将引用传递给对象。

#10


132  

I can't believe that nobody mentioned Barbara Liskov yet. When she designed CLU in 1974, she ran into this same terminology problem, and she invented the term call by sharing (also known as call by object-sharing and call by object) for this specific case of "call by value where the value is a reference".

我真不敢相信没人提到过芭芭拉·利斯科夫。当她在1974年设计出了这个术语时,她遇到了同样的术语问题,她通过共享(也称为对象共享和对象调用)来创建术语调用,用于“值为引用的值的调用”。

#11


119  

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

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

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 bar is a copy of the value of baz, which is just a reference to "Hah!". If it were the actual reference itself, then foo would have redefined baz to null.

将打印出“哈!”而不是NULL。之所以这么做是因为bar是baz值的一个拷贝,这只是“哈!”的一个参考。如果它是实际的引用本身,那么foo就会将baz重新定义为null。

#12


87  

The crux of the matter is that the word reference in the expression "pass by reference" means something completely different from the usual meaning of the word reference in Java.

问题的关键在于,在“通过引用”这个表达式中的“引用”这个词的含义完全不同于Java中“引用”这个词的通常含义。

Usually in Java reference means a a reference to an object. But the technical terms pass by reference/value from programming language theory is talking about a reference to the memory cell holding the variable, which is something completely different.

通常在Java引用中,指的是对对象的引用。但是,从编程语言理论中引用/价值的技术术语是指对保存变量的内存单元的引用,这是完全不同的。

#13


71  

In java everything is reference, so when you have something like: Point pnt1 = new Point(0,0); Java does following:

在java中,一切都是引用,所以当你有类似的东西时:Point pnt1 = new Point(0,0);Java做如下:

  1. Creates new Point object
  2. 创建新的点对象
  3. Creates new Point reference and initialize that reference to point (refer to) on previously created Point object.
  4. 创建新的点引用并初始化该引用指向先前创建的Point对象。
  5. From here, through Point object life, you will access to that object through pnt1 reference. So we can say that in Java you manipulate object through its reference.
  6. 从这里,通过点对象生命,您将通过pnt1引用访问该对象。所以我们可以说,在Java中,你通过它的引用来操作对象。

Java是“旁路”还是“按值”?

Java doesn't pass method arguments by reference; it passes them by value. I will use example from this site:

Java不通过引用来传递方法参数;它通过值传递它们。我将用这个网站的例子:

public static void tricky(Point arg1, Point arg2) {
  arg1.x = 100;
  arg1.y = 100;
  Point temp = arg1;
  arg1 = arg2;
  arg2 = temp;
}
public static void main(String [] args) {
  Point pnt1 = new Point(0,0);
  Point pnt2 = new Point(0,0);
  System.out.println("X1: " + pnt1.x + " Y1: " +pnt1.y); 
  System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);
  System.out.println(" ");
  tricky(pnt1,pnt2);
  System.out.println("X1: " + pnt1.x + " Y1:" + pnt1.y); 
  System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);  
}

Flow of the program:

流的程序:

Point pnt1 = new Point(0,0);
Point pnt2 = new Point(0,0);

Creating two different Point object with two different reference associated. Java是“旁路”还是“按值”?

创建两个不同引用关联的不同的Point对象。

System.out.println("X1: " + pnt1.x + " Y1: " +pnt1.y); 
System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);
System.out.println(" ");

As expected output will be:

预期的产出将是:

X1: 0     Y1: 0
X2: 0     Y2: 0

On this line 'pass-by-value' goes into the play...

在这一行中,“pass-by-value”进入游戏……

tricky(pnt1,pnt2);           public void tricky(Point arg1, Point arg2);

References pnt1 and pnt2 are passed by value to the tricky method, which means that now yours references pnt1 and pnt2 have their copies named arg1 and arg2.So pnt1 and arg1 points to the same object. (Same for the pnt2 and arg2) Java是“旁路”还是“按值”?

引用pnt1和pnt2的值传递给复杂的方法,这意味着现在您的引用pnt1和pnt2有它们的副本,名为arg1和arg2。所以pnt1和arg1指向同一个对象。(pnt2和arg2相同)

In the tricky method:

在复杂的方法:

 arg1.x = 100;
 arg1.y = 100;

Java是“旁路”还是“按值”?

Next in the tricky method

接下来是棘手的方法。

Point temp = arg1;
arg1 = arg2;
arg2 = temp;

Here, you first create new temp Point reference which will point on same place like arg1 reference. Then you move reference arg1 to point to the same place like arg2 reference. Finally arg2 will point to the same place like temp.

在这里,您首先创建一个新的temp点引用,它将指向与arg1引用相同的位置。然后你移动参考arg1指向相同的地方,如arg2引用。最后,arg2将指向与temp相同的地方。

Java是“旁路”还是“按值”?

From here scope of tricky method is gone and you don't have access any more to the references: arg1, arg2, temp. But important note is that everything you do with these references when they are 'in life' will permanently affect object on which they are point to.

从这里开始,复杂的方法已经消失了,你再也无法访问参考文献:arg1, arg2, temp,但重要的是,当这些引用在“生命中”时,你所做的每件事都会永久地影响它们指向的对象。

So after executing method tricky, when you return to main, you have this situation: Java是“旁路”还是“按值”?

所以在执行完方法之后,当你回到main,你会遇到这样的情况:

So now, completely execution of program will be:

所以现在,程序的完全执行将是:

X1: 0         Y1: 0
X2: 0         Y2: 0
X1: 100       Y1: 100
X2: 0         Y2: 0

#14


61  

A reference is always a value when represented, no matter what language you use.

Getting an outside of the box view, let's look at Assembly or some low level memory management. At the CPU level a reference to anything immediately becomes a value if it gets written to memory or to one of the CPU registers. (That is why pointer is a good definition. It is a value, which has a purpose at the same time).

在框视图之外,让我们看看程序集或一些低级内存管理。在CPU级别,如果将其写入内存或CPU寄存器之一,则对任何内容的引用都会立即成为一个值。这就是为什么指针是一个很好的定义。它是一个有目的的价值。

Data in memory has a Location and at that location there is a value (byte,word, whatever). In Assembly we have a convenient solution to give a Name to certain Location (aka variable), but when compiling the code, the assembler simply replaces Name with the designated location just like your browser replaces domain names with IP addresses.

内存中的数据有一个位置,在那个位置有一个值(字节、单词或其他)。在程序集中,我们有一个方便的解决方案,可以将名称指定到某个位置(也就是变量),但是在编译代码时,汇编器只是将名称替换为指定的位置,就像您的浏览器用IP地址替换域名一样。

Down to the core it is technically impossible to pass a reference to anything in any language without representing it (when it immediately becomes a value).

从本质上说,从技术上讲,在任何语言中传递一个引用都是不可能的(当它立即变成一个值的时候)。

Lets say we have a variable Foo, its Location is at the 47th byte in memory and its Value is 5. We have another variable Ref2Foo which is at 223rd byte in memory, and its value will be 47. This Ref2Foo might be a technical variable, not explicitly created by the program. If you just look at 5 and 47 without any other information, you will see just two Values. If you use them as references then to reach to 5 we have to travel:

假设我们有一个变量Foo,它的位置在内存中的第47个字节,它的值是5。我们还有另一个变量Ref2Foo,它的内存是223字节,它的值是47。这个Ref2Foo可能是一个技术变量,不是由程序显式创建的。如果你只看5和47没有其他信息,你只会看到两个值。如果你把它们当作参考,那么我们就得去旅行。

(Name)[Location] -> [Value at the Location]
---------------------
(Ref2Foo)[223]  -> 47
(Foo)[47]       -> 5

This is how jump-tables work.

这就是跳跃表的工作原理。

If we want to call a method/function/procedure with Foo's value, there are a few possible way to pass the variable to the method, depending on the language and its several method invocation modes:

如果我们想用Foo的值调用方法/函数/过程,有几种可能的方法将变量传递给方法,这取决于语言和它的几种方法调用模式:

  1. 5 gets copied to one of the CPU registers (ie. EAX).
  2. 将5复制到一个CPU寄存器(ie)。EAX)。
  3. 5 gets PUSHd to the stack.
  4. 5被推到堆栈。
  5. 47 gets copied to one of the CPU registers
  6. 47被复制到一个CPU寄存器。
  7. 47 PUSHd to the stack.
  8. 47推到堆栈。
  9. 223 gets copied to one of the CPU registers.
  10. 223被复制到一个CPU寄存器。
  11. 223 gets PUSHd to the stack.
  12. 223被推到堆栈。

In every cases above a value - a copy of an existing value - has been created, it is now upto the receiving method to handle it. When you write "Foo" inside the method, it is either read out from EAX, or automatically dereferenced, or double dereferenced, the process depends on how the language works and/or what the type of Foo dictates. This is hidden from the developer until she circumvents the dereferencing process. So a reference is a value when represented, because a reference is a value that has to be processed (at language level).

在一个值之上的每一个案例中,都已经创建了一个现有值的副本,现在它是接收方法来处理它。当您在方法中写入“Foo”时,它不是从EAX中读出,或者是自动取消引用,或者是double dereferenced,这个过程取决于语言的工作方式和/或Foo命令的类型。这是隐藏于开发人员的,直到她绕过了取消引用的过程。因此,引用是在表示时的值,因为引用是必须处理的值(在语言级别)。

Now we have passed Foo to the method:

现在我们已经将Foo传递给方法:

  • in case 1. and 2. if you change Foo (Foo = 9) it only affects local scope as you have a copy of the Value. From inside the method we cannot even determine where in memory the original Foo was located.
  • 在案例1中。和2。如果您更改Foo (Foo = 9),它只会影响本地范围,因为您有一个值的副本。从这个方法中,我们甚至不能确定原来的Foo在内存中的位置。
  • in case 3. and 4. if you use default language constructs and change Foo (Foo = 11), it could change Foo globally (depends on the language, ie. Java or like Pascal's procedure findMin(x, y, z: integer;var m: integer);). However if the language allows you to circumvent the dereference process, you can change 47, say to 49. At that point Foo seems to have been changed if you read it, because you have changed the local pointer to it. And if you were to modify this Foo inside the method (Foo = 12) you will probably FUBAR the execution of the program (aka. segfault) because you will write to a different memory than expected, you can even modify an area that is destined to hold executable program and writing to it will modify running code (Foo is now not at 47). BUT Foo's value of 47 did not change globally, only the one inside the method, because 47 was also a copy to the method.
  • 在例3中。和4。如果使用默认语言构造和更改Foo (Foo = 11),它可以在全局上更改Foo(取决于语言)。Java或类似Pascal的程序findMin(x, y, z: integer;var m: integer);然而,如果该语言允许您绕过取消引用的过程,您可以更改47,比如说49。在这一点上,Foo似乎已经被改变了,如果你读它,因为你已经改变了它的本地指针。如果你在方法中修改这个Foo (Foo = 12),你很可能会把程序的执行(也就是,也就是你的程序)。由于您将写入与预期不同的内存,您甚至可以修改一个区域,该区域注定要保存可执行程序,并写入它将修改运行代码(Foo现在不是47)。但是Foo的值47并没有改变全局,只有方法中的一个,因为47也是方法的副本。
  • in case 5. and 6. if you modify 223 inside the method it creates the same mayhem as in 3. or 4. (a pointer, pointing to a now bad value, that is again used as a pointer) but this is still a local problem, as 223 was copied. However if you are able to dereference Ref2Foo (that is 223), reach to and modify the pointed value 47, say, to 49, it will affect Foo globally, because in this case the methods got a copy of 223 but the referenced 47 exists only once, and changing that to 49 will lead every Ref2Foo double-dereferencing to a wrong value.
  • 例5。和6。如果你在方法中修改223,它会造成和3一样的混乱。或4。(指针指向现在的坏值,再次被用作指针),但这仍然是一个本地问题,因为223被复制了。然而如果你能废弃Ref2Foo(即223),达到和修改指出价值47岁说,49,它将影响全球Foo,因为在这种情况下,方法得到一份223但引用47只存在一次,和改变49将每个Ref2Foo double-dereferencing错误的值。

Nitpicking on insignificant details, even languages that do pass-by-reference will pass values to functions, but those functions know that they have to use it for dereferencing purposes. This pass-the-reference-as-value is just hidden from the programmer because it is practically useless and the terminology is only pass-by-reference.

对于无关紧要的细节,即使是传递引用的语言也会将值传递给函数,但是这些函数知道它们必须使用它来进行解除引用。这个传递引用的值只是被程序员隐藏了,因为它实际上是无用的,而术语只是一个引用。

Strict pass-by-value is also useless, it would mean that a 100 Mbyte array should have to be copied every time we call a method with the array as argument, therefore Java cannot be stricly pass-by-value. Every language would pass a reference to this huge array (as a value) and either employs copy-on-write mechanism if that array can be changed locally inside the method or allows the method (as Java does) to modify the array globally (from the caller's view) and a few languages allows to modify the Value of the reference itself.

严格的传递值也是无用的,这意味着每次我们调用一个以数组为参数的方法时,必须复制100个Mbyte数组,因此Java不能严格地按值传递。每一种语言能通过引用这个巨大的数组(值),要么使用即写即拷机制如果本地数组可以改变在方法或允许方法(如Java)修改数组全局(来自调用者的观点)和一些语言允许修改引用本身的价值。

So in short and in Java's own terminology, Java is pass-by-value where value can be: either a real value or a value that is a representation of a reference.

因此,简而言之,在Java自己的术语中,Java是一个按值传递的值,其中的值可以是:一个实值或一个表示引用的值。

#15


49  

No, it's not pass by reference.

不,它不是通过引用传递的。

Java is pass by value according to the Java Language Specification:

Java是按Java语言规范的值传递的:

When the method or constructor is invoked (§15.12), the values of the actual argument expressions initialize newly created parameter variables, each of the declared type, before execution of the body of the method or constructor. The Identifier that appears in the DeclaratorId may be used as a simple name in the body of the method or constructor to refer to the formal parameter.

当调用方法或构造函数时(15.12),实际的参数表达式的值在方法或构造函数的主体执行之前初始化新创建的参数变量,每一个声明类型。在声明语句中出现的标识符可以作为方法或构造函数的一个简单名称来引用形式参数。

#16


48  

As far as I know, Java only knows call by value. This means for primitive datatypes you will work with an copy and for objects you will work with an copy of the reference to the objects. However I think there are some pitfalls; for example, this will not work:

据我所知,Java只知道按值调用。这意味着对于原始数据类型,您将使用一个副本和对象,您将使用对对象的引用的副本工作。然而,我认为有一些陷阱;例如,这将不起作用:

public static void swap(StringBuffer s1, StringBuffer s2) {
    StringBuffer temp = s1;
    s1 = s2;
    s2 = temp;
}


public static void main(String[] args) {
    StringBuffer s1 = new StringBuffer("Hello");
    StringBuffer s2 = new StringBuffer("World");
    swap(s1, s2);
    System.out.println(s1);
    System.out.println(s2);
}

This will populate Hello World and not World Hello because in the swap function you use copys which have no impact on the references in the main. But if your objects are not immutable you can change it for example:

这将填充Hello World,而不是World Hello,因为在交换函数中,您使用的是copys,它对main中的引用没有影响。但是如果你的对象不是不可变的,你可以改变它,例如:

public static void appendWorld(StringBuffer s1) {
    s1.append(" World");
}

public static void main(String[] args) {
    StringBuffer s = new StringBuffer("Hello");
    appendWorld(s);
    System.out.println(s);
}

This will populate Hello World on the command line. If you change StringBuffer into String it will produce just Hello because String is immutable. For example:

这将在命令行中填充Hello World。如果你将StringBuffer改为String,它会生成Hello,因为String是不可变的。例如:

public static void appendWorld(String s){
    s = s+" World";
}

public static void main(String[] args) {
    String s = new String("Hello");
    appendWorld(s);
    System.out.println(s);
}

However you could make a wrapper for String like this which would make it able to use it with Strings:

但是你可以为字符串做一个包装这样它就能使用字符串:

class StringWrapper {
    public String value;

    public StringWrapper(String value) {
        this.value = value;
    }
}

public static void appendWorld(StringWrapper s){
    s.value = s.value +" World";
}

public static void main(String[] args) {
    StringWrapper s = new StringWrapper("Hello");
    appendWorld(s);
    System.out.println(s.value);
}

edit: i believe this is also the reason to use StringBuffer when it comes to "adding" two Strings because you can modifie the original object which u can't with immutable objects like String is.

编辑:我认为这也是在“添加”两个字符串时使用StringBuffer的原因,因为您可以修改原始对象,而不能使用不可变对象,比如String。

#17


45  

Let me try to explain my understanding with the help of four examples. Java is pass-by-value, and not pass-by-reference

让我用四个例子来解释我的理解。Java是按值传递的,而不是通过引用传递的。

/**

/ * *

Pass By Value

按值传递

In Java, all parameters are passed by value, i.e. assigning a method argument is not visible to the caller.

在Java中,所有参数都是通过值传递的,即分配一个方法参数对调用者来说是不可见的。

*/

* /

Example 1:

示例1:

public class PassByValueString {
    public static void main(String[] args) {
        new PassByValueString().caller();
    }

    public void caller() {
        String value = "Nikhil";
        boolean valueflag = false;
        String output = method(value, valueflag);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'value' and 'valueflag'
         */
        System.out.println("output : " + output);
        System.out.println("value : " + value);
        System.out.println("valueflag : " + valueflag);

    }

    public String method(String value, boolean valueflag) {
        value = "Anand";
        valueflag = true;
        return "output";
    }
}

Result

结果

output : output
value : Nikhil
valueflag : false

Example 2:

示例2:

/** * * Pass By Value * */

/** ** ** **

public class PassByValueNewString {
    public static void main(String[] args) {
        new PassByValueNewString().caller();
    }

    public void caller() {
        String value = new String("Nikhil");
        boolean valueflag = false;
        String output = method(value, valueflag);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'value' and 'valueflag'
         */
        System.out.println("output : " + output);
        System.out.println("value : " + value);
        System.out.println("valueflag : " + valueflag);

    }

    public String method(String value, boolean valueflag) {
        value = "Anand";
        valueflag = true;
        return "output";
    }
}

Result

结果

output : output
value : Nikhil
valueflag : false

Example 3:

示例3:

/** This 'Pass By Value has a feeling of 'Pass By Reference'

/**这个“价值传递”有“通过参考”的感觉

Some people say primitive types and 'String' are 'pass by value' and objects are 'pass by reference'.

有些人说,原始类型和“String”是“通过值传递”,对象是“通过引用传递”。

But from this example, we can understand that it is infact pass by value only, keeping in mind that here we are passing the reference as the value. ie: reference is passed by value. That's why are able to change and still it holds true after the local scope. But we cannot change the actual reference outside the original scope. what that means is demonstrated by next example of PassByValueObjectCase2.

但是从这个例子中,我们可以理解它实际上是通过值传递的,记住这里我们传递的是引用作为值。ie:引用是按值传递的。这就是为什么能够改变,并且在局部范围之后仍然是正确的。但我们不能改变原范围之外的实际引用。下一个PassByValueObjectCase2示例演示了这意味着什么。

*/

* /

public class PassByValueObjectCase1 {

    private class Student {
        int id;
        String name;
        public Student() {
        }
        public Student(int id, String name) {
            super();
            this.id = id;
            this.name = name;
        }
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        @Override
        public String toString() {
            return "Student [id=" + id + ", name=" + name + "]";
        }
    }

    public static void main(String[] args) {
        new PassByValueObjectCase1().caller();
    }

    public void caller() {
        Student student = new Student(10, "Nikhil");
        String output = method(student);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'student'
         */
        System.out.println("output : " + output);
        System.out.println("student : " + student);
    }

    public String method(Student student) {
        student.setName("Anand");
        return "output";
    }
}

Result

结果

output : output
student : Student [id=10, name=Anand]

Example 4:

示例4:

/**

/ * *

In addition to what was mentioned in Example3 (PassByValueObjectCase1.java), we cannot change the actual reference outside the original scope."

除了Example3 (PassByValueObjectCase1.java)中提到的之外,我们不能更改原始范围之外的实际引用。

Note: I am not pasting the code for private class Student. The class definition for Student is same as Example3.

注意:我不是在粘贴私有类学生的代码。学生的阶级定义和考试一样。

*/

* /

public class PassByValueObjectCase2 {

    public static void main(String[] args) {
        new PassByValueObjectCase2().caller();
    }

    public void caller() {
        // student has the actual reference to a Student object created
        // can we change this actual reference outside the local scope? Let's see
        Student student = new Student(10, "Nikhil");
        String output = method(student);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'student'
         */
        System.out.println("output : " + output);
        System.out.println("student : " + student); // Will it print Nikhil or Anand?
    }

    public String method(Student student) {
        student = new Student(20, "Anand");
        return "output";
    }

}

Result

结果

output : output
student : Student [id=10, name=Nikhil]

#18


42  

You can never pass by reference in Java, and one of the ways that is obvious is when you want to return more than one value from a method call. Consider the following bit of code in C++:

您不能在Java中通过引用传递,而显而易见的方法之一是,当您希望从方法调用返回多个值时。考虑一下c++中的以下代码:

void getValues(int& arg1, int& arg2) {
    arg1 = 1;
    arg2 = 2;
}
void caller() {
    int x;
    int y;
    getValues(x, y);
    cout << "Result: " << x << " " << y << endl;
}

Sometimes you want to use the same pattern in Java, but you can't; at least not directly. Instead you could do something like this:

有时候你想在Java中使用相同的模式,但是你不能;至少不是直接。你可以这样做:

void getValues(int[] arg1, int[] arg2) {
    arg1[0] = 1;
    arg2[0] = 2;
}
void caller() {
    int[] x = new int[1];
    int[] y = new int[1];
    getValues(x, y);
    System.out.println("Result: " + x[0] + " " + y[0]);
}

As was explained in previous answers, in Java you're passing a pointer to the array as a value into getValues. That is enough, because the method then modifies the array element, and by convention you're expecting element 0 to contain the return value. Obviously you can do this in other ways, such as structuring your code so this isn't necessary, or constructing a class that can contain the return value or allow it to be set. But the simple pattern available to you in C++ above is not available in Java.

正如前面的答案所解释的那样,在Java中,您将一个指针传递给数组作为getValues的值。这就足够了,因为该方法然后修改了数组元素,并且按照约定,您期望元素0包含返回值。显然,您可以通过其他方式来实现这一点,比如构造代码,这样就没有必要了,或者构造一个可以包含返回值或允许它被设置的类。

#19


41  

I thought I'd contribute this answer to add more details from the Specifications.

我想我应该贡献这个答案来从规范中添加更多的细节。

First, What's the difference between passing by reference vs. passing by value?

首先,通过引用传递和传递值之间有什么区别?

Passing by reference means the called functions' parameter will be the same as the callers' passed argument (not the value, but the identity - the variable itself).

通过引用传递函数的参数将与调用者传递的参数相同(不是值,而是标识-变量本身)。

Pass by value means the called functions' parameter will be a copy of the callers' passed argument.

通过值的传递意味着调用函数的参数将是调用者传递参数的副本。

Or from wikipedia, on the subject of pass-by-reference

或者从*上,以旁证的方式。

In call-by-reference evaluation (also referred to as pass-by-reference), a function receives an implicit reference to a variable used as argument, rather than a copy of its value. This typically means that the function can modify (i.e. assign to) the variable used as argument—something that will be seen by its caller.

在对引用的评价(也称为传递引用)中,函数接收一个隐式引用作为参数的变量,而不是它的值的副本。这通常意味着函数可以修改(即分配给)用作参数的变量,这是它的调用者可以看到的。

And on the subject of pass-by-value

以及关于传递价值的问题。

In call-by-value, the argument expression is evaluated, and the resulting value is bound to the corresponding variable in the function [...]. If the function or procedure is able to assign values to its parameters, only its local copy is assigned [...].

在callby -value中,参数表达式被求值,结果值被绑定到函数[…]中的相应变量。如果函数或过程能够为其参数赋值,则只分配其本地副本[…]。

Second, we need to know what Java uses in its method invocations. The Java Language Specification states

其次,我们需要知道Java在方法调用中使用了什么。Java语言规范声明。

When the method or constructor is invoked (§15.12), the values of the actual argument expressions initialize newly created parameter variables, each of the declared type, before execution of the body of the method or constructor.

当调用方法或构造函数时(15.12),实际的参数表达式的值在方法或构造函数的主体执行之前初始化新创建的参数变量,每一个声明类型。

So it assigns (or binds) the value of the argument to the corresponding parameter variable.

因此,它将参数的值分配给相应的参数变量。

What is the value of the argument?

这个论证的价值是什么?

Let's consider reference types, the Java Virtual Machine Specification states

让我们考虑引用类型,Java虚拟机规范。

There are three kinds of reference types: class types, array types, and interface types. Their values are references to dynamically created class instances, arrays, or class instances or arrays that implement interfaces, respectively.

有三种类型的引用类型:类类型、数组类型和接口类型。它们的值是对动态创建的类实例、数组或实现接口的类实例或数组的引用。

The Java Language Specification also states

Java语言规范也说明了这一点。

The reference values (often just references) are pointers to these objects, and a special null reference, which refers to no object.

引用值(通常只是引用)是指向这些对象的指针,以及一个特殊的空引用,它引用没有对象。

The value of an argument (of some reference type) is a pointer to an object. Note that a variable, an invocation of a method with a reference type return type, and an instance creation expression (new ...) all resolve to a reference type value.

参数(某些引用类型)的值是指向对象的指针。注意,一个变量、一个方法的调用和一个引用类型返回类型,以及一个实例创建表达式(new…)都解析为一个引用类型值。

So

所以

public void method (String param) {}
...
String var = new String("ref");
method(var);
method(var.toString());
method(new String("ref"));

all bind the value of a reference to a String instance to the method's newly created parameter, param. This is exactly what the definition of pass-by-value describes. As such, Java is pass-by-value.

将引用的值绑定到方法的新创建的参数param上。这就是所谓的“按值”的定义。因此,Java是按值传递的。

The fact that you can follow the reference to invoke a method or access a field of the referenced object is completely irrelevant to the conversation. The definition of pass-by-reference was

您可以遵循引用来调用方法或访问引用对象的字段,这与对话完全无关。旁路的定义是。

This typically means that the function can modify (i.e. assign to) the variable used as argument—something that will be seen by its caller.

这通常意味着函数可以修改(即分配给)用作参数的变量,这是它的调用者可以看到的。

In Java, modifying the variable means reassigning it. In Java, if you reassigned the variable within the method, it would go unnoticed to the caller. Modifying the object referenced by the variable is a different concept entirely.

在Java中,修改变量意味着重新分配它。在Java中,如果在方法中重新分配变量,那么调用者就不会注意到它。修改变量引用的对象是完全不同的概念。


Primitive values are also defined in the Java Virtual Machine Specification, here. The value of the type is the corresponding integral or floating point value, encoded appropriately (8, 16, 32, 64, etc. bits).

在这里,Java虚拟机规范中也定义了原始值。类型的值是相应的整数或浮点值,适当地编码(8、16、32、64等位)。

#20


36  

Java is always pass by value, not pass by reference

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

First of all, we need to understand what pass by value and pass by reference are.

首先,我们需要理解什么是通过值传递,然后通过引用传递。

Pass by value means that you are making a copy in memory of the actual parameter's value that is passed in. This is a copy of the contents of the actual parameter.

通过值传递意味着您正在创建一个副本,以记录传入的实际参数的值。这是实际参数的内容的副本。

Pass by reference (also called pass by address) means that a copy of the address of the actual parameter is stored.

通过引用传递(也称为传递通过地址)意味着存储实际参数的地址的副本。

Sometimes Java can give the illusion of pass by reference. Let's see how it works by using the example below:

有时,Java可以通过引用来产生传递的错觉。让我们来看看它是如何工作的:

public class PassByValue {
    public static void main(String[] args) {
        Test t = new Test();
        t.name = "initialvalue";
        new PassByValue().changeValue(t);
        System.out.println(t.name);
    }

    public void changeValue(Test f) {
        f.name = "changevalue";
    }
}

class Test {
    String name;
}

The output of this program is:

这个程序的输出是:

changevalue

Let's understand step by step:

让我们一步一步来理解:

Test t = new Test();

As we all know it will create an object in the heap and return the reference value back to t. For example, suppose the value of t is 0x100234 (we don't know the actual JVM internal value, this is just an example) .

我们都知道,它将在堆中创建一个对象,并将引用值返回给t。

Java是“旁路”还是“按值”?

new PassByValue().changeValue(t);

When passing reference t to the function it will not directly pass the actual reference value of object test, but it will create a copy of t and then pass it to the function. Since it is passing by value, it passes a copy of the variable rather than the actual reference of it. Since we said the value of t was 0x100234, both t and f will have the same value and hence they will point to the same object.

当将引用t传递给函数时,它不会直接传递对象测试的实际引用值,但它会创建一个t的副本,然后将其传递给函数。由于它是通过值传递的,所以它传递的是变量的副本,而不是它的实际引用。因为我们说t的值是0x100234,所以t和f都有相同的值,因此它们会指向同一个对象。

Java是“旁路”还是“按值”?

If you change anything in the function using reference f it will modify the existing contents of the object. That is why we got the output changevalue, which is updated in the function.

如果使用引用f改变函数中的任何内容,它将修改对象的现有内容。这就是为什么我们得到了输出changevalue,它是在函数中更新的。

To understand this more clearly, consider the following example:

为了更清楚地理解这一点,请考虑下面的例子:

public class PassByValue {
    public static void main(String[] args) {
        Test t = new Test();
        t.name = "initialvalue";
        new PassByValue().changeRefence(t);
        System.out.println(t.name);
    }

    public void changeRefence(Test f) {
        f = null;
    }
}

class Test {
    String name;
}

Will this throw a NullPointerException? No, because it only passes a copy of the reference. In the case of passing by reference, it could have thrown a NullPointerException, as seen below:

这会抛出NullPointerException吗?不,因为它只传递引用的副本。在通过引用的情况下,它可以抛出一个NullPointerException,如下所示:

Java是“旁路”还是“按值”?

Hopefully this will help.

希望这将帮助。

#21


34  

Java is a call by value.

Java是一个值调用。

How it works.

它是如何工作的。

  • You always pass a copy of the bits of the value of the reference!

    您总是通过一个副本的值的引用!

  • If it's a primitive data type these bits contain the value of the primitive data type itself, That's why if we change the value of header inside the method then it does not reflect the changes outside.

    如果是原始数据类型,这些位包含原始数据类型本身的值,这就是为什么如果我们在方法中更改header的值,那么它不会反映外部的变化。

  • If it's an object data type like Foo foo=new Foo() then in this case copy of the address of the object passes like file shortcut , suppose we have a text file abc.txt at C:\desktop and suppose we make shortcut of the same file and put this inside C:\desktop\abc-shortcut so when you access the file from C:\desktop\abc.txt and write 'Stack Overflow' and close the file and again you open the file from shortcut then you write ' is the largest online community for programmers to learn' then total file change will be 'Stack Overflow is the largest online community for programmers to learn' which means it doesn't matter from where you open the file , each time we were accessing the same file , here we can assume Foo as a file and suppose foo stored at 123hd7h(original address like C:\desktop\abc.txt ) address and 234jdid(copied address like C:\desktop\abc-shortcut which actually contains the original address of the file inside) .. So for better understanding make shortcut file and feel...

    如果它是一个对象数据类型,比如Foo Foo =new Foo(),那么在这个例子中,对象地址的副本就像文件快捷方式一样传递,假设我们有一个文本文件abc。txt在C:\desktop,假设我们在同一个文件中抄近路,并把它放在C:\desktop\ C -快捷方式中,所以当你从C:\desktop\abc访问文件时。txt和写“Stack Overflow”并关闭文件再一次你打开文件快捷方式然后你写的是最大的在线社区为程序员学习”然后总文件变化将是堆栈溢出是最大的网上社区程序员学习”这意味着不管从你打开文件,每一次我们都访问同一个文件,在这里我们可以假设Foo作为一个文件,并假设Foo储存在123 hd7h(原地址像C:\桌面\ abc。地址和234jdid(复制地址如C:\desktop\abc-快捷方式,实际上包含文件的原始地址)..所以为了更好的理解,制作快捷文件和感觉…

#22


33  

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中,除了原语以外的任何东西)都是引用。这些引用是按值传递的。

#23


28  

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的引用只会在本地范围内发生变化,而不是在此方法之外。

#24


28  

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

name = Maxx的名字=狗

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

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

#25


25  

Java has only pass by value. A very simple example to validate this.

Java只通过值传递。一个非常简单的例子来验证这一点。

public void test() {
    MyClass obj = null;
    init(obj);
    //After calling init method, obj still points to null
    //this is because obj is passed as value and not as reference.
}
private void init(MyClass objVar) {
    objVar = new MyClass();
}

#26


25  

In Java only references are passed and are passed by value:

在Java中,只有引用被传递并通过值传递:

Java arguments are all passed by value (the reference is copied when used by the method) :

Java参数都是通过值传递的(当方法使用时,引用被复制):

In the case of primitive types, Java behaviour is simple: The value is copied in another instance of the primitive type.

在基本类型的情况下,Java行为很简单:值是在原始类型的另一个实例中复制的。

In case of Objects, this is the same: Object variables are pointers (buckets) holding only Object’s address that was created using the "new" keyword, and are copied like primitive types.

在对象的情况下,这是相同的:对象变量是指针(bucket),只持有用“new”关键字创建的对象的地址,并且被复制为原始类型。

The behaviour can appear different from primitive types: Because the copied object-variable contains the same address (to the same Object) Object's content/members might still be modified within a method and later access outside, giving the illusion that the (containing) Object itself was passed by reference.

这种行为可能与原始类型不同:因为复制的对象变量包含相同的地址(对同一个对象),对象的内容/成员可能仍然会在方法中被修改,然后在外部访问,这就产生了一个错觉,认为(包含)对象本身是通过引用传递的。

"String" Objects appear to be a perfect counter-example to the urban legend saying that "Objects are passed by reference":

“String”对象似乎是都市传说中的一个完美的反例,它说“对象通过引用传递”:

In effect, within a method you will never be able, to update the value of a String passed as argument:

实际上,在一个方法中,您将永远无法更新作为参数传递的字符串的值:

A String Object, holds characters by an array declared final that can't be modified. Only the address of the Object might be replaced by another using "new". Using "new" to update the variable, will not let the Object be accessed from outside, since the variable was initially passed by value and copied.

一个字符串对象,由一个被声明为无法修改的数组中的字符来保存字符。只有对象的地址可以用“new”替换。使用“new”来更新变量,将不会让对象从外部访问,因为变量最初是通过值传递并复制的。

#27


23  

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

我已经为这里的任何编程语言创建了一个用于此类问题的线程。

Java is also mentioned. Here is the short summary:

Java也提到过。以下是简短的总结:

  • Java passes it parameters by value
  • Java通过值传递参数。
  • "by value" is the only way in java to pass a parameter to a method
  • “按值”是java中唯一将参数传递给方法的方法。
  • 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)
  • 使用对象的方法作为参数将会改变对象,因为引用指向原始对象。(如果该方法本身改变了一些值)

#28


23  

To make a long story short, Java objects 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具有直接通过值传递的基本类型(int、bool、char、double等)。然后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)是一个函数,它不需要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 a should 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++,我们有参考资料。引用基本上(在这种情况下)语法sugar隐藏了等式的指针部分:void foo(int &x)被foo(a)调用,编译器本身知道它是一个引用,而非引用a的地址应该被传递。在Java中,引用对象的所有变量实际上都是引用类型,实际上是在没有细粒度控制(和复杂性)的情况下对大多数意图和目的进行调用,例如c++。

#29


22  

A few corrections to some posts.

对一些帖子做了一些修改。

C does NOT support pass by reference. It is ALWAYS pass by value. C++ does support pass by reference, but is not the default and is quite dangerous.

C不支持通过引用传递。它总是通过价值传递。c++支持通过引用传递,但不是默认值,而且非常危险。

It doesn't matter what the value is in Java: primitive or address(roughly) of object, it is ALWAYS passed by value.

在Java中值是什么并不重要:原始或地址(大致),它总是通过值传递。

If a Java object "behaves" like it is being passed by reference, that is a property of mutability and has absolutely nothing to do with passing mechanisms.

如果一个Java对象“行为”就像它被引用传递一样,那么它就是一个可变性的属性,与传递机制完全没有关系。

I am not sure why this is so confusing, perhaps because so many Java "programmers" are not formally trained, and thus do not understand what is really going on in memory?

我不知道为什么这是如此令人困惑,也许是因为许多Java“程序员”没有经过正式的培训,因此不理解内存中到底发生了什么?

#30


17  

Java copies the reference by value. So if you change it to something else (e.g, using new) the reference does not change outside the method. For native types, it is always pass by value.

Java通过值复制引用。所以如果你把它换成别的东西(e)g,使用新的)引用在方法之外不会改变。对于本机类型,它总是按值传递。