Search code examples
javapass-by-referencepass-by-valuestringbuffer

revisiting the pass by value semantics in Java as posted by Jon Skeet in his article


I was going through this excellent article on Java reference semantics by Jon Skeet, where he states that

We assume the presence of a procedure named f that takes a formal parameter s. We call that function giving it an actual parameter g.

The calling code:

         f( g )

The function:

   procedure f( s )
   begin
     -- body of the procedure
   end;

All object instances in Java are allocated on the heap and can only be accessed through object references. So if I have the following:

       StringBuffer g = new StringBuffer( "Hello" );

The variable g does not contain the string "Hello", it contains a reference (or pointer) to an object instance that contains the string "Hello".

So if I then call f( g ), f is free to modify its formal parameter s to make it point to another StringBuffer or to set it to null. The function f could also modify the StringBuffer by appending " World" for instance. While this changes the value of that StringBuffer, the value of that StringBuffer is NOT the value of the actual parameter g.

my understanding could be wrong. the program below does change the Stringbuffer passed to the method

public class MutabilityStringBuffer {
    public static void main(String[] args){
        StringBuffer sb = new StringBuffer("hello");
        System.out.println("String before append: "+ sb.toString());
        addString(sb);
        System.out.println("Sting after append "+ sb.toString());

        String s = "hello";
        System.out.println("String before append: "+ s);
        addString(s);
        System.out.println("Sting after append "+ s);

    }
    public static void addString(StringBuffer word){
        word.append(" world!");
    }
    public static void addString(String word){
        word+=" world!";
    }

}

ofcourse, Jon Skeet could not be wrong. But I see that the Stringbuffer can be changed by passing it to method, because stringbuffer is mutable, which is a bit contradictory to what Skeet posted. please clear my understanding here.

Thanks


Solution

  • See comments

    StringBuffer sb = new StringBuffer("hello"); // sb holds reference 
    System.out.println("String before append: "+ sb.toString()); // you print the value 
    addString(sb);  // you use the reference to append to the StringBuffer
    System.out.println("Sting after append "+ sb.toString()); // you print the value
    
    String s = "hello"; // s holds a refernece
    System.out.println("String before append: "+ s); // you print its value
    addString(s); // // the word variable would hold a new reference inside the method 
    System.out.println("Sting after append "+ s); // you print its value
    

    In here

    public static void addString(String word){
        word+=" world!";
    }
    

    The original value of the reference passed to word changes when you reassign it with

    word+=" world!"; 
    

    It goes something like this

    String word = [copy of value of the argument's reference];
    word = word.toString() /* toString() is unnecessary, but just to make the point */ + " world";
    

    where the result of String concatenation is a new String object and therefore a new reference.

    In the following

    public static void addString(StringBuffer word){
        word.append(" world!");
    }
    

    you access the object referenced by word, call a method on that object which internally modifies a char[]. So you've changed the value of the object, not the value of the reference. Changing the reference would look like

    public static void addString(StringBuffer word){
        word = new StringBuffer("Answer to the Ultimate Question of Life, the Universe, and Everything: ");
        word.append("42"); 
    }
    

    The append is performed on a new StringBuffer object, not the one you passed as an argument, proof that the objects are passed by value.