Search code examples
javareferencepass-by-value

Possible missunderstanding of how pass by value works


I'm not sure if I understand how pass-by-value works in Java. I know there are many topics about that, even on SO, but they all don't really fit and I'd like to know if I'm right:

Lets say I've got the following Code:

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

class Person {
    public String name;
}

The thing I don't 100% understand is:

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)

So as far as I understand, each Variable has a given space in the Stack and points to the reference of the object right? So am I correct, that the p = new Person(); doesn't affect the reference in the c Object, because this operation sets the reference of p to the one from the newly created Person object?

I find that hard to understand, because if I do something like that:

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

This will affect the reference of c.p of course. But what's the difference between (1) and (2)?


Solution

  • Your question has nothing to do with pass-by-value*. Your question is about object references.

    each Variable has a given space in the Stack and points to the reference of the object right?

    You have one too many levels of indirection in there. :-) The variable contains the object reference.

    Think of an object reference as an int that tells the JVM where the object is elsewhere in memory. (That's not literally true, but it's a very good way of thinking about it.)

    So in:

    c.p = p;
    

    the value in p (an object reference saying where that Person object is) is copied into c.p, modifying the state of the object c. There is no ongoing relationship between p and c.p, they just happen to contain the same value for the moment.

    This is exactly like if we had an int i in the class and did:

    int i = 42;
    c.i = i;
    

    The value in i is copied into c.i, modifying the state of c.

    After the c.p = ... line, when you do this:

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

    ...it has no effect on c.p at all. It just creates a new Person object and stores the object reference in p. c.p still has the old value (the reference to the earlier object).

    But what's the difference between (1) and (2)?

    In (1), you're assigning to p, not c.p; doing so has no effect on c.p. In (2) you're assigning to c.p.

    Let's follow that first code block through, but I'm going to use Container rather than Class as Java already has a Class class. After you do this:

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

    you have something like this in memory:

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

    (Leaving out many details; the string, for instance, actually refers to a char[] array elsewhere and has several other fields.)

    The Ref11325 in c, Ref21354 in p, and Ref54312 in p's name field are just there to show that they contain references; we never see a reference's actual value.

    Then when you do:

    c.p = p;
    

    you have (only change is c's p, of course):

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

    See how Ref21354 was copied from p to c.p.

    Then finally, when you do:

    p = new Person();
    

    you have this:

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

    p now contains a new reference, but that has no effect at all on c.p.


    * "Pass-by-value" and "pass-by-reference" are terms of art and relate to what happens when you pass a variable into a function:

    doSomething(someVariable);
    

    In pass-by-value, the value of someVariable is passed into doSomething. In pass-by-reference, a reference to the someVariable variable is passed into doSomething. In pass-by-reference, the function can reach out and change the contents of the variable. In pass-by-value, it cannot.

    The word "reference" in "pass-by-reference" has nothing to do with the word "reference" in object references, it's just two things defined using the same overloaded word. The "reference" in pass-by-reference is a reference to a variable, not an object.