Certification Objective Passing Variables into Methods (Objective 7.3)


Certification Objective —Passing Variables into Methods (Objective 7.3)

7.3 Determine the effect upon object references and primitive values when they are passed into methods that perform assignments or other modifying operations on the parameters.

Methods can be declared to take primitives and/or object references. You need to know how (or if) the caller's variable can be affected by the called method. The difference between object reference and primitive variables, when passed into methods, is huge and important. To understand this section, you'll need to be comfortable with the assignments section covered in the first part of this chapter.

Passing Object Reference Variables

When you pass an object variable into a method, you must keep in mind that you're passing the object reference, and not the actual object itself. Remember that a reference variable holds bits that represent (to the underlying VM) a way to get to a specific object in memory (on the heap). More importantly, you must remember that you aren't: even passing the actual reference variable, but rather a copy of the reference variable. A copy of a variable means you get a copy of the bits in that variable, so when you pass a reference variable, you're passing a copy of the bits representing how to get to a specific object. In other words, both the caller and the called method will now have identical copies of the reference, and thus both will refer to the same exact (not a copy) object on the heap.

For this example, we'll use the Dimension class from the java.awt package:

  1. import java.awt.Dimension;  2. class ReferenceTest {  3.   public static void main (String [] args) {  4.     Dimension d = new Dimension(5,10);  5.     ReferenceTest rt = new ReferenceTest();  6.     System.out.println("Before modify() d.height = "                            + d.height);  7.     rt.modify(d);  8.     System.out.println("After modify() d.height = "                           + d.height);  9.   } 10.   void modify(Dimension dim) { 11.     dim.height = dim.height + 1; 12.     System.out.println("dim = " + dim.height); 13.   } 14. } 

When we run this class, we can see that the modify() method was indeed able to modify the original (and only) Dimension object created on line 4.

 C:\Java Projects\Reference>java ReferenceTest Before modify()  d.height = 10 dim = 11 After modify()  d.height = 11 

Notice when the Dimension object on line 4 is passed to the modify() method, any changes to the object that occur inside the method are being made to the object whose reference was passed. In the preceding example, reference variables d and dim both point to the same object.

Does Java Use Pass-By-Value Semantics?

If Java passes objects by passing the reference variable instead, does that mean Java uses pass-by-reference for objects? Not exactly, although you'll often hear and read that it does. Java is actually pass-by-value for all variables running within a single VM. Pass-by-value means pass-by-variable-value. And that means, pass-by-copy-of-the-variable! (There's that word copy again!)

It makes no difference if you're passing primitive or reference variables, you are always passing a copy of the bits in the variable. So for a primitive variable, you're passing a copy of the bits representing the value. For example, if you pass an int variable with the value of 3, you're passing a copy of the bits representing 3. The called method then gets its own copy of the value, to do with it what it likes.

And if you're passing an object reference variable, you're passing a copy of the bits representing the reference to an object. The called method then gets its own copy of the reference variable, to do with it what it likes. But because two identical reference variables refer to the exact same object, if the called method modifies the object (by invoking setter methods, for example), the caller will see that the object the caller's original variable refers to has also been changed. In the next section, we'll look at how the picture changes when we're talking about primitives.

The bottom line on pass-by-value: the called method can't change the caller's variable, although for object reference variables, the called method can change the object the variable referred to. What's the difference between changing the variable and changing the object? For object references, it means the called method can't reassign the caller's original reference variable and make it refer to a different object, or null. For example, in the following code fragment,

 void bar() {    Foo f = new Foo();    doStuff(f) ; } void doStuff(Foo g) {    g.setName("Boo");    g = new Foo(); } 

