Java 是“按引用传递”吗?或“按值传递”?
我一直认为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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(30)
术语“按值传递”和“按引用传递”有特殊的 精确定义计算机科学中的含义。这些含义与许多人第一次听到这些术语时的直觉不同。这次讨论中的大部分混乱似乎都来自这个事实。
术语“按值传递”和“按引用传递”谈论的是变量。按值传递意味着传递变量的值到一个函数/方法。引用传递意味着对该变量的引用被传递给函数。后者为函数提供了更改变量内容的方法。
根据这些定义,Java 始终是按值传递。不幸的是,当我们处理保存对象的变量时,我们实际上是在处理称为引用的对象句柄,它们也是按值传递的。这种术语和语义很容易让许多初学者感到困惑。
它是这样的:
在此示例中,
aDog.getName()
仍将返回"Max"
。通过创建名称成员变量设置为的新Dog
,main
中的值aDog
不会在函数foo
中更改"Fifi"
因为对象引用是按值传递的。如果对象引用是通过引用传递的,则main
中的aDog.getName()
在调用后将返回
."Fifi"
>foo同样:
在此示例中,
Fifi
是调用foo(aDog)
后狗的名称,因为对象的名称是在foo(...)
内部设置的代码>.foo
对d
执行的任何操作,出于所有实际目的,它们都是在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:
In this example,
aDog.getName()
will still return"Max"
. The valueaDog
withinmain
is not changed in the functionfoo
by creating newDog
with name member variable set to"Fifi"
because the object reference is passed by value. If the object reference was passed by reference, then theaDog.getName()
inmain
would return"Fifi"
after the call tofoo
.Likewise:
In this example,
Fifi
is dog’s name after call tofoo(aDog)
because the object's name was set inside offoo(...)
. Any operations thatfoo
performs ond
are such that, for all practical purposes, they are performed onaDog
, but it is not possible to change the value of the variableaDog
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.
我刚刚注意到您引用了我的文章。
Java 规范规定 Java 中的所有内容都是按值传递的。 Java 中不存在“按引用传递”这样的东西。
理解这一点的关键是,类似的东西
不是狗;它实际上是一个指向 Dog 的指针。 Java 中术语“引用”的使用非常具有误导性,也是造成这里大部分混乱的原因。他们所谓的“引用”的行为/感觉更像是我们在大多数其他语言中所说的“指针”。
这意味着,当您创建时,
您实际上是将创建的
Dog
对象的地址传递给foo
方法。(我说本质上是因为 Java 指针/引用不是直接地址,但这样考虑它们是最容易的。)
假设
Dog
对象驻留在内存地址 42 处。这意味着我们将 42 传递给方法。如果方法被定义为
让我们看看发生了什么。
someDog
someDog
后面是它指向的Dog
(地址 42 处的Dog
对象)狗
(地址为 42 的那只)被要求将他的名字改为 Max狗
。假设他位于地址 74someDog
分配给 74Dog
(地址 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 中,您可以写
这不是一个C 中的特殊情况。两种语言都使用按值传递语义。这里,调用站点正在创建额外的数据结构来协助函数访问和操作数据。
该函数将传递指向数据的指针,并遵循这些指针来访问和修改该数据。
Java 中的类似方法(调用者设置辅助结构)可能是:(
或者如果您希望两个示例都演示其他语言没有的功能,请创建一个可变的 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
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
you're essentially passing the address of the created
Dog
object to thefoo
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
let's look at what's happening.
someDog
is set to the value 42someDog
is followed to theDog
it points to (theDog
object at address 42)Dog
(the one at address 42) is asked to change his name to MaxDog
is created. Let's say he's at address 74someDog
to 74Dog
it points to (theDog
object at address 74)Dog
(the one at address 74) is asked to change his name to RowlfNow 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 actualDog
, the answer is NO.myDog
still has the value 42; it's still pointing to the originalDog
(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 wheremyDog
was pointing when it assignedsomeDog
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
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:
(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.
Java 总是按值传递参数,而不是按引用传递参数。
让我通过一个示例来解释这一点:
我将分步骤解释这一点:
声明一个名为
f的引用
类型为Foo
的对象,并为其分配一个类型为Foo
且属性为“f”
的新对象。从方法端,带有
Foo
类型的引用namea
已声明,并且最初分配为null
。当您调用方法
changeReference
时,引用a
将被分配作为参数传递的对象。声明一个名为
b
且类型为Foo< 的引用/code> 并为其分配一个
Foo
类型的新对象,其属性为"b"
。a = b
对参考进行新分配a
,不是f
,属性为“b”
的对象。当您调用
modifyReference(Foo c)
方法时,引用c
创建并分配给具有属性“f”
的对象。c.setAttribute("c");
将更改引用c
指向它的对象的属性,与引用f
指向它的对象是同一个对象。Java always passes arguments by value, NOT by reference.
Let me explain this through an example:
I will explain this in steps:
Declaring a reference named
f
of typeFoo
and assign it a new object of typeFoo
with an attribute"f"
.From the method side, a reference of type
Foo
with a namea
is declared and it's initially assignednull
.As you call the method
changeReference
, the referencea
will be assigned the object which is passed as an argument.Declaring a reference named
b
of typeFoo
and assign it a new object of typeFoo
with an attribute"b"
.a = b
makes a new assignment to the referencea
, notf
, of the object whose attribute is"b"
.As you call
modifyReference(Foo c)
method, a referencec
is created and assigned the object with attribute"f"
.c.setAttribute("c");
will change the attribute of the object that referencec
points to it, and it's the same object that referencef
points to it.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
int
,long
, etc.), the pass by value is the actual value of the primitive (for example, 3).So if you have
doSomething(foo)
andpublic 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.
这将使您对 Java 的真正工作原理有一些了解,以至于在您下次讨论 Java 按引用传递或按值传递时,您只会微笑:-)
请从您的脑海中抹去第一步以“p”“_ _ _ _ _ _ _”开头的单词,尤其是如果您来自其他编程语言。 Java和'p'不能写在同一本书、论坛、甚至txt中。
第二步请记住,当您将对象传递给方法时,您传递的是对象引用,而不是对象本身。
现在想想对象的引用/变量的作用:
在下面(请不要尝试编译/执行这个...):
会发生什么?
一张图片胜过一千个单词:
请注意,anotherReferenceToTheSamePersonObject 箭头指向对象,而不是指向对象面向变量 person!
如果您不明白,那么请相信我并记住,最好说Java 是按值传递的。好吧,传递参考值。哦,更好的是通过变量值的副本传递! ;)
现在请随意恨我,但请注意,考虑到这一点,在谈论方法参数时,传递原始数据类型和对象之间没有区别。
您总是传递引用值的位副本!
当然,你可以简单地说,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.
Now think of what an Object's reference/variable does/is:
In the following (please don't try to compile/execute this...):
What happens?
A picture is worth a thousand words:
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!
Of course you can cut it short and just say that Java is pass-by-value!
Java 按值传递引用。
因此您无法更改传入的引用。
Java passes references by value.
So you can't change the reference that gets passed in.
我觉得争论按引用传递与按值传递并没有多大帮助。
如果您说 Java 是pass-by-whatever,那么您并没有提供完整的答案。以下是一些附加信息,希望可以帮助您了解内存中实际发生的情况。
在我们开始 Java 实现之前,我们先来了解一下堆栈/堆的速成课程:
值以良好有序的方式在堆栈中上下移动,就像自助餐厅里的一堆盘子一样。
堆中的内存(也称为动态内存)是随意且无组织的。 JVM 会尽可能地寻找空间,并在不再需要使用该空间的变量时将其释放。
好的。首先,局部原语进入堆栈。所以这段代码:
结果是:
当你声明并实例化一个对象时。实际的对象位于堆上。堆栈上有什么?对象在堆上的地址。 C++ 程序员将其称为指针,但一些 Java 开发人员反对使用“指针”这个词。任何。只需知道对象的地址位于堆栈中即可。
就像这样:
数组是一个对象,因此它也位于堆上。那么数组中的对象呢?它们拥有自己的堆空间,每个对象的地址都在数组中。
那么,当您调用方法时会传入什么?如果您传入一个对象,那么您实际传入的是该对象的地址。有些人可能会说地址的“值”,有些人说它只是对对象的引用。这就是“参考”和“价值”支持者之间圣战的起源。你怎么称呼它并不重要,重要的是你明白传入的是对象的地址。
创建一个字符串并在堆中为其分配空间,并且该字符串的地址存储在堆栈中并给出标识符
hisName
,因为第二个字符串的地址与第一个字符串的地址相同首先,没有创建新的String,也没有分配新的堆空间,但在堆栈上创建了一个新的标识符。然后我们调用shout()
:创建一个新的堆栈帧并创建一个新标识符name
并分配已存在字符串的地址。那么,价值、参考?你说的是“土豆”。
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:
results in this:
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:
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.
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.
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 callshout()
: a new stack frame is created and a new identifier,name
is created and assigned the address of the already-existing String.So, value, reference? You say "potato".
基本上,重新分配对象参数不会影响参数,例如,
将打印出
“Hah!”
而不是null
。之所以有效,是因为bar
是baz
值的副本,它只是对"Hah!"
的引用。如果它是实际引用本身,则foo
会将baz
重新定义为null
。Basically, reassigning Object parameters doesn't affect the argument, e.g.,
will print out
"Hah!"
instead ofnull
. The reason this works is becausebar
is a copy of the value ofbaz
, which is just a reference to"Hah!"
. If it were the actual reference itself, thenfoo
would have redefinedbaz
tonull
.为了显示对比,请比较以下 C++ 和 Java 片段:
在 C++ 中:注意:错误代码 - 内存泄漏! 但它证明了这一点。
在Java中,
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.
In Java,
Java only has the two types of passing: by value for built-in types, and by value of the pointer for object types.
Java 按值传递对对象的引用。
Java passes references to objects by value.
我不敢相信还没有人提到芭芭拉·利斯科夫。当她在 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".
问题的关键在于“按引用传递”表达式中的“引用”一词与 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.
已经有很好的答案涵盖了这一点。我想通过分享一个非常简单的示例(将编译)来做出一点贡献,对比 C++ 中的按引用传递和 Java 中的按值传递之间的行为。
几点:
C++ 按引用传递示例:
Java 按值传递“Java 引用”示例
编辑
有几个人写了评论,这些评论似乎表明他们要么没有看我的示例,要么没有了解 c++例子。不确定断开连接在哪里,但猜测 c++ 示例并不清楚。我在 pascal 中发布了相同的示例,因为我认为通过引用在 pascal 中看起来更干净,但我可能是错的。我可能只是让人们更加困惑;我希望不会。
在 pascal 中,按引用传递的参数称为“var 参数”。在下面的 setToNil 过程中,请注意参数“ptr”之前的关键字“var”。当指针传递到此过程时,它将通过引用传递。请注意行为:当此过程将 ptr 设置为 nil(即 pascal 中的 NULL)时,它将把参数设置为 nil —— 在 Java 中不能这样做。
编辑 2
一些摘录自 Ken Arnold、James Gosling(Java 的发明者) 和 David Holmes 所著的 “Java 编程语言”,第 2 章第 2.6.5 节
他接着对物体提出了同样的观点。 。 。
在同一节的末尾,他对 java 只按值传递而从不按引用传递进行了更广泛的声明。
本书的这一部分对 Java 中的参数传递以及按引用传递和按值传递之间的区别进行了很好的解释,并且由 Java 的创建者撰写。我鼓励任何人阅读它,特别是如果你仍然不相信的话。
我认为这两个模型之间的差异非常微妙,除非您在实际使用传递引用的情况下进行了编程,否则很容易错过两个模型的不同之处。
我希望这能解决争论,但可能不会。
编辑3
我可能对这篇文章有点着迷。可能是因为我觉得 Java 的开发者无意中传播了错误信息。如果他们没有使用“引用”这个词来表示指针,而是使用了其他东西,比如
dingleberry,那就没有问题了。你可以说,“Java 通过值传递 dingleberry,而不是通过引用”,没有人会感到困惑。
这就是只有 Java 开发人员对此有疑问的原因。他们看着“参考”这个词,就认为自己确切地知道这意味着什么,所以他们甚至懒得去考虑相反的论点。
不管怎样,我注意到一篇旧帖子中的一条评论,它做了一个我非常喜欢的气球类比。以至于我决定将一些剪贴画粘在一起制作一组漫画来说明这一点。
按值传递引用——对引用的更改不会反映在调用者的作用域中,但对对象的更改会反映在调用者的作用域中。这是因为引用被复制,但原始对象和副本都引用同一个对象。

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

编辑4
我看过关于这个主题的帖子,其中描述了Java中参数传递的低级实现,我认为这很棒并且非常有帮助,因为它使抽象的想法具体化。然而,对我来说,问题更多的是关于语言规范中描述的行为,而不是关于行为的技术实现。这是摘自 Java 语言规范,第 8.4.1 节:
这意味着,java 在执行方法之前创建传递参数的副本。和大多数在大学学习编译器的人一样,我使用了“龙之书”这是编译器书。第 1 章中对“Call-by-value”和“Call-by-Reference”有很好的描述。Call-by-value 的描述与 Java 规范完全匹配。
当我在 90 年代研究编译器时,我使用了 1986 年这本书的第一版,它比 Java 早了大约 9 或 10 年。然而,我刚刚发现了 2007 年的第二版的副本 实际上提到了 Java! 标有“参数传递机制”的 1.6.6 节很好地描述了参数传递。以下是“按值调用”标题下的摘录,其中提到了 Java:
编辑 5
刚刚向副驾驶询问了以下问题:
我得到的答案是:
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:
C++ pass by reference example:
Java pass "a Java reference" by value example
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.
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
He goes on to make the same point regarding objects . . .
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.
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.

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.

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 :
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:
EDIT 5
Just asked copilot this questions:
The answer I got :
Nicely done AI!
在java中一切都是引用,所以当你有类似的东西时:
Point pnt1 = new Point(0,0);
Java 执行以下操作:参考。所以我们可以说,在 Java 中,您通过对象的引用来操作对象。
Java 不会不通过引用传递方法参数;它按值传递它们。 我将使用 此站点:
程序流程:
创建两个不同的 Point 对象,并关联两个不同的引用。

正如预期的输出将是:
在此行上,“按值传递”进入play...
引用

pnt1
和pnt2
被按值传递到棘手的方法,这意味着现在您的参考pnt1
和pnt2
的副本
名为arg1
和arg2
。因此pnt1
和arg1
指向同一个对象。 (pnt2
和arg2
相同)在
tricky
方法中:接下来是
tricky
方法这里,您首先创建新的
temp
点引用,该引用将 点在同一个地方,例如arg1 参考。然后,将引用arg1
移动到 指向 与arg2
引用相同的位置。最后,
arg2
将指向与temp
相同的位置。从这里起,
tricky
方法的范围消失了,您无权访问更多参考:arg1
、arg2
、temp
。 但重要的是,当这些引用处于“生命状态”时,您对它们所做的一切都会永久影响它们指向的对象。因此,在执行方法

之后棘手
,当你返回main
时,你会出现这种情况:所以现在,程序的完整执行将是:
In java everything is reference, so when you have something like:
Point pnt1 = new Point(0,0);
Java does following:reference. So we can say that in Java you manipulate object through its reference.
Java doesn't pass method arguments by reference; it passes them by value. I will use example from this site:
Flow of the program:
Creating two different Point object with two different reference associated.

As expected output will be:
On this line 'pass-by-value' goes into the play...
References

pnt1
andpnt2
are passed by value to the tricky method, which means that now yours referencespnt1
andpnt2
have theircopies
namedarg1
andarg2
.Sopnt1
andarg1
points to the same object. (Same for thepnt2
andarg2
)In the
tricky
method:Next in the
tricky
methodHere, you first create new
temp
Point reference which will point on same place likearg1
reference. Then you move referencearg1
to point to the same place likearg2
reference.Finally
arg2
will point to the same place liketemp
.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 tomain
, you have this situation:So now, completely execution of program will be:
Java总是按值传递,而不是按引用传递
首先,我们需要了解什么是按值传递和按引用传递。
按值传递意味着您正在内存中复制传入的实际参数值。这是实际参数内容的副本。
按引用传递(也称为按地址传递)意味着存储实际参数地址的副本。
有时,Java 会产生按引用传递的错觉。让我们通过下面的示例看看它是如何工作的:
该程序的输出是:
让我们一步一步来理解:
众所周知,它会在堆中创建一个对象,并将引用值返回给t。例如,假设 t 的值为
0x100234
(我们不知道实际的 JVM 内部值,这只是一个示例)。将引用 t 传递给函数时,不会直接传递对象 test 的实际引用值,而是会创建t 的副本,然后将其传递给函数。由于它是按值传递,因此它传递变量的副本而不是它的实际引用。由于我们说 t 的值为
0x100234
,因此 t 和 f 将具有相同的值,因此它们将指向同一个对象。如果使用引用 f 更改函数中的任何内容,它将修改对象的现有内容。这就是为什么我们得到输出
changevalue
,它在函数中更新。为了更清楚地理解这一点,请考虑以下示例:
这会抛出 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:
The output of this program is:
Let's understand step by step:
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) .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.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:
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: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..
无论您使用哪种语言,引用在表示时始终是一个值。
获得框架之外的视图,让我们看看汇编或一些低级内存管理。在 CPU 级别,对任何内容的引用如果被写入内存或 CPU 寄存器之一,就会立即变成值。 (这就是为什么指针是一个很好的定义。它是一个值,同时也有一个目的)。
内存中的数据有一个位置,并且在该位置有一个值(字节、字等)。在汇编中,我们有一个方便的解决方案,可以为特定的位置(也称为变量)提供名称,但在编译代码时,汇编器只需替换名称 > 指定位置,就像浏览器将域名替换为 IP 地址一样。
从本质上讲,在技术上不可能在不表示任何语言的情况下传递对任何内容的引用(当它立即变成一个值时)。
假设我们有一个变量 Foo,它的 Location 位于内存中的第 47 个字节,它的 Value 是 5。我们还有另一个变量 Ref2Foo位于内存中的第 223 个字节,其值为 47。此 Ref2Foo 可能是一个技术变量,不是由程序显式创建的。如果您只查看 5 和 47 而没有任何其他信息,您将只看到两个值。
如果您使用它们作为参考,那么要到达
5
我们就必须移动:这就是跳转表的工作原理。
如果我们想用 Foo 的值调用方法/函数/过程,有几种可能的方法将变量传递给方法,具体取决于语言及其几种方法调用模式:
在上面的每种情况下,都会创建一个值(现有值的副本),现在由接收方法来处理它。当您在方法内写入“Foo”时,它要么从 EAX 中读出,要么自动取消引用,或者双重取消引用,该过程取决于语言的工作方式和/或 Foo 的类型所规定的内容。这对开发人员是隐藏的,直到她规避取消引用过程为止。因此,引用在表示时就是一个值,因为引用是必须(在语言级别)处理的值。
现在我们已将 Foo 传递给该方法:
Foo = 9
),它只会影响本地范围,因为您拥有 Value 的副本。从方法内部我们甚至无法确定原始 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
也是该方法的副本。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: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:
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:
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.Foo = 11
), it could change Foo globally (depends on the language, ie. Java or like Pascal'sprocedure findMin(x, y, z: integer;
var m: integer);
). However if the language allows you to circumvent the dereference process, you can change47
, say to49
. 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 at47
). BUT Foo's value of47
did not change globally, only the one inside the method, because47
was also a copy to the method.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 dereferenceRef2Foo
(that is223
), reach to and modify the pointed value47
, say, to49
, it will affect Foo globally, because in this case the methods got a copy of223
but the referenced47
exists only once, and changing that to49
will lead everyRef2Foo
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.
在 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.
据我所知,Java只知道按值调用。这意味着对于原始数据类型,您将使用副本,而对于对象,您将使用对象引用的副本。但我认为还是有一些陷阱;例如,这将不起作用:
这将填充 Hello World 而不是 World Hello,因为在交换函数中您使用的副本对 main 中的引用没有影响。但是,如果您的对象不是不可变的,您可以更改它,例如:
这将在命令行上填充 Hello World。如果将 StringBuffer 更改为 String,它只会生成 Hello,因为 String 是不可变的。例如:
但是,您可以像这样为字符串创建一个包装器,这将使其能够与字符串一起使用:
编辑:我相信这也是在“添加”两个字符串时使用 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:
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:
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:
However you could make a wrapper for String like this which would make it able to use it with Strings:
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.
不,它不是通过引用传递。
根据 Java 语言规范,Java 是按值传递的:
No, it's not pass by reference.
Java is pass by value according to the Java Language Specification:
让我尝试用四个例子来解释我的理解。 Java 是按值传递,而不是按引用传递
/**
按值传递
在 Java 中,所有参数都是按值传递,即分配方法参数对调用者不可见。
*/
示例1:
结果
示例2:
/**
*
* 传递值
*
*/
结果
示例3:
/**
这个“Pass By Value”有一种“Pass By Reference”的感觉
有人说原始类型和“String”是“pass by value”
对象是“按引用传递”。
但从这个例子中我们可以了解到,它实际上只是按值传递,
请记住,这里我们将引用作为值传递。
即:引用是按值传递的。
这就是为什么能够改变并且在本地范围之后仍然成立。
但我们不能改变原来范围之外的实际引用。
PassByValueObjectCase2 的下一个示例演示了这意味着什么。
*/
结果
示例 4:
**
除了示例 3 (PassByValueObjectCase1.java) 中提到的内容之外,我们不能更改原始范围之外的实际引用。"
/ 3 相同。
的类定义与示例
:我没有粘贴
private class Student
的代码Student
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:
Result
Example 2:
/**
*
* Pass By Value
*
*/
Result
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.
*/
Result
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 forStudent
is same as Example3.*/
Result
我想我会贡献这个答案来添加规范中的更多详细信息。
首先,通过引用传递与通过引用传递之间有什么区别. 按值传递?
或者来自维基百科,关于传递引用的主题
以及关于按值传递的主题
其次,我们需要知道Java在其方法调用中使用了什么。 Java 语言规范 状态
因此它将参数的值分配(或绑定)到相应的参数变量。
参数的值是多少?
让我们考虑一下引用类型,Java 虚拟机规范 状态
Java 语言规范 还规定
参数(某种引用类型)的值是指向对象的指针。请注意,变量、具有引用类型返回类型的方法的调用以及实例创建表达式 (
new ...
) 都解析为引用类型值。因此,
所有这些都将对
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?
Or from wikipedia, on the subject of pass-by-reference
And on the subject of pass-by-value
Second, we need to know what Java uses in its method invocations. The Java Language Specification states
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
The Java Language Specification also states
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
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
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).
在 Java 中永远不能通过引用传递,一种显而易见的方法是当您想要从方法调用中返回多个值时。考虑以下 C++ 代码:
有时您想在 Java 中使用相同的模式,但不能;至少不是直接的。相反,您可以执行以下操作:
正如前面的答案中所解释的,在 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++:
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:
As was explained in previous answers, in Java you're passing a pointer to the array as a value into
getValues
. That is enough, because the method then modifies the array element, and by convention you're expecting element 0 to contain the return value. Obviously you can do this in other ways, such as structuring your code so this isn't necessary, or constructing a class that can contain the return value or allow it to be set. But the simple pattern available to you in C++ above is not available in Java.区别,或者也许只是我记得的方式,因为我曾经与原始发布者有相同的印象:Java 总是按值传递。 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.
正如许多人之前提到的,Java 总是按值传递
这是另一个示例,将帮助您理解差异(经典交换示例):
打印:
发生这种情况是因为 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):
Prints:
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.
我一直认为这是“通过副本”。它是原始值或引用值的副本。如果它是基元,则它是值的位的副本,如果它是对象,则它是引用的副本。
java PassByCopy 的输出:
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.
output of java PassByCopy:
Primitive wrapper classes and Strings are immutable so any example using those types will not work the same as other types/objects.
Java只有值传递。一个非常简单的例子来验证这一点。
Java has only pass by value. A very simple example to validate this.
与其他一些语言不同,Java 不允许您在按值传递和按引用传递之间进行选择 - 所有参数均按值传递。方法调用可以将两种类型的值传递给方法——原始值的副本(例如,int 和double 的值)和对象引用的副本。
当方法修改原始类型参数时,对参数的更改不会影响调用方法中的原始参数值。
当涉及到对象时,对象本身不能传递给方法。所以我们传递对象的引用(地址)。我们可以使用这个引用来操作原始对象。
Java 如何创建和存储对象: 当我们创建对象时,我们将对象的地址存储在引用变量中。我们来分析一下下面的语句。
“Account account1”是引用变量的类型和名称,“=”是赋值运算符,“new”向系统请求所需的空间量。创建对象的关键字 new 右侧的构造函数由关键字 new 隐式调用。使用赋值运算符将创建的对象的地址(右值的结果,这是一个称为“类实例创建表达式”的表达式)分配给左值(这是一个指定名称和类型的引用变量)。
尽管对象的引用是按值传递的,但方法仍然可以通过使用对象引用的副本调用其公共方法来与引用的对象进行交互。由于参数中存储的引用是作为参数传递的引用的副本,因此被调用方法中的参数和调用方法中的参数引用内存中的同一对象。
出于性能原因,传递对数组的引用而不是数组对象本身是有意义的。因为Java中的一切都是按值传递的,如果传递数组对象,
将传递每个元素的副本。对于大型数组,这会浪费时间并消耗
元素副本的大量存储空间。
在下图中,您可以看到我们在 main 方法中有两个引用变量(这些在 C/C++ 中称为指针,我认为这个术语更容易理解此功能。)。原始变量和引用变量保存在堆栈内存中(下图中的左侧)。 array1 和 array2 引用变量“point”(C/C++ 程序员称之为)或分别对 a 和 b 数组的引用,它们是堆内存中的对象(这些引用变量保存的值是对象的地址)(下图中的右侧) 。
如果我们将 array1 引用变量的值作为参数传递给verseArray 方法,则会在该方法中创建一个引用变量,并且该引用变量开始指向同一个数组 (a)。
因此,如果我们在reverseArray方法中说
,它将对数组a进行更改。
我们在reverseArray方法中还有另一个引用变量(array2),它指向数组c。如果我们在reverseArray方法中说
,那么方法reverseArray中的引用变量array1将停止指向数组a并开始指向数组c(第二张图中的虚线)。
如果我们返回引用变量array2的值作为reverseArray方法的返回值,并将该值赋给main方法中的引用变量array1,则main方法中的array1将开始指向数组c。
因此,让我们一次性写下我们所做的所有事情。
现在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” 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).
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).
So, if we say
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
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.
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.
长话短说,Java 对象具有一些非常奇特的属性。
一般来说,Java 有直接传递的原始类型(
int
、bool
、char
、double
等)按价值。然后,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 fromjava.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 anint 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 byfoo(a)
, where the compiler itself knows that it is a reference and the address of the non-referencea
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++.我创建了一个专门针对任何编程语言的此类问题的线程此处。
还提到了 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:
object as the references point to
the original objects. (if that
method itself alters some values)