Java 是“按引用传递”吗?或“按值传递”?

发布于 2025-01-15 02:07:19 字数 202 浏览 4 评论 0原文

我一直认为Java使用按引用传递。然而,我读了一篇博客文章,其中声称Java使用按值传递< /强>。我认为我不明白作者所做的区分。

解释是什么?

I always thought Java uses pass-by-reference. However, I read a blog post which claims that Java uses pass-by-value. I don't think I understand the distinction the author is making.

What is the explanation?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

蓝天 2025-01-22 02:07:19

术语“按值传递”和“按引用传递”有特殊的 精确定义计算机科学中的含义。这些含义与许多人第一次听到这些术语时的直觉不同。这次讨论中的大部分混乱似乎都来自这个事实。

术语“按值传递”和“按引用传递”谈论的是变量。按值传递意味着传递变量的到一个函数/方法。引用传递意味着对该变量的引用被传递给函数。后者为函数提供了更改变量内容的方法。

根据这些定义,Java 始终是按值传递。不幸的是,当我们处理保存对象的变量时,我们实际上是在处理称为引用的对象句柄,它们也是按值传递的。这种术语和语义很容易让许多初学者感到困惑。

它是这样的:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    Dog oldDog = aDog;

    // we pass the object to foo
    foo(aDog);
    // aDog variable is still pointing to the "Max" dog when foo(...) returns
    aDog.getName().equals("Max"); // true
    aDog.getName().equals("Fifi"); // false
    aDog == oldDog; // true
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // change d inside of foo() to point to a new Dog instance construct red with name member variable set to "Fifi"
    d = new Dog("Fifi");
    d.getName().equals("Fifi"); // true
}

在此示例中,aDog.getName() 仍将返回 "Max"。通过创建名称成员变量设置为的新 Dogmain 中的值 aDog 不会在函数 foo 中更改"Fifi" 因为对象引用是按值传递的。如果对象引用是通过引用传递的,则 main 中的 aDog.getName() 在调用 后将返回 "Fifi" >foo.

同样:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    Dog oldDog = aDog;

    foo(aDog);
    // when foo(...) returns, the name of the dog has been changed to "Fifi"
    aDog.getName().equals("Fifi"); // true
    // but it is still the same dog:
    aDog == oldDog; // true
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // this changes the name of d to be "Fifi"
    d.setName("Fifi");
}

在此示例中,Fifi 是调用 foo(aDog) 后狗的名称,因为对象的名称是在 foo(...) 内部设置的代码>. food 执行的任何操作,出于所有实际目的,它们都是在 aDog 上执行的,但它不是可以更改变量aDog本身的值。

有关按引用传递和按值传递的更多信息,请参阅以下答案:https://stackoverflow.com/a/430958/6005228 。这更彻底地解释了两者背后的语义和历史,也解释了为什么 Java 和许多其他现代语言在某些情况下似乎同时执行这两种操作。

The terms "pass-by-value" and "pass-by-reference" have special, precisely defined meanings in computer science. These meanings differ from the intuition many people have when first hearing the terms. Much of the confusion in this discussion seems to come from this fact.

The terms "pass-by-value" and "pass-by-reference" are talking about variables. Pass-by-value means that the value of a variable is passed to a function/method. Pass-by-reference means that a reference to that variable is passed to the function. The latter gives the function a way to change the contents of the variable.

By those definitions, Java is always pass-by-value. Unfortunately, when we deal with variables holding objects we are really dealing with object-handles called references which are passed-by-value as well. This terminology and semantics easily confuse many beginners.

It goes like this:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    Dog oldDog = aDog;

    // we pass the object to foo
    foo(aDog);
    // aDog variable is still pointing to the "Max" dog when foo(...) returns
    aDog.getName().equals("Max"); // true
    aDog.getName().equals("Fifi"); // false
    aDog == oldDog; // true
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // change d inside of foo() to point to a new Dog instance construct red with name member variable set to "Fifi"
    d = new Dog("Fifi");
    d.getName().equals("Fifi"); // true
}

In this example, aDog.getName() will still return "Max". The value aDog within main is not changed in the function foo by creating new Dog with name member variable set to "Fifi" because the object reference is passed by value. If the object reference was passed by reference, then the aDog.getName() in main would return "Fifi" after the call to foo.

Likewise:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    Dog oldDog = aDog;

    foo(aDog);
    // when foo(...) returns, the name of the dog has been changed to "Fifi"
    aDog.getName().equals("Fifi"); // true
    // but it is still the same dog:
    aDog == oldDog; // true
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // this changes the name of d to be "Fifi"
    d.setName("Fifi");
}

In this example, Fifi is 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, but it is not possible to change the value of the variable aDog itself.

For more information on pass by reference and pass by value, consult the following answer: https://stackoverflow.com/a/430958/6005228. This explains more thoroughly the semantics and history behind the two and also explains why Java and many other modern languages appear to do both in certain cases.

白况 2025-01-22 02:07:19

我刚刚注意到您引用了我的文章

Java 规范规定 Java 中的所有内容都是按值传递的。 Java 中不存在“按引用传递”这样的东西。

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

Dog myDog;

不是狗;它实际上是一个指向 Dog 的指针。 Java 中术语“引用”的使用非常具有误导性,也是造成这里大部分混乱的原因。他们所谓的“引用”的行为/感觉更像是我们在大多数其他语言中所说的“指针”。

这意味着,当您创建时,

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

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

(我说本质上是因为 Java 指针/引用不是直接地址,但这样考虑它们是最容易的。)

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

如果方法被定义为

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

让我们看看发生了什么。

  • 参数 someDog
  • 在“AAA”行 设置为值 42
    • someDog 后面是它指向的 Dog(地址 42 处的 Dog 对象)
    • 那只(地址为 42 的那只)被要求将他的名字改为 Max
  • 在“BBB”行将 他的名字更改为 Max
    • 创建了一个新的。假设他位于地址 74
    • 我们将参数 someDog 分配给 74
  • “CCC”行的 74
    • someDog 跟随它指向的 Dog(地址 74 处的 Dog 对象)
    • 那只(地址为 74 的那只)被要求将他的名字改为 Rowlf
  • 然后,我们返回

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

myDog 改变了吗?

这就是关键。

请记住,myDog 是一个指针,而不是实际的Dog,答案是否定的。 myDog 的值仍然是 42;它仍然指向原来的 Dog (但请注意,由于行“AAA”,它的名称现在是“Max” - 仍然是同一个 Dog;myDog 的值已未更改。)

跟随一个地址并更改其末尾的内容是完全有效的;然而,这并没有改变变量。

Java 的工作方式与 C 完全相同。您可以分配一个指针,将指针传递给方法,跟随方法中的指针并更改所指向的数据。但是,调用者不会看到您对该指针指向的位置所做的任何更改。 (在具有传递引用语义的语言中,方法函数可以更改指针,并且调用者将看到该更改。)

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

如果 Java 具有按引用传递语义,则我们上面定义的 foo 方法在在线分配 someDog 时会更改 myDog 所指向的位置BBB。

将引用参数视为传入变量的别名。分配该别名后,传入的变量也是如此。

更新

