Do Java methods use pass-by-reference or pass-by-value?
Author: Deron Eriksson
Description: This tutorial describes how arguments are passed to methods in Java.
Tutorial created using: Windows XP || JDK 1.5.0_09 || Eclipse Web Tools Platform 2.0 (Eclipse 3.3.0)


Page: < 1 2

(Continued from page 1)

First off, a primitive type such as int is passed by value. When a primitive type is passed as an argument to a method, a copy is made of that type's value. So, if an int 'a' is an argument to a method and 'a' is changed within this method, outside of the method, 'a' retains its original value:

public class Test {

	public static void main(String[] args) throws IOException {
...
		int a = 1;
		System.out.println("a (in main) is: " + a);
		doSomething(a);
		System.out.println("a (in main) is: " + a);
...
	}
...
	public static void doSomething(int a) {
		a = 2;
		System.out.println("a (in doSomething) is: " + a);
	}
...
}

The output of the above snippet is shown here. We can see that the value of 'a' changes within the doSomething() method, but outside of this method, 'a' doesn't change since a copy of the type was made for the method.

a (in main) is: 1
a (in doSomething) is: 2
a (in main) is: 1

Now, let's look at what happens if we pass an object as a method argument. In this case, once again a copy is made, so it is pass-by-value, but this copy is a reference to the object. So, since this is a reference, we can change the values in the original object, since we have a reference pointing to the original object. However, a very important point, which we shall see soon, is that we can change what this new reference points to (such as to have it point to null), but changing where the reference in the method points to doesn't change where the original reference outside of the method points to.

public class Test {

	public static void main(String[] args) throws IOException {
...
		MyTest myTest = new MyTest();
		System.out.println("\nmyTest's x (in main) is: " + myTest.getX());
		doSomething(myTest);
		System.out.println("myTest's x (in main) is: " + myTest.getX());
...
	}
...
	public static void doSomething(MyTest myTest) {
		myTest.setX(2);
		System.out.println("myTest's x (in doSomething) is: " + myTest.getX());
	}
...
}

Our output is shown here. We can see that we changed the value of the myTest object's x field. In proper terms, I guess we should say that we changed the x field of the object that the myTest reference in our main method points to.

myTest's x (in main) is: 1
myTest's x (in doSomething) is: 2
myTest's x (in main) is: 2

Now, let's look at Strings. Up to this point, things seem pretty straightforward. A String is an object, so a common mistake (which I made several times when I first started learning JavaSW) is to expect that if we pass a String as an argument to a method, we can change the String value outside of the method if we do a String assignment within the method. However, we actually can't do this, which is illustrated with the following code:

public class Test {

	public static void main(String[] args) throws IOException {
...
		String s = "str1";
		System.out.println("\ns (in main) is: " + s);
		doSomething(s);
		System.out.println("s (in main) is: " + s);
...
	}
...
	public static void doSomething(String s) {
		s = "str2";
		System.out.println("s (in doSomething) is: " + s);
	}
...
}

The output is shown here:

s (in main) is: str1
s (in doSomething) is: str2
s (in main) is: str1

Although a String is a class (rather than a primitive type), the String value back in main retained its original value after we changed it in the method. What happened? Remember above when i stated that: "... a very important point, which we shall see soon, is that we can change what this new reference points to (such as to have it point to null), but changing where the reference in the method points to doesn't change where the original reference outside of the method points to." When we do an assignment operator with a String reference, we are actually creating a new String object for the reference to point to. Strings is Java are "immutable", which means that the value of a String object can't change after the String has been created. So, the call to 's = "str2";' actually creates a new String object for the String reference 's' in the method to point to. So, the 's' reference in the doSomething() method points to a new String object with the value "str2", but the 's' reference in the main() method still points to the String object with value "str1".


Let's check out one more example to highlight the point that references are passed-by-value in Java, meaning that we get a new reference to an object when we pass a reference to an object as a method parameter. However, this is a reference to the object and not the actual object, so changing where this reference in a method points to doesn't alter where the original reference outside of the method points to.

If we take an object reference and assign it the value of null, the reference now points to no object. Trying to call methods on nothing results in a NullPointerException. This is illustrated below with the myTest2 reference, which throws a NullPointerException when we assign it null and then try to call myTest2.getX() on null.

public class Test {

	public static void main(String[] args) throws IOException {
...
		try {
			MyTest myTest2 = new MyTest();
			myTest2 = null;
			System.out.print("\nmyTest2's x (in main) is: ");
			System.out.println(myTest2.getX());
		} catch (RuntimeException e) {
			System.out.println(e);
		}

		MyTest myTest3 = new MyTest();
		System.out.println("\nmyTest3's x (in main) is: " + myTest3.getX());
		doSomethingAndNull(myTest3);
		System.out.println("myTest3's x (in main) is: " + myTest3.getX());
...
	}
...
	public static void doSomethingAndNull(MyTest myTest3) {
		myTest3.setX(3);
		System.out.println("myTest3's x (in doSomethingAndNull) is: " + myTest3.getX());
		myTest3 = null;
	}
...
}

Examine the myTest3 reference in main to a MyTest object. We call the doSomethingAndNull() method with myTest3 as an argument. In this method, we change the value of the 'x' field of the object that the myTest3 reference points to. In the method, we then point the myTest3 reference to null. However, notice that back in the main() method, we can then call the myTest3.getX() method and display the value of the object. We don't run into a NullPointerException. This is because the myTest3 reference in main() still points to the original MyTest object. The null assignment in doSomethingAndNull() pointed the myTest3 reference within the doSomethingAndNull() method to null, but this did not alter where the myTest3 reference in main() was pointing to.

The console output of this code snippet is shown here:

myTest2's x (in main) is: java.lang.NullPointerException

myTest3's x (in main) is: 1
myTest3's x (in doSomethingAndNull) is: 3
myTest3's x (in main) is: 3

Hopefully this tutorial has helped clarify some of the nuances involved with understanding how pass-by-value works in Java for primitive types and reference types.


The complete console output of executing Test is shown here.

Console output from executing the Test class

a (in main) is: 1
a (in doSomething) is: 2
a (in main) is: 1

myTest's x (in main) is: 1
myTest's x (in doSomething) is: 2
myTest's x (in main) is: 2

s (in main) is: str1
s (in doSomething) is: str2
s (in main) is: str1

myTest2's x (in main) is: java.lang.NullPointerException

myTest3's x (in main) is: 1
myTest3's x (in doSomethingAndNull) is: 3
myTest3's x (in main) is: 3
Page: < 1 2