15

This question already has an answer here:

I've just discovered a huge hole in my Java knowledge. I knew that Java passes parameters by value. I thought I understand that and whenever I needed to edit the field object of a class, I create a field. For example,

private int mNumber;

void main(){
   mNumber = 1;
}

void otherMethod(){
   mNumber = 3;
}

This would change the field object, everyone's happy.

I was debugging and had to review the classes inside Android SDK and I noticed this piece of code

public static String dumpCursorToString(Cursor cursor) {
    StringBuilder sb = new StringBuilder();
    dumpCursor(cursor, sb);
    return sb.toString();
}

public static void dumpCursor(Cursor cursor, StringBuilder sb) {
    sb.append(">>>>> Dumping cursor " + cursor + "\n");
    if (cursor != null) {
        int startPos = cursor.getPosition();
        cursor.moveToPosition(-1);
        while (cursor.moveToNext()) {
            dumpCurrentRow(cursor, sb);
        }
        cursor.moveToPosition(startPos);
    }
    sb.append("<<<<<\n");
}

The are changing the local variable sb by passing it as a parameter to another method.

In my case, sb would be a field and I would change it from another method.

How is this possible? Is a reference (pointer) passed as a parameter so we are ALWAYS changing the source value (just like we pass it by reference in other languages)? Was my approach with field unnecessary all this time?

So my approach could be easily changed to

void main(){
   int number = 1;
   otherMethod(number)
}

void otherMethod(int number){
   number = 3;
}

Don't be angry at me if this sound like a beginner question. It may actually be, but I have just discovered a huge hole in my Java knowledge.

marked as duplicate by user40980, user22815, Bart van Ingen Schenau, Kilian Foth, Michael Kohne Jun 11 '15 at 12:32

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 3
    Possible duplicate: Is Java “pass-by-reference” or “pass-by-value”? – scriptin Jun 6 '15 at 9:26
  • 1
    @scriptin I read this post first, before posting. But it does not explain the logic of the code I posted here. – deviDave Jun 6 '15 at 9:28
  • 3
    It does explain it: "Java is always pass-by-value. The difficult thing to understand is that Java passes objects as references and those references are passed by value." then the accepted answer goes on to explain how this works. – user22815 Jun 7 '15 at 16:08
52

The statement that "java is always pass-by-value" is technically correct, but it can be very misleading, because as you have just witnessed, when you pass an object to a function, the function can modify the contents of the object, so it appears that the object has been passed by reference, and not by value. So, what is happening?

Pointers. That's what's happening.

Pointers are everywhere

One of the major selling points of Java was that "it is a safe language because it is free of pointers".

This statement is true in the sense that you cannot do most of the unsafe things like pointer arithmetic that have historically been the cause of many a monstrous bug in languages like C and C++. However, this statement has also hindered the understanding of java for many a novice programmer.

You see, java is actually full of pointers; Novice programmers only begin to make sense out of the language once they realize that in java everything which is not a primitive is in fact a pointer; pointers are everywhere; you cannot declare an object statically, or on the stack, or as a element of an array as you can do in C++; the only way to declare and use an object is by pointer; even arrays of objects are in fact nothing but arrays of pointers to objects; everything that looks like an object in java is never really an object, it is always a pointer to an object.

Java makes a clear distinction between primitives and objects. Primitives, (int, boolean, float, etc.,) also known as value types, are always passed by value, and they behave precisely as you would expect according to the "java is always pass-by-value" maxim.

Objects, on the other hand, also known as reference types, are always accessed by reference, that is, via a pointer, so their behavior is a bit trickier. When you call a method passing an object to it, java does not really pass the object itself; it passes a pointer to it. This pointer is passed by value, so the maxim "java is always pass-by-value" still holds from a strictly technical perspective. If you set this pointer to null within the method, the pointer held by the caller is unaffected, so obviously, what you have inside the method is a copy of the pointer. But if you modify the object pointed by the pointer, the caller will always see these modifications, which means that both the caller and the callee are holding references to the exact same object.

So, the statement "java is always pass-by-value" is true from a strictly technical, but ultimately not very useful point of view, according to which objects are never really passed anywhere, only pointers to the objects are passed.