注释中的讨论需要一些澄清...

在 C 中,您可以写

void swap(int *x, int *y) {
    int t = *x;
    *x = *y;
    *y = t;
}

int x = 1;
int y = 2;
swap(&x, &y);

这不是一个C 中的特殊情况。两种语言都使用按值传递语义。这里,调用站点正在创建额外的数据结构来协助函数访问和操作数据。

该函数将传递指向数据的指针,并遵循这些指针来访问和修改该数据。

Java 中的类似方法(调用者设置辅助结构)可能是:(

void swap(int[] x, int[] y) {
    int temp = x[0];
    x[0] = y[0];
    y[0] = temp;
}

int[] x = {1};
int[] y = {2};
swap(x, y);

或者如果您希望两个示例都演示其他语言没有的功能,请创建一个可变的 IntWrapper 类来代替数组

)在这种情况下,C 和 Java 都模拟引用传递。它们仍然传递值(指向整数或数组的指针),并在被调用函数内跟随这些指针来操作数据。

引用传递是关于函数声明/定义以及它如何处理其参数的。引用语义适用于对该函数的每个调用,并且调用站点只需要传递变量,不需要额外的数据结构。

这些模拟需要调用站点和函数进行配合。毫无疑问它很有用,但它仍然是按值传递的。

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.

The key to understanding this is that something like

Dog myDog;

is not a Dog; it's actually a pointer to a Dog. The use of the term "reference" in Java is very misleading and is what causes most of the confusion here. What they call "references" act/feel more like what we'd call "pointers" in most other languages.

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.

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

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

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
  • at line "AAA"
    • someDog is followed to the Dog it points to (the Dog object at address 42)
    • that Dog (the one at address 42) is asked to change his name to Max
  • at line "BBB"
    • a new Dog is created. Let's say he's at address 74
    • we assign the parameter someDog to 74
  • at line "CCC"
    • someDog is followed to the Dog it points to (the Dog object at address 74)
    • that Dog (the one at address 74) is asked to change his name to Rowlf
  • then, we return

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

Did myDog change?

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.)

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, the caller will not see any changes you make to where that pointer points. (In a language with pass-by-reference semantics, the method function can change the pointer and the caller will see that change.)

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

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.

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.

Update

A discussion in the comments warrants some clarification...

In C, you can write

void swap(int *x, int *y) {
    int t = *x;
    *x = *y;
    *y = t;
}

int x = 1;
int y = 2;
swap(&x, &y);

This is not a special case in C. Both languages use pass-by-value semantics. Here the call site is creating additional data structure to assist the function to access and manipulate data.

The function is being passed pointers to data, and follows those pointers to access and modify that data.

A similar approach in Java, where the caller sets up assisting structure, might be:

void swap(int[] x, int[] y) {
    int temp = x[0];
    x[0] = y[0];
    y[0] = temp;
}

int[] x = {1};
int[] y = {2};
swap(x, y);

(or if you wanted both examples to demonstrate features the other language doesn't have, create a mutable IntWrapper class to use in place of the arrays)

In these cases, both C and Java are simulating pass-by-reference. They're still both passing values (pointers to ints or arrays), and following those pointers inside the called function to manipulate the data.

Pass-by-reference is all about the function declaration/definition, and how it handles its parameters. Reference semantics apply to every call to that function, and the call site only needs to pass variables, no additional data structure.

These simulations require the call site and the function to cooperate. No doubt it's useful, but it's still pass-by-value.

苏别ゝ 2025-01-22 02:07:19

Java 总是按值传递参数,而不是按引用传递参数。


让我通过一个示例来解释这一点:

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

}

我将分步骤解释这一点:

  1. 声明一个名为f的引用 类型为 Foo 的对象,并为其分配一个类型为 Foo 且属性为 “f” 的新对象。

    Foo f = new Foo("f");
    

    在此处输入图像描述

  2. 从方法端,带有 Foo 类型的引用name a 已声明,并且最初分配为 null

    public static void changeReference(Foo a)
    

    在此处输入图像描述

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

    changeReference(f);
    

    在此处输入图像描述

  4. 声明一个名为 b 且类型为 Foo< 的引用/code> 并为其分配一个 Foo 类型的新对象,其属性为 "b"

    Foo b = new Foo("b");
    

    在此处输入图像描述

  5. a = b 对参考进行新分配 a不是f,属性为“b”的对象。

    在此处输入图像描述

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

    在此处输入图像描述

  7. c.setAttribute("c"); 将更改引用c 指向它的对象的属性,与引用f 指向它的对象是同一个对象。

    在此处输入图像描述

Java always passes arguments by value, NOT by reference.


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 a new object of type Foo with an attribute "f".

    Foo f = new Foo("f");
    

    enter image description here

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

    public static void changeReference(Foo a)
    

    enter image description here

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

    changeReference(f);
    

    enter image description here

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

    Foo b = new Foo("b");
    

    enter image description here

  5. a = b makes a new assignment to the reference a, not f, of the object whose attribute is "b".

    enter image description here

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

    enter image description here

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

    enter image description here

还给你自由 2025-01-22 02:07:19

Java 总是按值传递,永远无一例外。

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

因此,当调用方法时

  • 对于原始参数 (int, long,等),传递的值是原语的实际值(例如,3)。
  • 对于对象,传递值是对象引用的值。

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

当然,按值传递对对象的引用看起来非常类似于(并且在实践中与按引用传递对象没有区别)。

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

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.

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).
  • 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.

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

西瓜 2025-01-22 02:07:19

这将使您对 Java 的真正工作原理有一些了解,以至于在您下次讨论 Java 按引用传递或按值传递时,您只会微笑:-)

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

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

  • 学生:大师,这是否意味着Java是按引用传递的?
  • 大师:Grasshopper,不。

现在想想对象的引用/变量的作用:

  1. 变量保存的位告诉 JVM 如何获取内存(堆)中引用的对象.
  2. 将参数传递给方法时您传递的不是引用变量,而是引用变量中的位的副本。像这样的:3bad086a。 3bad086a 代表一种获取传递对象的方法。
  3. 所以你只是传递 3bad086a ,它是引用的值。
  4. 您传递的是引用的值,而不是引用本身(而不是对象)。
  5. 该值实际上被复制并赋予该方法

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

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. }

会发生什么?

  • 变量 person 在第 #1 行中创建,并且在开始时为 null。
  • 在第 #2 行中创建了一个新的 Person 对象,并将其存储在内存中,并为变量 person 提供了对 Person 对象的引用。即它的地址。比方说 3bad086a。
  • 保存对象地址的变量person被传递给第3行的函数。
  • 在第 #4 行中,您可以听到寂静的声音
  • 检查第 #5 行上的注释
  • 创建了一个方法局部变量 -anotherReferenceToTheSamePersonObject-,然后在第 #6 行中出现了魔法:
    • 变量/引用person被逐位复制并传递给函数内的anotherReferenceToTheSamePersonObject
    • 不会创建新的 Person 实例。
    • person”和“anotherReferenceToTheSamePersonObject”都具有相同的值 3bad086a。
    • 不要尝试这个,但 person==anotherReferenceToTheSamePersonObject 将为 true。
    • 两个变量都有相同的引用副本,并且它们都引用同一个 Person 对象,即堆上的相同对象,而不是副本。

