This basic question is in any Java 101 courses and the answer is quite obvious. But in fact things are a little bit trickier than they seem to be.
I Definition
First let’s clarify some words:
- Value: we call by value the real “content” of a data. Basic data type can be a numeric value, a character or an array of character (String). Complex types are a combination of these basic types.We can represent the value in the main program memory portion as block of bytes representing the data content. Each block of bytes is defined by its address in hexadecimal.
- Reference: a reference is merely a value pointing to another value. For those who know the old good C language, it is just a pointer. Please note that the reference itself is usually a long value of 32-bit (or 64-bit on 64-bit platforms). The value of the reference on an object is simply the address in memory of the object being refered to.
Example 1:
private int myNumber = 12345; private String text = "another";
The visual representation of these variables in the program memory is:
- pass by: when we say “pass by”, we mean parameter passing and returning for method calls.
II Pass by value
All primitive types are passed by value, meaning that when a method is called, all the primitives parameters taken from local context are copied and the copies are passed to the method. In that sense we said that the parameters are “passed by value” because it is the value of the primitives variables which is passed.
Example 2:
... private void myMethod() { int myNumber = 12345; String myString = "testing"; System.out.println("myNumber before call = "+myNumber); this.changeMyNumber(myNumber); System.out.println("myNumber after call = "+myNumber); } private void changeMyNumber(int justACopy) { justACopy = 10; System.out.println("justACopy = "+myNumber); } ...
The console output is:
myNumber before call = 12345
justACopy = 10
myNumber after call = 12345
The result is not surprising. To understand what happened under the hood, let’s have a look into the memory zone:
When calling changeMyNumber(), the “myNumber” primitive value is copied and passed to the method as variable “justACopy”. Now, whatever value we assign to “justACopy”, it does not affect the original value of “myNumber”.
The denomination pass-by-value is indeed incorrect, the proper name should be pass-by-copy-of-value because we do not pass the content of the variable itself but rather a copy of the content.
III Pass by reference
For non primitive variables, all parameters calls and returnings for method are done with references. When calling a method, all the references of non-primitive parameters taken from local context are copied and the copies of these references are passed to the method.
Example 3:
... private void myMethod() { int myNumber = 12345; StringBuffer myString = new StringBuffer("testing"); System.out.println("myString before call = "+myString.toString()); this.changeMyString(myString); System.out.println("myString after call = "+myString.toString()); } private void changeMyString(StringBuffer justACopy) { justACopy = new StringBuffer("another String"); System.out.println("justACopy = "+justACopy.toString()); } ...
The console output is:
myString before call = testing
justACopy = another String
myString after call = testing
The result is this time surprising. We expect that the “myString” variable will be the newly created StringBuffer and contains “another String” but it’s not the case. Let’s see what happened under the hood:
When calling changeMyString(), the reference to “myString” (0x25a1c) is copied and passed to the method. In the changeMyString() method context this reference is named “justACopy”. At that point we have 2 references pointing to the same memory location of the StringBuffer containing the “testing” value.
When doing justACopy = new StringBuffer(“anotherString”); a new StringBuffer is created in memory at the address 0x42f59 and now the “justACopy” variable is pointing to this new location. In fact the old reference “myString” is still pointing to “testing”. What is changed by the method changeMyString() is the copy of “myString”, not “myString” itself. That explains the output we got.
Again the pass-by-reference naming is incorrect, the proper name should by pass-by-copy-of-reference.
If we want to change the value of “myString” in the changeMyString() method, we could have done:
private void changeMyString(StringBuffer justACopy) { justACopy.clear(); justACopy.append("anotherString"); System.out.println("justACopy = "+justACopy.toString()); }
Rather than creating a new StringBuffer whose lifecyle is limited to the scope of the changeMyString() method, we could have changed the content of the original StringBuffer.
Really good one i never seen like this example any where…. thanks a lot!!!!!!!!!!!!!!!!!!!!!!
Happy thati t helped 😀
Pingback: Final variable in Java « DuyHai's Java Blog
Good article, but I think you made a typo in the third example:
“private void changeMyString(int justACopy)”
The parameter should be a StringBuffer, shouldn’t it?
Yes you’re right, it was a bad copy/paste. I fixed it. Thanks 🙂