reassigning g does not reassign f! At the end of the bar() method, two Foo objects have been created, one referenced by the local variable f and one referenced by the local (argument) variable g. Because the doStuff() method has a copy of the reference variable, it has a way to get to the original Foo object, for instance to call the setName() method. But, the doStuff() method does not have a way to get to the f reference variable. So doStuff() can change values within the object f refers to, but doStuff() can't change the actual contents (bit pattern) of f. In other words, doStuff() can change the state of the object that f refers to, but it can't make f refer to a different object!

Passing Primitive Variables

Let's look at what happens when a primitive variable is passed to a method:

 class ReferenceTest {   public static void main (String [] args) {     int a = 1;     ReferenceTest rt = new ReferenceTest();     System.out.println("Before modify() a = " + a);     rt.modify(a);     System.out.println("After modify() a = " + a) ;   }   void modify(int number) {     number = number + 1;     System.out.println("number = " + number);   } } 

In this simple program, the variable a is passed to a method called modify(), which increments the variable by 1. The resulting output looks like this:

 Before modify() a = 1 number = 2 After modify() a = 1 

Notice that a did not change after it was passed to the method. Remember, it was a copy of a that was passed to the method. When a primitive variable is passed to a method, it is passed by value, which means pass-by-copy-of-the-bits-in-the-variable.

image from book
From the Classroom

The Shadowy World of Variables

Just when you think you've got it all figured out, you see a piece of code with variables not behaving the way you think they should. You might have stumbled into code with a shadowed variable. You can shadow a variable in several ways. We'll look at the one most likely to trip you up: hiding an instance variable by shadowing it with a local variable. Shadowing involves redeclaring a variable that's already been declared somewhere else.

The effect of shadowing is to hide the previously declared variable in such a way that it may look as though you're using the hidden variable, but you're actually using the shadowing variable. You might find reasons to shadow a variable intentionally, but typically it happens by accident and causes hard-to-find bugs. On the exam, you can expect to see questions where shadowing plays a role.

You can shadow an instance variable by declaring a local variable of the same name, either directly or as part of an argument:

 class Foo {   static int size * 7;   static void changeIt(int size) {     size = size + 200;     Systsm.out.println("size in changelt is " + size);   }   public static void main (String [] args) {     Foo f = new Foo() ;     System.out.println("size = "  + size);     changeIt(size);     System.out.println("size after changeIt is " + size);   } } 

The preceding code appears to change the size instance variable in the changeIt() method, but because changeIt() has a parameter named size, the local size variable is modified while the instance variable size is untouched. Running class Foo prints

 %java Foo size = 7 size in changeIt is 207 size after changelt is 7 

Things become more interesting when the shadowed variable is an object reference, rather than a primitive:

 class Bar {   int barNum = 28; } class Foo {   Bar myBar =new Bar();   void changelt(Bar myBar) {     myBar.barNum = 99;     System.out.println("myBar.barNum in changeIt is " + myBar.barNum);     myBar = new Bar() ;     myBar.barNum = 420;     System.out.println("myBar.barNum in changelt is now " + myBar.barNum);   }   public. static void main (String [] args) {     Foo f = new Foo() ;     System. out .println.("f.myBar.barNum is " + f.myBar.barNum);     f.changeIt {f.myBar);     System.out.println("f .myBar.barNum after changeIt is "                        + f.myBar.barNum) ;   } } 

The preceding code prints out this:

 f.myBar.barNum is 28 myBar.barNum in changelt is 99 myBar.barNum in changeIt is now 420 f, myBar. barNum after changeIt is 99 

You can see that the shadowing variable (the local parameter myBar in changeIt().) can still affect the myBar instance variable, because the myBar parameter receives a reference to the same Bar object. But when the local myBar is reassigned a new Bar object, which we then modify by changing its barNum value, Foo's original myBar instance variable is untouched.

image from book



SCJP Sun Certified Programmer for Java 5 Study Guide Exam 310-055
SCJP Sun Certified Programmer for Java 5 Study Guide (Exam 310-055) (Certification Press)
ISBN: 0072253606
EAN: 2147483647
Year: 2006
Pages: 131

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net