• No results found

When an argument is passed in to a parameter, the argument’s data is copied into the parameter. The process of copying data between method calls is referred to in programming as call-by-value.

In Java, you do not specify that an argument is to be passed using call- by-value. It happens automatically, and is, in fact, your only option. Other programming languages use call-by-reference and/or call-by-pointer, in which an argument is not copied into a parameter. You cannot do call-by- reference or call-by-pointer in Java. No matter what type of argument you pass in to a method, the corresponding parameter will get a copy of that data, which is exactly how call-by-value works.

For example, to invoke the setVolume() method of the Radio class, you must pass in an int argument:

int x = 7;

someRadio.setVolume(x);

In the previous statements, the integer x is passed in to setVolume(). The contents of x are copied into the int parameter of setVolume(), which is the variable volume. There are now two 7s in memory. The value of volume is 7, and the value of x is still 7.

The following ListenToRadio program demonstrates passing arguments to parameters. Try to determine the flow of control of the program and also what the output will be.

public class ListenToRadio {

public static void main(String [] args) {

System.out.println(“Creating a radio...”); Radio radio = new Radio();

System.out.println(“...and turning it on...”); float initialStation = Float.parseFloat(args[0]); int initialVolume = 5;

radio.turnOn(initialVolume, initialStation, ‘F’);

System.out.println(“The tuning is “ + radio.getTuning()); int x = 7; radio.setVolume(x); System.out.println(“x = “ + x); radio.turnUp(); radio.turnUp(); radio.changeBand(); 116 Chapter 5

System.out.println(“The volume is now “ + radio.volume + “, the band is “ + radio.band

+ “, and the tuning is “ + radio.tuning); }

}

Let’s follow the flow of control of the ListenToRadio program. The JVM invokes main() when you run the program, so main() is at the bottom of the call stack. The first line of code within main() is a call to the println() method, which displays the following:

Creating a radio...

The next statement in main() instantiates a new Radio object, followed by another call to println():

...and turning it on...

I want to emphasize that before a Radio object is instantiated, none of the methods in the Radio class can be invoked. Until there is a Radio object in memory, there is no setVolume() method or turnOnRadio() method (and so on) to invoke. You cannot turn on a radio until a radio exists, and no radio exists until you instantiate one using the new keyword.

The first command-line argument of the ListenToRadio program is the ini- tial station to be tuned in. The command-line argument is parsed into a float and stored in the initialStation variable. An int is declared (initialVolume) and is set equal to 5.

The turnOn() method is then invoked, causing it to be pushed onto the top of the call stack. To invoke turnOn(), you must pass in an int, float, and char, in that order. The int passed in is initialVolume, which is 5. The value 5 is copied into the corresponding parameter of turnOn(), which is v. Similarly, the con- tents of initialStation are copied in to the parameter t. Finally, the character F is copied in to the parameter b.

The flow of control is now within turnOn(), and the first statement within turnOn() is a call to println(), displaying the following:

Turning on the radio

The setVolume() method is then invoked, pushing setVolume() onto the top of the call stack. You must pass an int into setVolume(), and v is passed as an argument. The contents of v are copied in to the parameter volume, which in this case is 5. There are now three variables in memory equal to 5: initialVolume in main(), v in turnOn(), and volume in setVolume().

Within setVolume(), the println() method is invoked and pushed onto the call stack. We now have the situation illustrated by the call stack discussed in the earlier section Method Call Stack. The following is displayed:

Setting the volume to 5

The parameter volume is assigned to the field volume, so now the 5 is in memory in four different places. The parameter volume is assigned to –5, a statement I added to emphasize that this is call-by-value. What does changing volume to –5 do to the argument v that was passed in to volume? It does nothing to v because volume is a copy of v and not the actual variable v.

Within the setVolume() method, the parameter name is volume, which also happens to be the name of one of the fields of the Radio class. This might seem like a naming conflict, but it is okay and is done regularly in Java. To distinguish between the local variable volume and the field volume, you must use the thisreference whenever referring to the field. Therefore, using just volume refers to the parameter and using

this.volume refers to the field.