一张图片胜过一千个单词:

Pass by Value

请注意,anotherReferenceToTheSamePersonObject 箭头指向对象,而不是指向对象面向变量 person!

如果您不明白,那么请相信我并记住,最好说Java 是按值传递的。好吧,传递参考值。哦,更好的是通过变量值的副本传递! ;)

现在请随意恨我,但请注意,考虑到这一点,在谈论方法参数时,传递原始数据类型和对象之间没有区别

您总是传递引用值的位副本!

  • 如果它是原始数据类型,这些位将包含原始数据类型本身的值。
  • 如果它是一个对象,这些位将包含告诉 JVM 如何访问该对象的地址值。

Java 是按值传递的,因为在方法内部,您可以根据需要修改引用的对象,但无论您多么努力,您都永远无法修改将继续引用的传递变量(不是 p _ _ _ _ _ _ _) 无论如何都是同一个对象!


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


当然,你可以简单地说,Java 是按值传递的!

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 :-)

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.

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?
  • 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. 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.
  3. So you're just passing 3bad086a that it's the value of the reference.
  4. You're passing the value of the reference and not the reference itself (and not the object).
  5. This value is actually COPIED and given to the method.

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.
  • 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.
  • The variable person holding the address of the Object is passed to the function in line #3.
  • In line #4 you can listen to the sound of silence
  • Check the comment on line #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.
    • Both "person" and "anotherReferenceToTheSamePersonObject" hold the same value of 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.

A picture is worth a thousand words:

Pass by Value

Note that the anotherReferenceToTheSamePersonObject arrows is directed towards the Object and not towards the variable 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! ;)

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.

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!


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.


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

如何视而不见 2025-01-22 02:07:19

Java 按值传递引用。

因此您无法更改传入的引用。

Java passes references by value.

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

野味少女 2025-01-22 02:07:19

我觉得争论按引用传递按值传递并没有多大帮助。

如果您说 Java 是pass-by-whatever,那么您并没有提供完整的答案。以下是一些附加信息,希望可以帮助您了解内存中实际发生的情况。

在我们开始 Java 实现之前,我们先来了解一下堆栈/堆的速成课程:
值以良好有序的方式在堆栈中上下移动,就像自助餐厅里的一堆盘子一样。
堆中的内存(也称为动态内存)是随意且无组织的。 JVM 会尽可能地寻找空间,并在不再需要使用该空间的变量时将其释放。

好的。首先,局部原语进入堆栈。所以这段代码:

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

结果是:

primitives on the stack

当你声明并实例化一个对象时。实际的对象位于堆上。堆栈上有什么?对象在堆上的地址。 C++ 程序员将其称为指针,但一些 Java 开发人员反对使用“指针”这个词。任何。只需知道对象的地址位于堆栈中即可。

就像这样:

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

ab*7ch aint one!

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

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

marx brothers

那么,当您调用方法时会传入什么?如果您传入一个对象,那么您实际传入的是该对象的地址。有些人可能会说地址的“值”,有些人说它只是对对象的引用。这就是“参考”和“价值”支持者之间圣战的起源。你怎么称呼它并不重要,重要的是你明白传入的是对象的地址。

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

创建一个字符串并在堆中为其分配空间,并且该字符串的地址存储在堆栈中并给出标识符 hisName,因为第二个字符串的地址与第一个字符串的地址相同首先,没有创建新的String,也没有分配新的堆空间,但在堆栈上创建了一个新的标识符。然后我们调用 shout():创建一个新的堆栈帧并创建一个新标识符 name 并分配已存在字符串的地址。

la da di da da da da da

那么,价值、参考?你说的是“土豆”。

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

If you say that Java is pass-by-whatever, you are not providing a complete answer. Here is some additional information that will hopefully help you understand what actually happens in memory.

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.

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:

primitives on the stack

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.

Like so:

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

a b*7ch aint one!

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

marx brothers

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.

la da di da da da da

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

忆伤 2025-01-22 02:07:19

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

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

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

将打印出“Hah!”而不是null。之所以有效,是因为 barbaz 值的副本,它只是对 "Hah!" 的引用。如果它是实际引用本身,则 foo 会将 baz 重新定义为 null

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

private static 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.

深陷 2025-01-22 02:07:19

为了显示对比,请比较以下 C++Java 片段:

在 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"
}

在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只有两种类型的传递:内置类型的值传递和对象类型的指针值传递。

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

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

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,

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.

眉目亦如画i 2025-01-22 02:07:19

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

Java passes references to objects by value.

把梦留给海 2025-01-22 02:07:19

我不敢相信还没有人提到芭芭拉·利斯科夫。当她在 1974 年设计 CLU 时,遇到了同样的术语问题,并发明了术语“共享调用”(也称为“对象共享调用”和“共享调用”)按对象调用)用于“按值调用,其中值是引用”的特定情况。

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".

扬花落满肩 2025-01-22 02:07:19

问题的关键在于“按引用传递”表达式中的“引用”一词与 Java 中“引用”一词的通常含义完全不同。

通常在 Java 中引用表示对对象的引用。但是编程语言理论中的技术术语“按引用/值传递”是指对保存变量的存储单元的引用,这是完全不同的东西。

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.

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.

演多会厌 2025-01-22 02:07:19

已经有很好的答案涵盖了这一点。我想通过分享一个非常简单的示例(将编译)来做出一点贡献,对比 C++ 中的按引用传递和 Java 中的按值传递之间的行为。

几点:

  1. “参考”一词具有两种不同的含义。在 Java 中,它仅表示一个指针,但在“按引用传递”的上下文中,它表示传入的原始变量的句柄。
  2. Java 是按值传递。 Java 是 C(以及其他语言)的后代。在 C 之前,几种(但不是全部)早期语言,如 FORTRAN 和 COBOL 支持 PBR,但 C 不支持。 PBR 允许这些其他语言对子例程内传递的变量进行更改。为了完成同样的事情(即更改函数内变量的值),C 程序员将指向变量的指针传递给函数。受 C 启发的语言,例如 Java,借用了这个思想,并继续像 C 那样将指针传递给方法,只不过 Java 将其指针称为“引用”。同样,“引用”一词的用法与“按引用传递”不同。
  3. C++ 允许通过引用传递,通过使用“&”声明引用参数字符(恰好与 C 和 C++ 中用于指示“变量的地址”的字符相同)。例如,如果我们通过引用传入一个指针,则形参和实参不只是指向同一个对象。相反,它们是相同的变量。如果其中一个地址被设置为不同的地址或为空,则另一个地址也会被设置为空。
  4. 在下面的 C++ 示例中,我通过引用将指针传递给以 null 结尾的字符串。在下面的 Java 示例中,我按值传递对 String 的 Java 引用(同样,与指向 String 的指针相同)。请注意注释中的输出。

C++ 按引用传递示例:

using namespace std;
#include <iostream>

void change (char *&str){   // the '&' makes this a reference parameter
    str = NULL;
}

