为了账号安全,请及时绑定邮箱和手机立即绑定

Java是“通过引用传递”还是“传递价值”?

Java是“通过引用传递”还是“传递价值”?

守着星空守着你 2019-05-20 15:21:34
我一直认为Java是传递引用的。但是,我看过一些博客文章(例如,这个博客)声称它不是。我不认为我理解他们所做的区别。解释是什么?
查看完整描述

6 回答

?
冉冉说

TA贡献1877条经验 获得超1个赞

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 "Fifi"
    d = new Dog("Fifi");
    d.getName().equals("Fifi"); // true}

在上面的例子中aDog.getName()仍将返回"Max"。值aDogmain未在功能改变fooDog "Fifi"作为对象基准由值来传递。如果它是通过引用传递的,则aDog.getName()in main"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(...)。任何操作foo执行上d是这样的,对于所有的实际目的,它们被执行的aDog,但它是不是可以改变变量的值aDog本身。


查看完整回答
反对 回复 2019-05-20
?
缥缈止盈

TA贡献2041条经验 获得超4个赞

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

Java规范说Java中的所有内容都是按值传递的。在Java中没有“pass-by-reference”这样的东西。

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

Dog myDog;

不是狗; 它实际上是指向狗的指针

这意味着,就在你拥有的时候

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

你实际上是将创建的对象的地址传递Dogfoo方法。

(我说的主要是因为Java指针不是直接地址,但最容易想到它们)

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

如果方法被定义为

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

让我们来看看发生了什么。

  • 参数someDog设置为值42

  • 在线“AAA”

    • someDog跟随Dog它指向(Dog地址42处的对象)

    • 要求Dog(地址为42的那个)将他的名字改为Max

  • 在线“BBB”

    • Dog创建了一个新的。让我们说他在地址74

    • 我们将参数分配someDog给74

  • 在线“CCC”

    • someDog跟随Dog它指向(Dog地址74处的对象)

    • 要求Dog(地址为74的那个)将他的名字改为Rowlf

  • 然后,我们回来了

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

没有myDog改变?

关键是。

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

按照地址并改变最后的内容是完全有效的; 但是,这不会改变变量。

Java的工作方式与C完全相同。您可以分配指针,将指针传递给方法,按照方法中的指针操作并更改指向的数据。但是,您无法更改指针指向的位置。

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

如果Java具有pass-by-reference语义,那么foo我们在上面定义的方法会在BBB上myDog分配的位置发生变化someDog

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


查看完整回答
反对 回复 2019-05-20
?
神不在的星期二

TA贡献1963条经验 获得超6个赞

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

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

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

  • 学生:师父,这是否意味着Java是传递参考?

  • 师父:蚱蜢,没有。

现在想想Object的引用/变量是什么/是什么:

  1. 变量保存位,告诉JVM如何到达内存中引用的Object(Heap)。

  2. 将参数传递给方法时,您不会传递引用变量,而是传递引用变量中的位副本。像这样:3bad086a。3bad086a代表了一种获取传递对象的方法。

  3. 所以你只是传递3bad086a,它是参考的价值。

  4. 您传递的是引用的值而不是引用本身(而不是对象)。

  5. 该值实际上是COPIED并赋予方法

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

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 nice6.
 static void changeName(Person anotherReferenceToTheSamePersonObject) {7.     anotherReferenceToTheSamePersonObject.setName("Jerry");8. }

怎么了?

  • 变量person在第1行中创建,并且在开头时为null。

  • 在第2行创建一个新的Person对象,存储在内存中,并为变量person提供对Person对象的引用。那就是它的地址。比方说3bad086a。

  • 保存Object的地址的变量person被传递给第3行中的函数。

  • 在第4行,你可以听到沉默的声音

  • 检查第5行的评论

  • 创建一个方法局部变量 - anotherReferenceToTheSamePersonObject - 然后在#6行中产生魔力:

    • 变量/引用被逐位复制并传递给函数内的anotherReferenceToTheSamePersonObject

    • 没有创建Person的新实例。

    • “ person ”和“ anotherReferenceToTheSamePersonObject ”都保持相同的3bad086a值。

    • 不要试试这个,但是人== anotherReferenceToTheSamePersonObject会是真的。

    • 两个变量都具有引用的IDENTICAL COPIES,它们都引用相同的Person对象,堆上的SAME对象而不是COPY。

    • 请注意,anotherReferenceToTheSamePersonObject箭头指向Object而不是指向变量person!

如果你没有得到它,那么只要相信我,并记住最好说Java是通过值传递的。那么,通过参考值传递。哦,更好的是传递变量值的副本!;)

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

你总是传递一份参考值的副本!

  • 如果它是原始数据类型,则这些位将包含原始数据类型本身的值。

  • 如果它是一个Object,那么这些位将包含告诉JVM如何到达Object的地址值。

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


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


当然,你可以缩短它,只是说 Java是值得传递的!


查看完整回答
反对 回复 2019-05-20
?
慕标琳琳

TA贡献1830条经验 获得超9个赞

Java的始终是按值传递,没有例外,永远

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

所以,在调用方法时

  • 对于原始参数(intlong等),pass by value是基元的实际值(例如,3)。

  • 对于对象,pass by value是对象引用的值。

所以,如果你有doSomething(foo)public void doSomething(Foo foo) { .. }两个FOOS已复制引用指向同一个对象。

当然,通过值传递对对象的引用看起来非常像(并且在实践中无法区分)通过引用传递对象。


查看完整回答
反对 回复 2019-05-20
?
杨魅力

TA贡献1811条经验 获得超5个赞

为了显示对比,请比较以下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只有两种类型的传递:内置类型的值,以及对象类型的指针值。


查看完整回答
反对 回复 2019-05-20
?
翻翻过去那场雪

TA贡献2065条经验 获得超13个赞

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

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

将打印出"Hah!"而不是null。这个工作的原因是因为bar是一个值的副本baz,它只是一个引用"Hah!"。如果它本身就是实际参考,那么foo将重新定义baznull


查看完整回答
反对 回复 2019-05-20
  • 6 回答
  • 0 关注
  • 620 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信