简体   繁体   中英

Confusion over Java's pass-by-value and immutability

In preparation for the SCJP (or OCPJP as it's now known) exam, I'm being caught out by some mock questions regarding pass-by-(reference)value and immutability.

My understanding, is that when you pass a variable into a method, you pass a copy of the bits that represent how to get to that variable, not the actual object itself.

The copy that you send in, points to the same object, so you can modify that object if its mutable, such as appending to a StringBuilder. However, if you do something to an immutable object, such as incrementing an Integer, the local reference variable now points to a new object, and the original reference variable remains oblivious to this.

Consider my example here :

public class PassByValueExperiment
{

    public static void main(String[] args)
    {
        StringBuilder sb = new StringBuilder();
        sb.append("hello");
        doSomething(sb);
        System.out.println(sb);


        Integer i = 0;
        System.out.println("i before method call : " + i);
        doSomethingAgain(i);
        System.out.println("i after method call: " + i);
    }

    private static void doSomethingAgain(Integer localI)
    {
        // Integer is immutable, so by incrementing it, localI refers to newly created object, not the existing one
        localI++;
    }

    private static void doSomething(StringBuilder localSb)
    {
        // localSb is a different reference variable, but points to the same object on heap
        localSb.append(" world");
    }
}

Question : Is it only immutable objects that behave in such a manner, and mutable objects can be modified by pass-by-value references? Is my understanding correct or are there other perks in this behaviour?

There is no difference between mutable and immautable objects on the language level - immutability is purely a property of a class's API.

This fact is only muddled by autoboxing which allows ++ to be used on wrapper types, making it look like an operation on the object - but it's not really, as you've noticed yourself. Instead, it's syntactic sugar for converting the value to a primitive, incrementing that, converting the result back to the wrapper type and assigning a reference to that to the variable.

So the distinction is really between what the ++ operator does when it's used on a primitive vs. a wrapper, which doesn't have anything to do with parameter passing.

Java itself has no idea of whether an object is immutable or not. In every case, you pass the value of the argument, which is either a reference or a primitive value. Changing the value of the parameter never has any effect.

Now, to clarify, this code does not change the value of the parameter:

localSb.append(" world");

That changes the data within the object that the value of the parameter refers to, which is very different. Note that you're not assigning a new value to localSb .

Fundamentally, you need to understand that:

  • The value of an expression (variable, argument, parameter etc) is always either a reference or a primitive value. It's never an object.
  • Java always uses pass-by-value semantics. The value of the argument becomes the initial value of the parameter.

Once you think about those things carefully, and separate in your mind the concepts of "variable", "value" and "object", things should become clearer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM