代码之家  ›  专栏  ›  技术社区  ›  Chris

对传递值的工作原理可能存在误解

  •  -1
  • Chris  · 技术社区  · 7 年前

    假设我有以下代码:

    class Class{
        public String className;
        public Person p;
    }
    
    class Person {
        public String name;
    }
    

    我不能百分之百理解的是:

    Class c = new Class(); // reference of the object oC is stored in var c
    Person p = new Person();// reference of the object oP is stored in var p
    p.name = "StudName"; // value = reference of string s is stored in var p
    c.p = p; // reference of p is updated to point to oP
    p = new Person(); // Confusing Part (1)
    

    p = new Person(); 不会影响中的引用 c p 到新创建的Person对象中的一个?

    c.p = new Person();//(2)
    

    这将影响 c.p

    3 回复  |  直到 7 年前
        1
  •  3
  •   T.J. Crowder    7 年前

    你的问题与传递值无关*。你的问题是关于对象引用的。

    每个变量在堆栈中都有一个给定的空间,并指向对象的引用,对吗?

    将对象引用视为 int

    c.p = p;
    

    中的值 p Person 对象被复制到 c.p ,修改对象的状态 c . 有 c、 p ,它们恰好包含相同的值。

    这就像我们有一个 int i 在课堂上,他做到了:

    int i = 42;
    c.i = i;
    

    这个 在里面 i 被复制到 c.i

    之后 c.p = ... 行,执行此操作时:

    p = new Person(); // Confusing Part (1)
    

    ...它对 c、 p 完全 . 它只是创造了一个新的 对象并将对象引用存储在 仍然具有旧值(对早期对象的引用)。

    但是(1)和(2)之间的区别是什么?

    在(1)中,您将分配给 p c、 p ; 这样做不会影响 .

    让我们按照第一个代码块进行操作,但我将使用 Container 而不是 Class 班完成此操作后:

    Container c = new Container();
    Person p = new Person();
    p.name = "StudName";
    

    你的记忆中有这样的东西:

                     +−−−−−−−−−−−−−−−−−+
    c: [Ref11325]−−−>|   (Container)   |
                     +−−−−−−−−−−−−−−−−−+
                     | className: null |
                     | p: null         |
                     +−−−−−−−−−−−−−−−−−+
    
                                             +−−−−−−−−−−−−−−−−−−+
    p: [Ref21354]−−−−−−−−−−−−−−−−−−−−−−−−−−−>|    (Person)      |
                                             +−−−−−−−−−−−−−−−−−−+    +−−−−−−−−−−−−+
                                             | name: [Ref54312] |−−−>|  (String)  |
                                             +−−−−−−−−−−−−−−−−−−+    +−−−−−−−−−−−−+
                                                                     | "studName" |
                                                                     +−−−−−−−−−−−−+
    

    (省略了许多细节;例如,字符串实际上是指 char[]

    Ref11325 在里面 , Ref21354 p Ref54312 在里面 name 字段只是为了显示它们包含引用;我们从未看到引用的实际值。

    然后,当您这样做时:

    c、 p=p;
    

    你有(唯一的变化是 c p ,当然):

                     +−−−−−−−−−−−−−−−−−+
    c: [Ref11325]−−−>|   (Container)   |
                     +−−−−−−−−−−−−−−−−−+
                     | className: null |
                     | p: [Ref21354]   |−−+
                     +−−−−−−−−−−−−−−−−−+  |
                                          |
                                          \    +−−−−−−−−−−−−−−−−−−+
    p: [Ref21354]−−−−−−−−−−−−−−−−−−−−−−−−−−+−−>|    (Person)      |
                                               +−−−−−−−−−−−−−−−−−−+    +−−−−−−−−−−−−+
                                               | name: [Ref54312] |−−−>|  (String)  |
                                               +−−−−−−−−−−−−−−−−−−+    +−−−−−−−−−−−−+
                                                                       | "studName" |
                                                                       +−−−−−−−−−−−−+
    

    看看怎么做 参考21354 复制自 c、 p

    最后,当你这样做时:

    p = new Person();
    

                     +−−−−−−−−−−−−−−−−−+
    c: [Ref11325]−−−>|   (Container)   |
                     +−−−−−−−−−−−−−−−−−+
                     | className: null |       +−−−−−−−−−−−−−−−−−−+                       
                     | p: [Ref21354]   |−−−−−−>|    (Person)      |                       
                     +−−−−−−−−−−−−−−−−−+       +−−−−−−−−−−−−−−−−−−+    +−−−−−−−−−−−−+
                                               | name: [Ref54312] |−−−>|  (String)  |
                                               +−−−−−−−−−−−−−−−−−−+    +−−−−−−−−−−−−+
                                                                       | "studName" |
                                                                       +−−−−−−−−−−−−+
    
                                               +−−−−−−−−−−−−−−−−−+
    p: [Ref34851]−−−−−−−−−−−−−−−−−−−−−−−−−−−−−>|    (Person)     |
                                               +−−−−−−−−−−−−−−−−−+
                                               | name: null      |
                                               +−−−−−−−−−−−−−−−−−+
    

    p 现在包含一个新的引用,但这对


    *“按值传递”和“按引用传递”是艺术术语,与将变量传递到函数时发生的情况有关:

    doSomething(someVariable);
    

    在传递值中 价值 someVariable 被传递到 doSomething . 在传递引用中,a 某些变量 变量传递到 . 在传递引用中,函数可以延伸并更改变量的内容。在传递值中,它不能。

    变量

        2
  •  1
  •   Mikita Berazouski    7 年前

    在准备OCA和OCP认证的过程中,我发现了一个非常好的建议——在纸上绘制每个关系。

    在您的例子中,对象、值和引用之间的关系如下。 enter image description here

    在操作#5中,您将删除在操作#2期间创建的连接(请参见虚线)。

    插图应该比文字解释更有帮助。希望这会有所帮助。

        3
  •  0
  •   David    7 年前

    执行此操作时:

    Person p = new Person();
    

    Person 是在堆(第一个实例)上创建的,只要有东西引用它就存在。 p 是堆栈上的变量,其值是对该实例的引用。

    p = new Person();
    

    的实例 p 为该实例指定了引用的新值。前一个实例(第一个实例)不再对其有任何引用,并且被垃圾收集。

    执行此操作时:

    c.p = new Person();
    

    再次在堆上创建(第三个实例)。局部变量 p 当然没有受到影响,所以“二审”也没有受到影响。局部变量 c 还指向 Class 在堆上,这仍然是它的第一个实例。该实例包括一个名为 并更新其值以指向新实例(第三个实例)。

    这些变量中的“值”只是对象所在堆中的一种地址。