int main()
{
    char *str = "not Null";
    change(str);
    cout<<"str is " << str;      // ==>str is <null>
}

Java 按值传递“Java 引用”示例

public class ValueDemo{
    
    public void change (String str){
        str = null;
    }

     public static void main(String []args){
        ValueDemo vd = new ValueDemo();
        String str = "not null";
        vd.change(str);
        System.out.println("str is " + str);    // ==> str is not null!!
                                                // Note that if "str" was
                                                // passed-by-reference, it
                                                // WOULD BE NULL after the
                                                // call to change().
     }
}

编辑

有几个人写了评论,这些评论似乎表明他们要么没有看我的示例,要么没有了解 c++例子。不确定断开连接在哪里,但猜测 c++ 示例并不清楚。我在 pascal 中发布了相同的示例,因为我认为通过引用在 pascal 中看起来更干净,但我可能是错的。我可能只是让人们更加困惑;我希望不会。

在 pascal 中,按引用传递的参数称为“var 参数”。在下面的 setToNil 过程中,请注意参数“ptr”之前的关键字“var”。当指针传递到此过程时,它将通过引用传递。请注意行为:当此过程将 ptr 设置为 nil(即 pascal 中的 NULL)时,它将把参数设置为 nil —— 在 Java 中不能这样做。

program passByRefDemo;
type 
   iptr = ^integer;
var
   ptr: iptr;
   
   procedure setToNil(var ptr : iptr);
   begin
       ptr := nil;
   end;

begin
   new(ptr);
   ptr^ := 10;
   setToNil(ptr);
   if (ptr = nil) then
       writeln('ptr seems to be nil');     { ptr should be nil, so this line will run. }
end.

编辑 2

一些摘录自 Ken Arnold、James Gosling(Java 的发明者) 和 David Holmes 所著的 “Java 编程语言”,第 2 章第 2.6.5 节

方法的所有参数均“按值”传递。换句话说,
方法中参数变量的值是调用者的副本
指定为参数。

他接着对物体提出了同样的观点。 。 。

需要注意的是,当参数是对象引用时,它是
对象引用(而不是对象本身)“按值”传递

在同一节的末尾,他对 java 只按值传递而从不按引用传递进行了更广泛的声明。

Java 编程语言不通过引用传递对象;它
按值传递对象引用。因为两份相同的
引用引用同一个实际对象,通过一个进行更改
引用变量通过其他变量可见。恰好有一个
参数传递模式 - 按值传递 - 这有助于保持事物
简单。


本书的这一部分对 Java 中的参数传递以及按引用传递和按值传递之间的区别进行了很好的解释,并且由 Java 的创建者撰写。我鼓励任何人阅读它,特别是如果你仍然不相信的话。

我认为这两个模型之间的差异非常微妙,除非您在实际使用传递引用的情况下进行了编程,否则很容易错过两个模型的不同之处。

我希望这能解决争论,但可能不会。

编辑3

我可能对这篇文章有点着迷。可能是因为我觉得 Java 的开发者无意中传播了错误信息。如果他们没有使用“引用”这个词来表示指针,而是使用了其他东西,比如
dingleberry,那就没有问题了。你可以说,“Java 通过值传递 dingleberry,而不是通过引用”,没有人会感到困惑。

这就是只有 Java 开发人员对此有疑问的原因。他们看着“参考”这个词,就认为自己确切地知道这意味着什么,所以他们甚至懒得去考虑相反的论点。

不管怎样,我注意到一篇旧帖子中的一条评论,它做了一个我非常喜欢的气球类比。以至于我决定将一些剪贴画粘在一起制作一组漫画来说明这一点。

按值传递引用——对引用的更改不会反映在调用者的作用域中,但对对象的更改会反映在调用者的作用域中。这是因为引用被复制,但原始对象和副本都引用同一个对象。
传递对象references By Value

通过引用传递——没有引用的副本。单个引用由调用者和被调用的函数共享。对引用或对象数据的任何更改都会反映在调用者的范围中。
经过参考

编辑4

我看过关于这个主题的帖子,其中描述了Java中参数传递的低级实现,我认为这很棒并且非常有帮助,因为它使抽象的想法具体化。然而,对我来说,问题更多的是关于语言规范中描述的行为,而不是关于行为的技术实现。这是摘自 Java 语言规范,第 8.4.1 节

当调用方法或构造函数时(第 15.12 节),
实参表达式初始化新创建的参数
变量,每个声明的类型,在执行主体之前
方法或构造函数。
出现在
DeclaratorId 可以用作方法主体中的简单名称,或者
构造函数引用形式参数。

这意味着,java 在执行方法之前创建传递参数的副本。和大多数在大学学习编译器的人一样,我使用了“龙之书”这是编译器书。第 1 章中对“Call-by-value”和“Call-by-Reference”有很好的描述。Call-by-value 的描述与 Java 规范完全匹配。

当我在 90 年代研究编译器时,我使用了 1986 年这本书的第一版,它比 ​​Java 早了大约 9 或 10 年。然而,我刚刚发现了 2007 年的第二版的副本 实际上提到了 Java! 标有“参数传递机制”的 1.6.6 节很好地描述了参数传递。以下是“按值调用”标题下的摘录,其中提到了 Java:

在按值调用中,将计算实际参数(如果它是一个
表达式)或复制(如果它是变量)。该值被放置在
属于相应形式参数的位置
称为程序。 该方法用于C和Java中,是一种常见的方法
C++ 以及大多数其他语言中的选项。

编辑 5

刚刚向副驾驶询问了以下问题:

stackoverflow上有一些人断言Java是合格的
通过参考语言。为什么要告诉他们他们是多么侮辱人
都是错误的。

我得到的答案是:

啊,关于 Java 的值传递性质的争论肯定会激起一些人的兴趣。
热烈响应!虽然保持讨论总是最好的
建设性的,如果您正在寻找一种聪明的方法来纠正
误解,你可以这样说:

“Java 是按引用传递的,就像全息图是固体对象一样。它们是
将引用的副本与引用本身混淆。

There are already great answers that cover this. I wanted to make a small contribution by sharing a very simple example (which will compile) contrasting the behaviors between Pass-by-reference in c++ and Pass-by-value in Java.

A few points:

  1. The term "reference" is a overloaded with two separate meanings. In Java it simply means a pointer, but in the context of "Pass-by-reference" it means a handle to the original variable which was passed in.
  2. Java is Pass-by-value. Java is a descendent of C (among other languages). Before C, several (but not all) earlier languages like FORTRAN and COBOL supported PBR, but C did not. PBR allowed these other languages to make changes to the passed variables inside sub-routines. In order to accomplish the same thing (i.e. change the values of variables inside functions), C programmers passed pointers to variables into functions. Languages inspired by C, such as Java, borrowed this idea and continue to pass pointer to methods as C did, except that Java calls its pointers References. Again, this is a different use of the word "Reference" than in "Pass-By-Reference".
  3. C++ allows Pass-by-reference by declaring a reference parameter using the "&" character (which happens to be the same character used to indicate "the address of a variable" in both C and C++). For example, if we pass in a pointer by reference, the parameter and the argument are not just pointing to the same object. Rather, they are the same variable. If one gets set to a different address or to null, so does the other.
  4. In the C++ example below I'm passing a pointer to a null terminated string by reference. And in the Java example below I'm passing a Java reference to a String (again, the same as a pointer to a String) by value. Notice the output in the comments.

