///Pass-by-value Semantics in Java Applications

Pass-by-value Semantics in Java Applications

Pass-by-value semantics in Java applications

A few months ago, developerWorks posted some excerpts from my book, Practical Java, published by Addison-Wesley. I will initially use this column on developerWorks to answer some questions asked by readers and to respond to various comments about the excerpts.

The excerpt Understanding that parameters are passed by value and not by reference described the one and only parameter-passing mechanism in Java applications, pass by value. It was written to debunk the commonly held myth that Java applications pass parameters by reference, to avoid common programming mistakes that result from relying on pass-by-reference behavior.

Some of the feedback on this excerpt suggested that I was confusing the issue or was altogether wrong about it. Many of the readers who disagreed with me used the C++ language as an example. Therefore, in this column, I will use C++ and Java applications to further clarify the facts.

The key point

 After reading all of the comments, it became clear that there is at least one major point of confusion. Some comments said that my excerpt is wrong because objects are passed by reference. It is true that objects are passed by reference; the excerpt was not contradicting that. The excerpt said that all parameters are passed by value — a different argument. It is correct that you never pass an object in a Java application, only an object reference. Therefore, you are passing objects by reference. However, it is important to distinguish how parameters are passed, and that was the intent of the excerpt. The fact that a Java application passes objects by reference does not mean that a Java application passes parameters by reference. Parameters can be object references, and a Java application passes object references by value.

Parameter passing in C++ and Java applications

 

Variables in Java applications can be one of two types: reference types or primitive types. Both types are handled the same way when passed as arguments to a method. Both are passed by value; neither is passed by reference. This is an important distinction as the subsequent code examples illustrate.

Before going further, it is important to define the terms pass by value and pass by reference. Pass by value means that when an argument is passed to a function, the function receives a copy of the original value. Therefore, if the function modifies the parameter, only the copy is changed and the original value remains unchanged. Pass by reference means that when an argument is passed to a function, the function receives the memory address of the original value, not a copy of the value. Therefore, if the function modifies the parameter, the original value in the calling code is changed.

Some of the confusion about parameter passing in Java applications originates from the fact that many programmers came to Java programming from C++ programming. C++ contains both non-reference types and reference types and passes them by value and by reference, respectively. The Java programming language has primitive types and object references, and therefore, it is logical to think that Java applications use pass by value for the primitive types and pass by reference for references, much like C++. After all, you might think if you are passing a reference, it must be pass by reference. It is tempting to believe this, and in fact it was my belief for some time, but it is not true.

In both C++ and Java applications, when a parameter to a function is not a reference, you pass a copy of the value (pass by value). The difference is with references. In C++ when a parameter to a function is a reference, you are passing the reference, or the memory address (pass by reference). In Java applications, when an object reference is a parameter to a method, you are passing a copy of the reference (pass by value), not the reference itself. Note that the calling method’s object reference and the copy are pointing to the same object. This is an important distinction. A Java application does nothing differently when passing parameters of varying types like C++ does. Java applications pass all parameters by value, thus making copies of all parameters regardless of type.

Examples

Using the preceding definitions and discussion, we will analyze some examples. First consider some C++ code. The C++ language uses both pass-by-value and pass-by-reference argument passing mechanisms:Listing 1: C++ example



#include
#include

void modify(int a, int *P, int &r);

int main (int argc, char** argv)
{
int val, ref;
int *pint;

val = 10;
ref = 50;
pint = (int*)malloc(sizeof(int));
*pint = 15;

printf("val is %d\n", val);
printf("pint is %d\n", pint);
printf("*pint is %d\n", *pint);
printf("ref is %d\n\n", ref);

printf("calling modify\n");
//val and pint passed by value, ref is passed by reference.
modify(val, pint, ref);
printf("returned from modify\n\n");

printf("val is %d\n", val);
printf("pint is %d\n", pint);
printf("*pint is %d\n", *pint);
printf("ref is %d\n", ref);

return 0;
}

void modify(int a, int *p, int &r)
{
printf("in modify...\n");
a = 0;
*p = 7;
p = 0;
r = 0;
printf("a is %d\n", a);
printf("p is %d\n", p);
printf("r is %d\n", r);
}

The output of this code is:

Listing 2: Output of C++ code



val is 10
pint is 4262128
*pint is 15
ref is 50

calling modify
in modify...
a is 0
p is 0
r is 0
returned from modify

val is 10
pint is 4262128
*pint is 7
ref is 0

This code declares three variables: two ints and one pointer. Each variable is set to an initial value and then printed. The pointer value is printed along with what it points to. All three variables are then passed as parameters to the modify function. The first two parameters are passed by value and the last parameter is passed by reference. The function prototype for the modify function shows that the last parameter is to be passed as a reference. Recall that C++ passes all parameters by value, except references, which are passed by reference.

The modify function changes the values of all three parameters:

  • The first parameter is set to 0.
  • The value the second parameter points to is set to 7, then the second parameter is set to 0.
  • The third parameter is set to 0.