In reality, when you write code, when you think what the code does, it is very useful to think of objects as being passed to functions, and since java passes pointers (by value) under the hood, the objects themselves always appear to be passed by reference.

  • 3
    Yes, that's the whole I had. I actually new this, it's not new to me, but I completely forgot it. – deviDave Jun 6 '15 at 9:32
  • 11
    Java is always pass-by-value. When you pass a variable of a non-primitive type, you still pass it by value, which just happens to be a pointer (to an object). – scriptin Jun 6 '15 at 9:38
  • 6
    @scriptin: I very much prefer to use the term "pointer" in this discussion, because using the term "reference" here is confusing, since it means something very different from what it means as part of the compound term "pass-by-reference". – Jörg W Mittag Jun 6 '15 at 9:41
  • 3
    @scriptin * sigh * – Mike Nakis Jun 6 '15 at 10:54
  • 3
    My "intuitive" understanding of 'ref' is fine, your interpretation of my understanding is incorrect. Yannis Rizos will probably show up any moment now to put an end to this lengthy debate in comments, so, if anyone has not had enough yet, please consult this discussion. The answer with the 1900+ upvotes says: "Java is always pass-by-value. The difficult thing to understand is that Java passes objects as references and those references are passed by value". (Which renders the "Java is always pass-by-value" statement pointless bickering.) – Mike Nakis Jun 6 '15 at 13:36
9

In short, you are confusing reassigning a local variable with modifying a shared object. sb.append() does not modify the variable sb, it modifies the object that the variable references. But number = 3 modifies the variable number by assigning it a new value. Objects are shared across method calls, but local variables are not. Therefore assigning a local variable a new value will not have any effect outside the method, but modifying an object will. A bit simplified: if you use a dot (.), you are modifying an object, not a local variable.


You confusion is probably due to confusing terminology. "Reference" means two different things in different context.

1) When talking about Java objects and variables, a reference is a "handle" to an object. Strictly speaking, a variable cannot hold an object, it holds a reference to an object. References can be copied, so two variables can get a reference to the same object. E.g.

StringBuilder a = new StringBuilder();
StringBuilder b = a; // a and b now references the same object
a.writeLine("Hello"); // modify the shared object
b.toString() // -> "Hello"

When an objects is passed into a method as a parameter, it is also the reference that is copied, so the called method can modify the same object. This is also called "Call by sharing".

void main(){
   String builder b = new StringBuilder();
   otherMethod(b);
   b.toString(); // --> "Hello" because the shared object is modified
}

void otherMethod(StringBuilder b){
   b.write("Hello"); // modify the shared object
}

But the otherMethod can only modify the shared object, it cannot modify any local variables in the calling method. If the called function assigns a new value to its own parameters, it doesn't affect the calling function.

void main(){
   String builder b = new StringBuilder();
   b.writeLine("Hello"); 
   otherMethod(b);
   b.toString(); // --> "Hello" because the shared object is NOT modified
}

void otherMethod(StringBuilder b){
   b = new StringBuilder(); // assign a new object to b
   b.write("Goodbye"); // modify the new object
}

So you have to be clear on the distinction between modifying a shared object (through modifying fields or calling method on the object) and assigning a new value to a local variable or parameter.

2) When talking evaluation strategy (how parameters are passed into function) in general computer language theory, call-by-reference and call-by-value means something quite different.

Call-by-value, which Java supports, means that values from arguments are copied into parameters when calling methods. The value can be a reference, which is why the called function will get a reference to the shared object. Confusingly Java is said to "pass references by value", which is quite distinct from call-by-reference.

If a language supports call-by-reference, a function can change the value of a local variable in the calling function. Java does not support this, which is shown by the example above. Call-by-reference means the called function gets a reference to the local variable used as argument in the call. This notion is completely foreign to Java, where we simply do not have the concept of a reference to a local variable. We only have references to objects, never to local variables.

To show how call-by-reference works, we can look at C# which do support call-by-reference with the ref modifier:

void main(){
   String builder b = new StringBuilder();
   b.writeLine("Hello"); 
   otherMethod(ref b);
   b.toString(); // --> "Goodbye" because otherMethod changed b to point to the new object
}

void otherMethod(ref StringBuilder b){
   b = new StringBuilder(); // assign a new object to b
   b.write("Goodbye"); // modify the new object
}

Here otherMethod() actually modifies the local variable b in main().

So what about primitives like integers, booleans etc.? These are immutable which means they cannot be modified like objects can. Since they cannot be modified, the question if they are shared or not is moot, since there is no observable difference whether they are shared or copied.


Note: I didn't invent the terminology, so don't blame the messenger. I much prefer the terms call-by-sharing or call-by-object which is more intuitive for Java, but the fact remains that the terms call-by-value and call-by-reference is established terminology, and the Java specification itself uses the term call-by-value.

  • 1
    "When an objects is passed into a method as a parameter," "Objects" are not values in Java (there are no "object types") and cannot be "passed". If you can explain that, then everything else follows. – user102008 Jun 9 '15 at 2:15

Not the answer you're looking for? Browse other questions tagged or ask your own question.