C++ pass by reference example:

using namespace std;
#include <iostream>

void change (char *&str){   // the '&' makes this a reference parameter
    str = NULL;
}

int main()
{
    char *str = "not Null";
    change(str);
    cout<<"str is " << str;      // ==>str is <null>
}

Java pass "a Java reference" by value example

public class ValueDemo{
    
    public void change (String str){
        str = null;
    }

     public static void main(String []args){
        ValueDemo vd = new ValueDemo();
        String str = "not null";
        vd.change(str);
        System.out.println("str is " + str);    // ==> str is not null!!
                                                // Note that if "str" was
                                                // passed-by-reference, it
                                                // WOULD BE NULL after the
                                                // call to change().
     }
}

EDIT

Several people have written comments which seem to indicate that either they are not looking at my examples or they don't get the c++ example. Not sure where the disconnect is, but guessing the c++ example is not clear. I'm posting the same example in pascal because I think pass-by-reference looks cleaner in pascal, but I could be wrong. I might just be confusing people more; I hope not.

In pascal, parameters passed-by-reference are called "var parameters". In the procedure setToNil below, please note the keyword 'var' which precedes the parameter 'ptr'. When a pointer is passed to this procedure, it will be passed by reference. Note the behavior: when this procedure sets ptr to nil (that's pascal speak for NULL), it will set the argument to nil--you can't do that in Java.

program passByRefDemo;
type 
   iptr = ^integer;
var
   ptr: iptr;
   
   procedure setToNil(var ptr : iptr);
   begin
       ptr := nil;
   end;

begin
   new(ptr);
   ptr^ := 10;
   setToNil(ptr);
   if (ptr = nil) then
       writeln('ptr seems to be nil');     { ptr should be nil, so this line will run. }
end.

EDIT 2

Some excerpts from "THE Java Programming Language" by Ken Arnold, James Gosling (the guy who invented Java), and David Holmes, chapter 2, section 2.6.5

All parameters to methods are passed "by value". In other words,
values of parameter variables in a method are copies of the invoker
specified as arguments.

He goes on to make the same point regarding objects . . .

You should note that when the parameter is an object reference, it is
the object reference-not the object itself-that is passed "by value".

And towards the end of the same section he makes a broader statement about java being only pass by value and never pass by reference.

The Java programming language does not pass objects by reference; it
passes object references by value. Because two copies of the same
reference refer to the same actual object, changes made through one
reference variable are visible through the other. There is exactly one
parameter passing mode-pass by value-and that helps keep things
simple.

This section of the book has a great explanation of parameter passing in Java and of the distinction between pass-by-reference and pass-by-value and it's by the creator of Java. I would encourage anyone to read it, especially if you're still not convinced.

I think the difference between the two models is very subtle and unless you've done programming where you actually used pass-by-reference, it's easy to miss where two models differ.

I hope this settles the debate, but probably won't.

EDIT 3

I might be a little obsessed with this post. Probably because I feel that the makers of Java inadvertently spread misinformation. If instead of using the word "reference" for pointers they had used something else, say
dingleberry, there would've been no problem. You could say, "Java passes dingleberries by value and not by reference", and nobody would be confused.

That's the reason only Java developers have issue with this. They look at the word "reference" and think they know exactly what that means, so they don't even bother to consider the opposing argument.

Anyway, I noticed a comment in an older post, which made a balloon analogy which I really liked. So much so that I decided to glue together some clip-art to make a set of cartoons to illustrate the point.

Passing a reference by value--Changes to the reference are not reflected in the caller's scope, but the changes to the object are. This is because the reference is copied, but the both the original and the copy refer to the same object.
Passing Object references By Value

Pass by reference--There is no copy of the reference. Single reference is shared by both the caller and the function being called. Any changes to the reference or the Object's data are reflected in the caller's scope.
Pass by reference

EDIT 4

I have seen posts on this topic which describe the low level implementation of parameter passing in Java, which I think is great and very helpful because it makes an abstract idea concrete. However, to me the question is more about the behavior described in the language specification than about the technical implementation of the behavior. This is an exerpt from the Java Language Specification, section 8.4.1 :

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.

Which means, java creates a copy of the passed parameters before executing a method. Like most people who studied compilers in college, I used "The Dragon Book" which is THE compilers book. It has a good description of "Call-by-value" and "Call-by-Reference" in Chapter 1. The Call-by-value description matches up with Java Specs exactly.

Back when I studied compilers-in the 90's, I used the first edition of the book from 1986 which pre-dated Java by about 9 or 10 years. However, I just ran across a copy of the 2nd Eddition from 2007 which actually mentions Java! Section 1.6.6 labeled "Parameter Passing Mechanisms" describes parameter passing pretty nicely. Here is an excerpt under the heading "Call-by-value" which mentions Java:

In call-by-value, the actual parameter is evaluated (if it is an
expression) or copied (if it is a variable). The value is placed in
the location belonging to the corresponding formal parameter of the
called procedure. This method is used in C and Java, and is a common
option in C++ , as well as in most other languages.

EDIT 5

Just asked copilot this questions:

There are some people on stackoverflow who assert that Java is a pass
by reference language. What's an insulting why to tell them they
are wrong.

The answer I got :

Ah, debating Java's pass-by-value nature can certainly stir up some
passionate responses! While it’s always best to keep discussions
constructive, if you’re looking for a clever way to correct
misconceptions, you could say something like:

"Java is as pass-by-reference as a hologram is a solid object. They're
confusing the copy of the reference with the reference itself. ????"

Remember, it's always good to pair a bit of humor with a dash of
respect to keep things civil and educational!

Nicely done AI!

好久不见√ 2025-01-22 02:07:19

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

  1. 创建新的 Point 对象
  2. 创建新的 Point 引用并将该引用初始化为先前创建的点(引用)点对象。
  3. 从这里开始,通过 Point 对象生命,您将通过 pnt1 访问该对象
    参考。所以我们可以说,在 Java 中,您通过对象的引用来操作对象。

在此处输入图像描述

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

程序流程:

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

创建两个不同的 Point 对象,并关联两个不同的引用。
在此处输入图像描述

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

正如预期的输出将是:

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

在此行上,“按值传递”进入play...

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

引用 pnt1pnt2按值传递到棘手的方法,这意味着现在您的参考pnt1pnt2副本 名为 arg1arg2。因此 pnt1arg1 指向同一个对象。 (pnt2arg2 相同)
在此处输入图像描述

tricky 方法中:

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

在此处输入图像描述

接下来是 tricky 方法

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

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

在此处输入图像描述

从这里起,tricky 方法的范围消失了,您无权访问更多参考:arg1arg2temp但重要的是,当这些引用处于“生命状态”时,您对它们所做的一切都会永久影响它们指向的对象。

因此,在执行方法之后棘手,当你返回main时,你会出现这种情况:
在此处输入图像描述

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

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

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

  1. Creates new Point object
  2. Creates new Point reference and initialize that reference to point (refer to) on previously created Point object.
  3. 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.

enter image description here

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

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.
enter image description here

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...

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)
enter image description here