The new values are printed, then the function returns. When execution returns to main, the values of the three variables, along with the value the pointer points to, are printed again. The variables passed as the first and second parameters are not affected by the modify function because they were passed by value. The value that the pointer points to did change, however. Notice that unlike the first two parameters, the variable passed as the last parameter is changed by the modify function because it is passed by reference.

Now consider similar code written in the Java language:

Listing 3: Java application



class Test
{
public static void main(String args[])
{
int val;
StringBuffer sb1, sb2;

val = 10;
sb1 = new StringBuffer("apples");
sb2 = new StringBuffer("pears");
System.out.println("val is " + val);
System.out.println("sb1 is " + sb1);
System.out.println("sb2 is " + sb2);
System.out.println("");

System.out.println("calling modify");
//All parameters passed by value
modify(val, sb1, sb2);
System.out.println("returned from modify");
System.out.println("");

System.out.println("val is " + val);
System.out.println("sb1 is " + sb1);
System.out.println("sb2 is " + sb2);
}

public static void modify(int a, StringBuffer r1,
StringBuffer r2)
{
System.out.println("in modify...");
a = 0;
r1 = null; //1
r2.append(" taste good");
System.out.println("a is " + a);
System.out.println("r1 is " + r1);
System.out.println("r2 is " + r2);
}
}

The output of this code is:

Listing 4: Output of Java application



val is 10
sb1 is apples
sb2 is pears

calling modify
in modify...
a is 0
r1 is null
r2 is pears taste good
returned from modify

val is 10
sb1 is apples
sb2 is pears taste good

This code declares three variables: one int and two object references. Each variable is set to an initial value and printed. All three variables are then passed as parameters to the modify method.

The modify method changes the values of all three parameters.

  • The first parameter, the int, is set to 0.
  • The first object reference, r1, is set to null.
  • The second object reference, r2, retains its value but changes what it refers to by calling the append method. (This is analogous to what was done with the pointer, p, in the previous C++ example.)

When execution returns to main, the values of the three variables are printed again. The int val is unchanged as expected. The object reference sb1 is also unchanged. If sb1 was passed by reference, as many people claim, it would be null. However, since the Java programming language passes all parameters by value, a copy of the reference for sb1 is passed to the modify method. When the modify method set r1 to null at //1, it was doing so on a copy of the sb1 reference, not the original as was done in C++.

Also, note that the second object reference, sb2, prints out the new string that was set in the modify method. Even though the variable r2 in the modify method is a copy of the reference sb2, they both refer to the same object. Therefore, methods called on the copied reference change the same object.

Writing a swap method

Given what we know about how parameters are passed, writing a swap function in C++ can be accomplished in different ways. A swap function using pointers, which are passed by value, looks like this:Listing 5: Swap function using pointers



#include
#include

void swap(int *a, int *b);

int main (int argc, char** argv)
{
int val1, val2;
val1 = 10;
val2 = 50;
swap(&val1, &val2);
return 0;
}

void swap(int *a, int *b)
{
int temp = *b;
*b = *a;
*a = temp;
}

A swap function using references, which are passed by reference, looks like this:

Listing 6: Swap function using references



#include
#include

void swap(int &a, int &b);

int main (int argc, char** argv)
{
int val1, val2;
val1 = 10;
val2 = 50;
swap(val1, val2);
return 0;
}

void swap(int &a, int &b)
{
int temp = b;
b = a;
a = temp;
}

Both C++ code examples swap the values as expected. If Java applications used pass by reference, the following swap method would work like the C++ examples:

Listing 7: Java swap function if pass by reference worked like in C++



class Swap
{
public static void main(String args[])
{
Integer a, b;

a = new Integer(10);
b = new Integer(50);

System.out.println("before swap...");
System.out.println("a is " + a);
System.out.println("b is " + b);
swap(a, b);
System.out.println("after swap...");
System.out.println("a is " + a);
System.out.println("b is " + b);
}

public static void swap(Integer a, Integer b)
{
Integer temp = a;
a = b;
b = temp;
}
}

Because the Java application passes all parameters by value, this code does not work and produces the following output:

Listing 8: Output from Listing 7



before swap...
a is 10
b is 50
after swap...
a is 10
b is 50

So how do you write a method in a Java application to swap the values of two primitive types or two object references? Because a Java application passes all parameters by value, you cannot. To swap the values, you must do so inline, outside of a method call.

Resources

  • Discover a practical approach to producing efficient Java code in the Practical Java Programming Language Guide , by Peter Haggar.
  • Learn how to effectively exploit Java’s constructs, libraries, and language details in the Java Programming Language, 3rd Edition, by Ken Arnold, David Holmes, and James Gosling.
  • Read other excerpts from Practical Java that have been published on developerWorks.
  • 2010-05-26T17:11:04+00:00 November 24th, 2003|Java|0 Comments

    About the Author:

    Leave A Comment