At one point in the ListenToRadio program, the initial volume of 5 was in four different variables in memory: initialVolume, v, volume, and this.volume. Keep in mind that the variables volume, v, and initialVolume are local vari- ables (sometimes aptly referred to as temporary variables). They are allocated in memory on the call stack, and when the method is done executing, these variables go away.

For example, when setVolume() is done, the parameter volume goes out of scope. When turnOn() is done, the parameter v goes out of scope. When main() is done, initialVolume goes out of scope. But the field volume in the Radio object stays in memory until the object is garbage collected, which can be long after these temporary variables have gone away.

After setVolume() finishes, control returns to the turnOn() method, which invokes setBand(). The setBand() method has a char parameter, and the b in turnOn() is passed as an argument in to the b parameter of setBand(). There are now two variables in memory named b, both of value F, but their scopes are different. The b in turnOn() is not accessible from within setBand(). Nor is the b in setBand() accessible to the turnOn() method. This is why the value F was passed into setBand() because the F was needed within the scope of setBand().

The setBand() method prints out the following:

Setting the band to F

The field band is assigned to F, and the setBand() method returns. We are back in the turnOn() method, which sets the field tuning equal to the parame- ter t. The turnOn() method is now complete, so flow of control jumps back down to main().

The next line of code in main() is a println() statement, but before println() is invoked, the getTuning() method of radio is called. Notice that no arguments are passed in because getTuning() has no parameters. Control jumps to get- Tuning(), which prints out the following:

Inside getTuning

The getTuning() method declares that it returns a float, so it must do so somewhere in the method. Notice that it returns the tuning field, which is a float containing the current radio station. The value of tuning is returned-by- value, meaning that a copy of tuning is sent back to main(), similarly to how arguments are copied into parameters.

The copy of tuning is sent back as a float and then concatenated to “The tun- ing is “, displaying something like the following:

The tuning is 100.3

Control is back within main(), and the volume is set to 7. Recall that the setVolume() sets the volume parameter to –5 to demonstrate that the argument has not changed. The output of displaying the contents of x looks like the following:

x = 7

The volume is then turned up by invoking turnUp() twice. The band is changed by invoking changeBand(), and the final output looks similar to the following:

The volume is now 9, the band is A, and the tuning is 100.3

Figure 5.2 shows a sample output of the ListenToRadio program.

Figure 5.2 Sample output of the ListenToRadio program when the command-line argument is 100.3.

Classroom Q & A

Q: What if I want the method to be able to change the contents of the argument?

A: This cannot be done in Java. It’s as simple as that. When using call- by-value, changing the parameter in the method does not change the argument. Look closely at the setVolume() method of the Radio class discussed earlier. The last line of code in that method sets the parameter to –5. This does not change the argument to –5. We simply can’t change the argument, even if we want to.

Q: I suppose I can live with parameters not being able to change arguments, but what if I have a large amount of data that needs to be passed in to a method. I’m talking very large. Typically, I would want to pass in a pointer so as to not waste the time and memory of copying large data. Can I avoid passing large objects in Java?

A: Not only can you avoid passing large objects, you can’t do it even if you want to. I need to reiterate a very important aspect of Java: A variable in Java is either one of the eight primitive data types or it is a reference. If the argument is a primitive data type, it is at most 64 bits in size (a double or a long). If the data I want to pass to a method is a very large object, it is not the object that is passed. It is a reference to the object, which is no larger than 64 bits, and in most cases is 32 bits. It is the reference that is copied, not the large amount of data.

Q: So the largest amount of data that is copied with call-by-value in Java is only 64 bits?

A: Correct! And copying 64 bits in today’s computing world is rarely a concern in terms of performance or overhead.

Q: What if I really want the method to change the argument passed in?

A: Well, you just can’t do it. But notice what you can do with the parameter if it is a reference to an object. The method can use this reference to do anything it wants to the object (depending on the access specifiers of the object’s fields and methods). The method can change the data of the object being pointed to and invoke methods on the object. The only restriction arising from call-by- value is that the method cannot change where the reference is pointing to.