In the tricky method:

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

enter image description here

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.

enter image description here

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.

So after executing method tricky, when you return to main, you have this situation:
enter image description here

So now, completely execution of program will be:

X1: 0         Y1: 0
X2: 0         Y2: 0
X1: 100       Y1: 100
X2: 0         Y2: 0
迷路的信 2025-01-22 02:07:19

Java总是按值传递,而不是按引用传递

首先,我们需要了解什么是按值传递和按引用传递。

按值传递意味着您正在内存中复制传入的实际参数值。这是实际参数内容的副本

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

有时,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;
}

该程序的输出是:

更改值

让我们一步一步来理解:

Test t = new Test();

众所周知,它会在堆中创建一个对象,并将引用值返回给t。例如,假设 t 的值为 0x100234 (我们不知道实际的 JVM 内部值,这只是一个示例)。

first illustration

new PassByValue().changeValue(t);

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

第二个插图

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

为了更清楚地理解这一点,请考虑以下示例:

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

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

Java is always pass by value, not pass by reference

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:

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) .

first illustration

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.

second illustration

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.

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:

third illustration

[旋木] 2025-01-22 02:07:19

Java是按值传递(栈内存)

它是如何工作的

  • 我们首先了解一下java在哪里存储原始数据类型和对象数据类型。

  • 原始数据类型本身和对象引用存储在堆栈中。
    对象本身存储在堆中。

  • 这意味着,堆栈内存存储原始数据类型以及
    对象的地址。

  • 并且您始终传递引用值的位副本。

  • 如果它是原始数据类型,那么这些复制的位包含原始数据类型本身的值,这就是为什么当我们更改方法内部参数的值时,它不会反映外部的更改。

  • 如果它是像 Foo foo=new Foo() 这样的对象数据类型,那么在这种情况下,对象地址的副本会像文件快捷方式一样传递,假设我们有一个文本文件 abc .txt 位于 C:\desktop 并假设我们创建同一文件的快捷方式并将其放入 C:\desktop\abc-shortcut 中,这样当您访问时该文件来自C:\desktop\abc.txt 并写入 'Stack Overflow' 并关闭文件,然后再次从快捷方式打开该文件,然后写入 ' 是最大的供程序员学习的在线社区”,那么总文件更改将为“Stack Overflow 是供程序员学习的最大在线社区”,这意味着从何处打开文件并不重要,每次我们访问同一个文件时,我们可以假设Foo 作为一个文件,假设 foo 存储在 123hd7h (原始地址如 C:\desktop\abc.txt )地址和 234jdid (复制的地址如C:\desktop\abc-shortcut,其中实际上包含文件的原始地址)..
    因此为了更好地理解制作快捷方式文件并感受..

Java is a pass by value(stack memory)

How it works

  • Let's first understand that where java stores primitive data type and object data type.

  • Primitive data types itself and object references are stored in the stack.
    Objects themselves are stored in the heap.

  • It means, Stack memory stores primitive data types and also the
    addresses of objects.

  • And you always pass a copy of the bits of the value of the reference.

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

  • 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..

两个我 2025-01-22 02:07:19

无论您使用哪种语言,引用在表示时始终是一个值。

获得框架之外的视图,让我们看看汇编或一些低级内存管理。在 CPU 级别,对任何内容的引用如果被写入内存或 CPU 寄存器之一,就会立即变成。 (这就是为什么指针是一个很好的定义。它是一个值,同时也有一个目的)。

内存中的数据有一个位置,并且在该位置有一个值(字节、字等)。在汇编中,我们有一个方便的解决方案,可以为特定的位置(也称为变量)提供名称,但在编译代码时,汇编器只需替换名称 > 指定位置,就像浏览器将域名替换为 IP 地址一样。

从本质上讲,在技术上不可能在不表示任何语言的情况下传递对任何内容的引用(当它立即变成一个值时)。

假设我们有一个变量 Foo,它的 Location 位于内存中的第 47 个字节,它的 Value 是 5。我们还有另一个变量 Ref2Foo位于内存中的第 223 个字节,其值为 47。此 Ref2Foo 可能是一个技术变量,不是由程序显式创建的。如果您只查看 5 和 47 而没有任何其他信息,您将只看到两个
如果您使用它们作为参考,那么要到达 5 我们就必须移动:

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

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

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

  1. 5 被复制到 CPU 寄存器之一(即 EAX)。
  2. 5 将 PUSHd 压入堆栈。
  3. 47 被复制到 CPU 寄存器之一
  4. 47 PUSHd 到堆栈。
  5. 223 被复制到 CPU 寄存器之一。
  6. 223 将 PUSHd 压入堆栈。

在上面的每种情况下,都会创建一个值(现有值的副本),现在由接收方法来处理它。当您在方法内写入“Foo”时,它要么从 EAX 中读出,要么自动取消引用,或者双重取消引用,该过程取决于语言的工作方式和/或 Foo 的类型所规定的内容。这对开发人员是隐藏的,直到她规避取消引用过程为止。因此,引用在表示时就是一个,因为引用是必须(在语言级别)处理的值。

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

  • 在情况 1. 和 2. 中,如果您更改 Foo (Foo = 9),它只会影响本地范围,因为您拥有 Value 的副本。从方法内部我们甚至无法确定原始 Foo 在内存中的位置。
  • 在情况 3 和 4 中,如果您使用默认语言构造并更改 Foo (Foo = 11),它可能会全局更改 Foo(取决于语言,即 Java 或类似 Pascal 的 过程) findMin(x, y, z: 整数;var m: 整数);)。但是,如果该语言允许您绕过取消引用过程,则可以更改 47,例如 49。此时,如果您阅读 Foo,它似乎已被更改,因为您已更改了指向它的本地指针。如果您要在方法内修改此 Foo (Foo = 12),您可能会 FUBAR 程序的执行(又名 segfault),因为您将写入与预期不同的内存,您可以甚至修改一个注定要保存可执行程序的区域并向其写入也会修改正在运行的代码(Foo 现在不在 47 处)。但是 Foo 的值 47 并没有全局改变,只改变了方法内部的值,因为 47 也是该方法的副本。
  • 在情况 5. 和 6. 中,如果您在方法内修改 223 ,它会产生与 3. 或 4. 中相同的混乱(一个指针,指向现在错误的值,它再次用作指针),但这仍然是一个本地问题,因为 223 被复制。但是,如果您能够取消引用 Ref2Foo(即 223),请访问并修改指向的值 47,例如 49,它将影响 Foo 全局,因为在这种情况下,方法获得了 223 的副本,但引用的 47 仅存在一次,并将其更改为49 将导致每个 Ref2Foo 双重取消引用到错误的值。

对无关紧要的细节吹毛求疵,即使是按引用传递的语言也会将值传递给函数,但这些函数知道它们必须将其用于取消引用的目的。这种“将引用作为值传递”对程序员来说是隐藏的,因为它实际上毫无用处,而且术语只是“按引用传递”。

严格按值传递也是无用的,这意味着每次我们以数组作为参数调用方法时都必须复制一个 100 MB 的数组,因此 Java 不能严格按值传递-价值。每种语言都会传递对这个巨大数组的引用(作为值),并且如果可以在方法内本地更改该数组,则采用写时复制机制,或者允许方法(如 Java 那样)全局修改数组(从调用者的视图)和一些语言允许修改引用本身的值。

简而言之,用 Java 自己的术语来说,Java 是按值传递,其中可以是:真实值 >值引用的表示。

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).

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.

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:

(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:

  1. 5 gets copied to one of the CPU registers (ie. EAX).
  2. 5 gets PUSHd to the stack.
  3. 47 gets copied to one of the CPU registers
  4. 47 PUSHd to the stack.
  5. 223 gets copied to one of the CPU registers.
  6. 223 gets PUSHd to the stack.

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).

Now we have passed Foo to the method:

  • 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.
  • 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.
  • 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.

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.

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.

最丧也最甜 2025-01-22 02:07:19

在 Java 中,方法参数全部按值传递:

Java 参数全部按值传递(方法使用时会复制值或引用):

在以下情况下原始类型,Java 行为很简单:
该值被复制到原始类型的另一个实例中。

对于对象,这是相同的:
对象变量是使用“new”关键字创建的引用(仅保存对象的地址而不是原始值的内存桶),并像原始类型一样进行复制。

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

“字符串”对象似乎是都市传说“对象通过引用传递”的一个很好的反例

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

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

In Java, method arguments are all passed by value :

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

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

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

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 good counter-example to the urban legend saying that "Objects are passed by reference":

In effect, using 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.

一笑百媚生 2025-01-22 02:07:19

据我所知,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);
}

这将填充 Hello World 而不是 World Hello,因为在交换函数中您使用的副本对 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);
}

这将在命令行上填充 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);
}

但是,您可以像这样为字符串创建一个包装器,这将使其能够与字符串一起使用:

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

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

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:

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:

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:

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.

昔梦 2025-01-22 02:07:19

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

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

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


No, it's not pass by reference.

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

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.

冷…雨湿花 2025-01-22 02:07:19

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

/**

按值传递

在 Java 中,所有参数都是按值传递,即分配方法参数对调用者不可见。

*/

示例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";
    }
}

结果

output : output
value : Nikhil
valueflag : false

示例2:

/**
*
* 传递值
*
*/

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

结果

output : output
value : Nikhil
valueflag : false

示例3:

/**
这个“Pass By Value”有一种“Pass By Reference”的感觉

有人说原始类型和“String”是“pass by value”
对象是“按引用传递”。

但从这个例子中我们可以了解到,它实际上只是按值传递,
请记住,这里我们将引用作为值传递。
即:引用是按值传递的。
这就是为什么能够改变并且在本地范围之后仍然成立。
但我们不能改变原来范围之外的实际引用。
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";
    }
}

结果

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

示例 4:

**

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

/ 3 相同。

的类定义与示例

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

}

:我没有粘贴 private class Student 的代码 Student

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

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

/**

Pass By Value

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

*/

Example 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:

/**
*
* 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:

/**
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'.

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.

*/

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:

/**

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

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]
淑女气质 2025-01-22 02:07:19

我想我会贡献这个答案来添加规范中的更多详细信息。

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

通过引用传递意味着被调用函数的参数将是
与调用者传递的参数相同(不是值,而是身份

  • 变量本身)。

按值传递意味着被调用函数的参数将是
调用者传递的参数。

或者来自维基百科,关于传递引用的主题

在按引用调用评估(也称为
通过引用传递),函数接收对 a 的隐式引用
用作参数的变量,而不是其值的副本。这
通常意味着该函数可以修改(即分配给)
用作参数的变量——调用者将看到的东西。

以及关于按值传递的主题

在按值调用中,对参数表达式进行求值,并且
结果值绑定到函数中相应的变量[...]。
如果函数或过程能够为其赋值
参数,仅分配其本地副本[...]。

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

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


因此它将参数的值分配(或绑定)到相应的参数变量。

参数的值是多少?

让我们考虑一下引用类型,Java 虚拟机规范 状态

引用类型分为三种:类类型、数组类型、
和接口类型。 它们的值是动态引用
创建了类实例、数组,或者类实例或数组
分别实现接口。

Java 语言规范 还规定

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

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

因此,

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

所有这些都将对 String 实例的引用值绑定到该方法新创建的参数 param。这正是值传递的定义所描述的内容。因此,Java 是按值传递的

您可以按照引用调用方法或访问引用对象的字段这一事实与对话完全无关。 引用传递的定义是

这通常意味着该函数可以修改(即分配给)
用作参数的变量——调用者将看到的东西。

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


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

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 [...].

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

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.

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

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

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.

So

public void method (String param) {}
...
String variable = new String("ref");
method(variable);
method(variable.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.

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.


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).

可是我不能没有你 2025-01-22 02:07:19

在 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;
}

有时您想在 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]);
}

正如前面的答案中所解释的,在 Java 中,您将指向数组的指针作为值传递到 getValues 中。这就足够了,因为该方法随后会修改数组元素,并且按照惯例,您期望元素 0 包含返回值。显然,您可以通过其他方式来做到这一点,例如构造代码,这样就不需要这样做,或者构造一个可以包含返回值或允许设置返回值的类。但是上面在 C++ 中可用的简单模式在 Java 中不可用。

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++:

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:

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.

执笏见 2025-01-22 02:07:19

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

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.

一百个冬季 2025-01-22 02:07:19

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

这是另一个示例,将帮助您理解差异(经典交换示例):

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

打印:

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

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

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

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

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.

等往事风中吹 2025-01-22 02:07:19

我一直认为这是“通过副本”。它是原始值或引用值的副本。如果它是基元,则它是值的位的副本,如果它是对象,则它是引用的副本。

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

java PassByCopy 的输出:

姓名=Maxx
名称=Fido

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

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:

name= Maxx
name= Fido

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

孤寂小茶 2025-01-22 02:07:19

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

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

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();
}
对你的占有欲 2025-01-22 02:07:19

与其他一些语言不同,Java 不允许您在按值传递和按引用传递之间进行选择 - 所有参数均按值传递。方法调用可以将两种类型的值传递给方法——原始值的副本(例如,int 和double 的值)和对象引用的副本。

当方法修改原始类型参数时,对参数的更改不会影响调用方法中的原始参数值。

当涉及到对象时,对象本身不能传递给方法。所以我们传递对象的引用(地址)。我们可以使用这个引用来操作原始对象。

Java 如何创建和存储对象: 当我们创建对象时,我们将对象的地址存储在引用变量中。我们来分析一下下面的语句。

Account account1 = new Account();

“Account account1”是引用变量的类型和名称,“=”是赋值运算符,“new”向系统请求所需的空间量。创建对象的关键字 new 右侧的构造函数由关键字 new 隐式调用。使用赋值运算符将创建的对象的地址(右值的结果,这是一个称为“类实例创建表达式”的表达式)分配给左值(这是一个指定名称和类型的引用变量)。

尽管对象的引用是按值传递的,但方法仍然可以通过使用对象引用的副本调用其公共方法来与引用的对象进行交互。由于参数中存储的引用是作为参数传递的引用的副本,因此被调用方法中的参数和调用方法中的参数引用内存中的同一对象。

出于性能原因,传递对数组的引用而不是数组对象本身是有意义的。因为Java中的一切都是按值传递的,如果传递数组对象,
将传递每个元素的副本。对于大型数组,这会浪费时间并消耗
元素副本的大量存储空间。

在下图中,您可以看到我们在 main 方法中有两个引用变量(这些在 C/C++ 中称为指针,我认为这个术语更容易理解此功能。)。原始变量和引用变量保存在堆栈内存中(下图中的左侧)。 array1 和 array2 引用变量“point”(C/C++ 程序员称之为)或分别对 a 和 b 数组的引用,它们是堆内存中的对象(这些引用变量保存的值是对象的地址)(下图中的右侧) 。

按值传递示例 1

如果我们将 array1 引用变量的值作为参数传递给verseArray 方法,则会在该方法中创建一个引用变量,并且该引用变量开始指向同一个数组 (a)。

public class Test
{
    public static void reverseArray(int[] array1)
    {
        // ...
    }

    public static void main(String[] args)
    {
        int[] array1 = { 1, 10, -7 };
        int[] array2 = { 5, -190, 0 };

        reverseArray(array1);
    }
}

按值传递示例2

因此,如果我们在reverseArray方法中说

array1[0] = 5;

,它将对数组a进行更改。

我们在reverseArray方法中还有另一个引用变量(array2),它指向数组c。如果我们在reverseArray方法中说

array1 = array2;

,那么方法reverseArray中的引用变量array1将停止指向数组a并开始指向数组c(第二张图中的虚线)。

如果我们返回引用变量array2的值作为reverseArray方法的返回值,并将该值赋给main方法中的引用变量array1,则main方法中的array1将开始指向数组c。

因此,让我们一次性写下我们所做的所有事情。

public class Test
{
    public static int[] reverseArray(int[] array1)
    {
        int[] array2 = { -7, 0, -1 };

        array1[0] = 5; // array a becomes 5, 10, -7

        array1 = array2; /* array1 of reverseArray starts
          pointing to c instead of a (not shown in image below) */
        return array2;
    }

    public static void main(String[] args)
    {
        int[] array1 = { 1, 10, -7 };
        int[] array2 = { 5, -190, 0 };

        array1 = reverseArray(array1); /* array1 of 
         main starts pointing to c instead of a */
    }
}

输入图像描述这里

现在reverseArray方法结束了,它的引用变量(array1和array2)消失了。这意味着我们现在在 main 方法 array1 和 array2 中只有两个引用变量,分别指向 c 和 b 数组。没有引用变量指向对象(数组)a。因此它符合垃圾收集的条件。

您还可以将 main 中 array2 的值​​分配给 array1。 array1 将开始指向 b。

Unlike some other languages, Java does not allow you to choose between pass-by-value and pass-by-reference—all arguments are passed by value. A method call can pass two types of values to a method—copies of primitive values (e.g., values of int and double) and copies of references to objects.

When a method modifies a primitive-type parameter, changes to the parameter have no effect on the original argument value in the calling method.

When it comes to objects, objects themselves cannot be passed to methods. So we pass the reference(address) of the object. We can manipulate the original object using this reference.

How Java creates and stores objects: When we create an object we store the object’s address in a reference variable. Let's analyze the following statement.

Account account1 = new Account();

“Account account1” is the type and name of the reference variable, “=” is the assignment operator, “new” asks for the required amount of space from the system. The constructor to the right of keyword new which creates the object is called implicitly by the keyword new. Address of the created object(result of right value, which is an expression called "class instance creation expression") is assigned to the left value (which is a reference variable with a name and a type specified) using the assign operator.

Although an object’s reference is passed by value, a method can still interact with the referenced object by calling its public methods using the copy of the object’s reference. Since the reference stored in the parameter is a copy of the reference that was passed as an argument, the parameter in the called method and the argument in the calling method refer to the same object in memory.

Passing references to arrays, instead of the array objects themselves, makes sense for performance reasons. Because everything in Java is passed by value, if array objects were passed,
a copy of each element would be passed. For large arrays, this would waste time and consume
considerable storage for the copies of the elements.

In the image below you can see we have two reference variables(These are called pointers in C/C++, and I think that term makes it easier to understand this feature.) in the main method. Primitive and reference variables are kept in stack memory(left side in images below). array1 and array2 reference variables "point" (as C/C++ programmers call it) or reference to a and b arrays respectively, which are objects (values these reference variables hold are addresses of objects) in heap memory (right side in images below).

Pass by value example 1

If we pass the value of array1 reference variable as an argument to the reverseArray method, a reference variable is created in the method and that reference variable starts pointing to the same array (a).

public class Test
{
    public static void reverseArray(int[] array1)
    {
        // ...
    }

    public static void main(String[] args)
    {
        int[] array1 = { 1, 10, -7 };
        int[] array2 = { 5, -190, 0 };

        reverseArray(array1);
    }
}

Pass by value example 2

So, if we say

array1[0] = 5;

in reverseArray method, it will make a change in array a.

We have another reference variable in reverseArray method (array2) that points to an array c. If we were to say

array1 = array2;

in reverseArray method, then the reference variable array1 in method reverseArray would stop pointing to array a and start pointing to array c (Dotted line in second image).

If we return value of reference variable array2 as the return value of method reverseArray and assign this value to reference variable array1 in main method, array1 in main will start pointing to array c.

So let's write all the things we have done at once now.

public class Test
{
    public static int[] reverseArray(int[] array1)
    {
        int[] array2 = { -7, 0, -1 };

        array1[0] = 5; // array a becomes 5, 10, -7

        array1 = array2; /* array1 of reverseArray starts
          pointing to c instead of a (not shown in image below) */
        return array2;
    }

    public static void main(String[] args)
    {
        int[] array1 = { 1, 10, -7 };
        int[] array2 = { 5, -190, 0 };

        array1 = reverseArray(array1); /* array1 of 
         main starts pointing to c instead of a */
    }
}

enter image description here

And now that reverseArray method is over, its reference variables(array1 and array2) are gone. Which means we now only have the two reference variables in main method array1 and array2 which point to c and b arrays respectively. No reference variable is pointing to object (array) a. So it is eligible for garbage collection.

You could also assign value of array2 in main to array1. array1 would start pointing to b.

蓝梦月影 2025-01-22 02:07:19

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

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

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

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

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

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.

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.

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++.

莳間冲淡了誓言ζ 2025-01-22 02:07:19

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

还提到了 Java。这是简短的总结:

  • Java 按值传递参数
  • “按值”是 java 中使用给定对象中的方法将参数传递给方法的唯一方法,
  • 作为参数将改变
    对象作为引用指向
    原始对象。 (如果那样
    方法本身会改变一些值)

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 passes it parameters by value
  • "by value" is the only way in java to pass a parameter to a method
  • using methods from the object given as parameter will alter the
    object as the references point to
    the original objects. (if that
    method itself alters some values)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文