Chapter 10
Putting Variables and Methods Where They Belong
In This Chapter
Making something belong to an entire class
Putting variables inside and outside methods
Improving your batting average
Hello, again. You’re listening to radio station WWW, and I’m your host, Sam Burd. It’s the start again of the big baseball season, and today station WWW brought you live coverage of the Hankees versus Socks game. At this moment, I’m awaiting news of the game’s final score.
If you remember from earlier this afternoon, the Socks looked like they were going to take those Hankees to the cleaners. Then, the Hankees were belting ball after ball, giving the Socks a run for their money. Those Socks! I’m glad I wasn’t in their shoes.
Anyway, as the game went on, the Socks pulled themselves up. Now the Socks are nose to nose with the Hankees. We’ll get the final score in a minute, but first, a few reminders. Stay tuned after this broadcast for the big Jersey’s game. And don’t forget to tune in next week when the Cleveland Gowns play the Bermuda Shorts.
Okay, here’s the final score. Which team has the upper hand? Which team will come out a head? And the winner is . . . oh, no! It’s a tie!
Defining a Class (What It Means to Be a Baseball Player)
As far as I’m concerned, a baseball player has a name and a batting average. Listing 10-1 puts my feeling about this into Java program form.
Listing 10-1: The Player Class
import java.text.DecimalFormat;
public class Player {
private String name;
private double average;
public Player(String name, double average) {
this.name=name;
this.average=average;
}
public String getName() {
return name;
}
public double getAverage() {
return average;
}
public String getAverageString() {
DecimalFormat decFormat = new DecimalFormat();
decFormat.setMaximumIntegerDigits(0);
decFormat.setMaximumFractionDigits(3);
decFormat.setMinimumFractionDigits(3);
return decFormat.format(average);
}
}
So here I go, picking apart the code in Listing 10-1. Luckily, earlier chapters cover lots of stuff in this code. The code defines what it means to be an instance of the Player
class. Here’s what’s in the code:
Declarations of the fields name
and average
. For bedtime reading about field declarations, see Chapter 7.
A constructor to make new instances of the Player
class. For the lowdown on constructors, see Chapter 9.
Getter methods for the fields name
and average
. For chitchat about accessor methods (that is, setter and getter methods), see Chapter 7.
A method that returns the player’s batting average in String
form. For the good word about methods, see Chapter 7. (I put a lot of good stuff in Chapter 7, didn’t I?)
Another way to beautify your numbers
The getAverageString
method in Listing 10-1 takes the value from the average
field (a player’s batting average), converts that value (normally of type double
) into a String
, and then sends that String
value right back to the method caller. The use of DecimalFormat
, which comes right from the Java API (Application Programming Interface), makes sure that the String
value looks like a baseball player’s batting average. According to the decFormat.setMaximum...
and decFormat.setMinimum...
method calls, the String
value has no digits to the left of the decimal point, and has exactly three digits to the right of the decimal point.
Java’s DecimalFormat
class can be quite handy. For example, to display the values 345
and –345
with an accounting-friendly format, you can use the following code:
DecimalFormat decFormat = new DecimalFormat();
decFormat.setMinimumFractionDigits(2);
decFormat.setNegativePrefix(“(“);
decFormat.setNegativeSuffix(“)”);
System.out.println(decFormat.format(345));
System.out.println(decFormat.format(-345));
In this little example’s format string, everything before the semicolon dictates the way positive numbers are displayed, and everything after the semicolon determines the way negative numbers are displayed. So with this format, the numbers 345 and –345 appear as follows:
345.00
(345.00)
To discover some other tricks with numbers, visit the DecimalFormat
page of Java’s API documentation.
Using the Player class
Listings 10-2 and 10-3 have code that uses the Player
class — the class that’s defined back in Listing 10-1.
Listing 10-2: Using the Player Class
import java.util.Scanner;
import java.io.File;
import java.io.IOException;
import javax.swing.JFrame;
import javax.swing.JLabel;
import java.awt.GridLayout;
@SuppressWarnings(“serial”)
public class TeamFrame extends JFrame {
public TeamFrame() throws IOException {
Player player;
Scanner keyboard =
new Scanner(new File(“Hankees.txt”));
for (int num = 1; num <= 9; num++) {
player = new Player(keyboard.nextLine(),
keyboard.nextDouble());
keyboard.nextLine();
addPlayerInfo(player);
}
setTitle(“The Hankees”);
setLayout(new GridLayout(9, 2, 20, 3));
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setVisible(true);
}
void addPlayerInfo(Player player) {
add(new JLabel(“ “ + player.getName()));
add(new JLabel(player.getAverageString()));
}
}
Listing 10-3: Displaying a Frame
import java.io.IOException;
class ShowTeamFrame {
public static void main(String args[])
throws IOException {
new TeamFrame();
}
}
For a run of the code in Listings 10-1, 10-2, and 10-3, see Figure 10-1.
To run this program yourself, you need the Hankees.txt
file. This file contains data on your favorite baseball players. (See Figure 10-2.)
You must have the Hankees.txt
file in a certain place on your hard drive. If you’re using Eclipse, that “certain place” is a project directory within your Eclipse workspace. On the other hand, if you’re running Java from the command line, that “place” may be the directory that contains the Listing 10-3 code. One way or another, you can’t get away without having the Hankees.txt
file in the right place on your hard drive. If you don’t have Hankees.txt
in the right place, then when you try to run this section’s example, you’ll get an unpleasant FileNotFoundException
message.
Nine, count ’em, nine
The code in Listing 10-2 calls the Player
constructor nine times. This means that the code creates nine instances of the Player
class. The first time through the loop, the code creates an instance with the name Barry Burd
. The second time through the loop, the code abandons the Barry Burd
instance and creates another instance with name Harriet Ritter
. The third time through, the code abandons poor Harriet Ritter
and creates an instance for Weelie J. Katz
. The code has only one instance at a time but, all in all, the code creates nine instances.
Each Player
instance has its own name
and average
fields. Each instance also has its own Player
constructor and its own getName
, getAverage
, and getAverageString
methods. Look at Figure 10-3 and think of the Player
class with its nine incarnations.
Don’t get all GUI on me
The code in Listing 10-2 uses several names from the Java API. Some of these names are explained in Chapter 9. Others are explained right here:
JLabel
: A JLabel
is an object with some text in it. One of the ways to display text inside the frame is to add an instance of the JLabel
class to the frame.
In Listing 10-2, the addPlayerInfo
method is called nine times, once for each player on the team. Each time addPlayerInfo
is called, the method adds two new JLabel
objects to the frame. The text for each JLabel
object comes from a player object’s getter method.
GridLayout
: A GridLayout
arranges things in evenly spaced rows and columns. This constructor for the GridLayout
class takes two parameters — the number of rows and the number of columns.
In Listing 10-2, the call to the GridLayout
constructor takes parameters (9, 2, 20, 3)
. So in Figure 10-1, the display has nine rows (one for each player) and two columns (one for a name and another for an average). The horizontal gap between the two columns is 20 pixels wide, and the vertical gap between any two rows is 3 pixels tall.
pack
: When you pack a frame, you set the frame’s size. That’s the size the frame has when it appears on your computer screen. Packing a frame shrink-wraps the frame around whatever objects you’ve added inside the frame.
In Listing 10-2, by the time you’ve reached the call to pack
, you’ve already called addPlayerInfo
nine times and added 18 labels to the frame. In executing the pack
method, the computer picks a nice size for each label, given whatever text you’ve put inside the label. Then, the computer picks a nice size for the whole frame, given that the frame has these 18 labels inside it.
When you plop stuff onto frames, you have quite a bit of leeway with the order in which you do things. For instance, you can set the layout before or after you’ve added labels and other stuff to the frame. If you call setLayout
and then add labels, the labels appear in nice, orderly positions on the frame. If you reverse this order (add labels and then call setLayout
), the calling of setLayout
rearranges the labels in a nice, orderly fashion. It works fine either way.
In setting up a frame, the one thing that you shouldn’t do is violate the following sequence:
Add things to the frame, then
pack();
setVisible(true);
If you call pack
and then add more things to the frame, the pack
method doesn’t take the more recent things that you’ve added into consideration. If you call setVisible
before you add things or call pack
, the user sees the frame as it’s being constructed. Finally, if you forget to set the frame’s size (by calling pack
or some other sizing method), the frame that you see looks like the one in Figure 10-4. (Normally, I wouldn’t show you an anomalous run like the one in Figure 10-4, but I’ve made the mistake so many times that I feel as if this puny frame is an old friend of mine.)
Tossing an exception from method to method
Chapter 8 introduces input from a disk file, and along with that topic comes the notion of an exception. When you tinker with a disk file, you need to acknowledge the possibility of raising an IOException
. That’s the lesson from Chapter 8, and that’s why the constructor in Listing 10-2 has a throws IOException
clause.
But what about the main
method in Listing 10-3? With no apparent reference to disk files in this main
method, why does the method need its own throws IOException
clause? Well, an exception is a hot potato. If you have one, you either have to eat it (as you can see in Chapter 12) or use a throws
clause to toss it to someone else. If you toss an exception with a throws
clause, someone else is stuck with the exception just the way you were.
The constructor in Listing 10-2 throws an IOException
, but to whom is this exception thrown? Who in this chain of code becomes the bearer of responsibility for the problematic IOException
? Well, who called the constructor in Listing 10-2? It was the main
method in Listing 10-3 — that’s who called the TeamFrame
constructor. Because the TeamFrame
constructor throws its hot potato to the main
method in Listing 10-3, the main
method has to deal with it. As shown in Listing 10-3, the main
method deals with it by tossing the IOException
again (by having a throws IOException
clause of its own). That’s how the throws
clause works in Java programs.
Making Static (Finding the Team Average)
Thinking about the code in Listings 10-1 through 10-3, you decide that you’d like to find the team’s overall batting average. Not a bad idea! The Hankees in Figure 10-1 have an average of about .106, so the team needs some intensive training. While the players are out practicing on the ball field, you have a philosophical hurdle to overcome.
In Listings 10-1 through 10-3, you have three classes: a Player
class and two other classes that help display data from the Player
class. So in this class morass, where do the variables storing your overall, team-average tally go?
It makes no sense to put tally variables in either of the displaying classes (TeamFrame
and ShowTeamFrame
). After all, the tally has something or other to do with players, teams, and baseball. The displaying classes are about creating windows, not about playing baseball.
You’re uncomfortable putting an overall team average in an instance of the Player
class because an instance of the Player
class represents just one player on the team. What business does a single player have storing overall team data? Sure, you could make the code work, but it wouldn’t be an elegant solution to the problem.
Finally, you discover the keyword static
. Anything that’s declared to be static belongs to the whole class, not to any particular instance of the class. When you create the static
field, totalOfAverages
, you create just one copy of the field. This copy stays with the entire Players
class. No matter how many instances of the Player
class you create — one, nine, or none — you have just one totalOfAverages
field. And, while you’re at it, you create other static
fields (playerCount
and decFormat
) and static
methods (findTeamAverage
and findTeamAverageString
). To see what I mean, look at Figure 10-5.
Going along with your passion for subclasses, you put code for team-wide tallies in a subclass of the Player
class. The code is shown in Listing 10-4.
Listing 10-4: Creating a Team Batting Average
import java.text.DecimalFormat;
public class PlayerPlus extends Player {
private
static
int playerCount = 0;
private
static
double totalOfAverages = .000;
private
static
DecimalFormat decFormat =
new DecimalFormat();
static
{
decFormat.setMaximumIntegerDigits(0);
decFormat.setMaximumFractionDigits(3);
decFormat.setMinimumFractionDigits(3);
}
public PlayerPlus(String name, double average) {
super(name, average);
playerCount++;
totalOfAverages += average;
}
public
static
double findTeamAverage() {
return totalOfAverages / playerCount;
}
public
static
String findTeamAverageString() {
return decFormat.format
(totalOfAverages / playerCount);
}
}
Why is there so much static?
Maybe you’ve noticed — the code in Listing 10-4 is overflowing with the word static. That’s because nearly everything in this code belongs to the entire PlayerPlus
class and not to individual instances of the class. That’s good because something like playerCount
(the number of players on the team) shouldn’t belong to individual players, and having each PlayerPlus
object keep track of its own count would be silly. (“I know how many players I am. I’m just one player!”) If you had nine individual playerCount
fields, either each field would store the number 1 (which is useless) or you would have nine different copies of the count, which is wasteful and prone to error. So by making playerCount
static, you’re keeping the playerCount
in just one place, where it belongs.
The same kind of reasoning holds for the totalOfAverages
. Eventually, the totalOfAverages
field will store the sum of the players’ batting averages. For all nine members of the Hankees, this adds up to .956. It’s not until someone calls the findTeamAverage
or findTeamAverageString
method that the computer actually finds the overall Hankee team batting average.
You also want the methods findTeamAverage
and findTeamAverageString
to be static. Without the word static
, there would be nine findTeamAverage
methods — one for each instance of the PlayerPlus
class. This wouldn’t make much sense. Each instance would have the code to calculate totalOfAverages/playerCount
on its own, and each of the nine calculations would yield the very same answer.
Meet the static initializer
In Listing 10-4, the decFormat
field is static. This makes sense, because decFormat
makes totalOfAverages / playerCount
look nice, and both fields in the expression totalOfAverages / playerCount
are static. Thinking more directly, the code needs only one thing for formatting numbers. If you have several numbers to format, the same decFormat
thing that belongs to the entire class can format each number. Creating a decFormat
for each player is not only inelegant, but also wasteful.
But declaring decFormat
to be static presents a little problem. To set up the formatting, you have to call methods like decFormat.setMaximumIntegerDigits(0)
. You can’t just plop these method calls anywhere in the PlayerPlus
class. For example, the following code is bad, invalid, illegal, and otherwise un-Java-like:
// THIS IS BAD CODE:
public class PlayerPlus extends Player {
private static DecimalFormat decFormat =
new DecimalFormat();
decFormat.setMaximumIntegerDigits(0);
// Bad!
decFormat.setMaximumFractionDigits(3);
// Bad!
decFormat.setMinimumFractionDigits(3);
// Bad!
Look at the examples from previous chapters. In those examples, I never let a method call just dangle on its own, the way I do in the bad, bad code. In this chapter, in Listing 10-1, I don’t call setMaximumIntegerDigits
without putting the method call inside the getAverageString
method’s body. This no-dangling-method-calls business isn’t an accident. Java’s rules restrict the places in the code where you can issue calls to methods, and putting a lonely method call on its own immediately inside a class definition is a big no-no.
So in Listing 10-4, where can you put the necessary setMax
and setMin
calls? You can put them inside the body of the findTeamAverageString
method, much the way I put them inside the getAverageString
method in Listing 10-1. But putting those method calls inside the findTeamAverageString
method’s body might defeat the purpose of having decFormat
be static. After all, a programmer might call findTeamAverageString
several times, calling decFormat.setMaximumIntegerDigits(0)
each time. But that would be very wasteful. The entire PlayerPlus
class has only one decFormat
field, and that decFormat
field’s MaximumIntegerDigits
value is always 0
. So don’t keep setting MaximumIntegerDigits(0)
over and over again.
The best alternative is to take the bad lines in this section’s bad code and put them inside a static initializer. Then they become good lines inside good code. (See Listing 10-4.) A static initializer is a block that’s preceded by the word static
. Java executes the static initializer’s statements once for the entire class. That’s exactly what you want for something called “static.”
Displaying the overall team average
You may be noticing a pattern. When you create code for a class, you generally write two pieces of code. One piece of code defines the class, and the other piece of code uses the class. (The ways to use a class include calling the class’s constructor, referencing the class’s non-private fields, calling the class’s methods, and so on.) Listing 10-4, shown previously, contains code that defines the PlayerPlus
class, and Listing 10-5 contains code that uses this PlayerPlus
class.
Listing 10-5: Using the Code from Listing 10-4
import java.util.Scanner;
import java.io.File;
import java.io.IOException;
import javax.swing.JFrame;
import javax.swing.JLabel;
import java.awt.GridLayout;
@SuppressWarnings(“serial”)
public class TeamFrame extends JFrame {
public TeamFrame() throws IOException {
PlayerPlus
player;
Scanner keyboard =
new Scanner(new File(“Hankees.txt”));
for (int num = 1; num <= 9; num++) {
player =
new
PlayerPlus
(keyboard.nextLine(),
keyboard.nextDouble());
keyboard.nextLine();
addPlayerInfo(player);
}
add(new JLabel());
add(new JLabel(“ ------”));
add(new JLabel(“Team Batting Average:”));
add(new JLabel(PlayerPlus.findTeamAverageString()));
setTitle(“The Hankees”);
setLayout(new GridLayout(
11
, 2, 20, 3));
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setVisible(true);
}
void addPlayerInfo(
PlayerPlus
player) {
add(new JLabel(“ “ + player.getName()));
add(new JLabel(player.getAverageString()));
}
}
To run the code in Listing 10-5, you need a class with a main
method. The ShowTeamFrame
class in Listing 10-3 works just fine.
Figure 10-6 shows a run of the code from Listing 10-5. This run depends on the availability of the Hankees.txt
file from Figure 10-2. The code in Listing 10-5 is almost an exact copy of the code from Listing 10-2. (So close is the copy that if I could afford it, I’d sue myself for theft of intellectual property.) The only thing new in Listing 10-5 is the stuff shown in bold.
In Listing 10-5, the GridLayout
has two extra rows: one row for spacing and another row for the Hankee team’s average. Each of these rows has two Label
objects in it.
The spacing row has a blank label and a label with a dashed line. The blank label is a placeholder. When you add components to a GridLayout
, the components are added row by row, starting at the left end of a row and working toward the right end. Without this blank label, the dashed line label would appear at the left end of the row, under Hugh R. DaReader’s name.
The other row has a label displaying the words Team Batting Average, and another label displaying the number .106. The method call that gets the number .106 is interesting. The call looks like this:
PlayerPlus.findTeamAverageString()
Take a look at that method call. That call has the following form:
ClassName
.methodName
()
That’s new and different. In earlier chapters, I say that you normally preface a method call with an object’s name, not a class’s name. So why do I use a class name here? The answer: When you call a static
method, you preface the method’s name with the name of the class that contains the method. The same holds true whenever you reference another class’s static
field. This makes sense. Remember, the whole class that defines a static
field or method owns that field or method. So, to refer to a static
field or method, you preface the field or method’s name with the class’s name.
Static is old hat
This section makes a big noise about static
fields and methods, but static
things have been part of the picture since early in this book. For example, Chapter 3 introduces System.out.println
. The name System refers to a class, and out is a static
field in that class. That’s why, in Chapter 4 and beyond, I use the static
keyword to import the out
field:
import
static
java.lang.System.out;
In Java, static
fields and methods show up all over the place. When they’re declared in someone else’s code, and you’re making use of them in your code, you hardly ever have to worry about them. But when you’re declaring your own fields and methods and must decide whether to make them static, you have to think a little harder.
Could cause static; handle with care
When I first started writing Java, I had recurring dreams about getting a certain error message. The message was non-static variable or method cannot be referenced from a static context
. So often did I see this message, so thoroughly was I perplexed, that the memory of this message became burned into my subconscious existence.
These days, I know why I got that error message so often. I can even make the message occur if I want. But I still feel a little shiver whenever I see this message on my screen.
Before you can understand why the message occurs and how to fix the problem, you need to get some terminology under your belt. If a field or method isn’t static, it’s called non-static. (Real surprising, hey?) Given that terminology, there are at least two ways to make the dreaded message appear:
Put Class.nonstaticThing
somewhere in your program.
Put nonstaticThing
somewhere inside a static
method.
In either case, you’re getting yourself into trouble. You’re taking something that belongs to an object (the non-static thing) and putting it in a place where no objects are in sight.
Take, for instance, the first of the two situations I just described. To see this calamity in action, go back to Listing 10-5. Toward the end of the listing, change player.getName()
to Player.getName()
. That does the trick. What could Player.getName
possibly mean? If anything, the expression Player.getName
means “call the getName
method that belongs to the entire Player
class.” But look back at Listing 10-1. The getName
method isn’t static. Each instance of the Player
(or PlayerPlus
) class has a getName
method. None of the getName
methods belong to the entire class. So the call Player.getName
doesn’t make any sense. (Maybe the computer is pulling punches when it displays the inoffensive cannot be referenced . . .
message. Perhaps a harsh nonsensical expression
message would be more fitting.)
For a taste of the second situation (in the bullet list that I give earlier in this section), go back to Listing 10-4. While no one’s looking, quietly remove the word static from the declaration of the decFormat
field (near the top of the listing). This removal turns decFormat
into a non-static field. Suddenly, each player on the team has a separate decFormat
field.
Well, things are just hunky-dory until the computer reaches the findTeamAverageString
method. That static
method has four decFormat.
SuchAndSuch
statements in it. Once again, you’re forced to ask what a statement of this kind could possibly mean. Method findTeamAverageString
belongs to no instance in particular. (The method is static
, so the entire PlayerPlus
class has one findTeamAverageString
method.) But with the way you’ve just butchered the code, plain old decFormat
without reference to a particular object has no meaning. So again, you’re referencing the non-static field, decFormat
, from inside a static
method’s context. For shame, for shame, for shame!
Experiments with Variables
One summer during my college days, I was sitting on the front porch, loafing around, talking with someone I’d just met. I think her name was Janine. “Where are you from?” I asked. “Mars,” she answered. She paused to see whether I’d ask a follow-up question.
As it turned out, Janine was from Mars, Pennsylvania, a small town about 20 miles north of Pittsburgh. Okay, so what’s my point? The point is that the meaning of a name depends on the context. If you’re just north of Pittsburgh and ask, “How do I get to Mars from here?” you may get a sensible, nonchalant answer. But if you ask the same question standing on a street corner in Manhattan, you’ll probably arouse some suspicion. (Okay, knowing Manhattan, people would probably just ignore you.)
Of course, the people who live in Mars, Pennsylvania, are very much aware that their town has an oddball name. Fond memories of teenage years at Mars High School don’t prevent a person from knowing about the big red planet. On a clear evening in August, you can still have the following conversation with one of the local residents:
You: How do I get to Mars?
Local resident: You’re in Mars, pal. What particular part of Mars are you looking for?
You: No, I don’t mean Mars, Pennsylvania. I mean the planet Mars.
Local resident: Oh, the planet! Well, then, catch the 8:19 train leaving for Cape Canaveral . . . No, wait, that’s the local train. That’d take you through West Virginia. . . .
So the meaning of a name depends on where you’re using the name. Although most English-speaking people think of Mars as a place with a carbon dioxide atmosphere, some folks in Pennsylvania think about all the shopping they can do in Mars. And those folks in Pennsylvania really have two meanings for the name Mars. In Java, those names may look like this: Mars
and planets.Mars
.
Putting a variable in its place
Your first experiment is shown in Listings 10-6 and 10-7. The listings’ code highlights the difference between variables that are declared inside and outside methods.
Listing 10-6: Two Meanings for Mars
import static java.lang.System.out;
class EnglishSpeakingWorld {
String mars = “ red planet”;
void visitPennsylvania() {
out.println(“visitPA is running:”);
String mars = “ Janine’s home town”;
out.println(mars);
out.println(this.mars);
}
}
Listing 10-7: Calling the Code of Listing 10-6
import static java.lang.System.out;
class GetGoing {
public static void main(String args[]) {
out.println(“main is running:”);
EnglishSpeakingWorld e =
new EnglishSpeakingWorld();
//out.println(mars); cannot resolve symbol
out.println(e.mars);
e.visitPennsylvania();
}
}
Figure 10-7 shows a run of the code in Listings 10-6 and 10-7. Figure 10-8 shows a diagram of the code’s structure. In the GetGoing
class, the main
method creates an instance of the EnglishSpeakingWorld
class. The variable e
refers to the new instance. The new instance is an object with a variable named mars inside it. That mars
variable has the value “red planet”
. The “red planet”
mars
variable is a field.
Now look at the main
method in Listing 10-7. Inside the GetGoing
class’s main
method, you aren’t permitted to write out.println(mars)
. In other words, a bare-faced reference to any mars
variable is a definite no-no. The mars
variable that I mention in the previous paragraph belongs to the EnglishSpeakingWorld
object, not the GetGoing
class.
However, inside the GetGoing
class’s main
method, you can certainly write e.mars
because the e
variable refers to your EnglishSpeakingWorld
object. That’s nice.
Near the bottom of the code, the visitPennsylvania
method is called. When you’re inside visitPennsylvania
, you have another declaration of a mars
variable, whose value is “Janine’s home town”
. This particular mars
variable is called a method-local variable because it belongs to just one method — the visitPennsylvania
method.
So now you have two variables, both with the name mars. One mars
variable, a field, has the value “red planet”
. The other mars
variable, a method-local variable, has the value “Janine’s home town”
. In the code, when you use the word mars, to which of the two variables are you referring?
The answer is, when you’re visiting Pennsylvania, the variable with value “Janine’s home town”
wins. When in Pennsylvania, think the way the Pennsylvanians think. When you’re executing code inside the visitPennsylvania
method, resolve any variable name conflicts by going with method-local variables — variables declared right inside the visitPennsylvania
method.
So what if you’re in Pennsylvania and need to refer to that two-mooned celestial object? More precisely, how does code inside the visitPennsylvania
method refer to the field with value “red planet”
? The answer is, use this.mars
. The word this points to whatever object contains all this code (and not to any methods inside the code). That object, an instance of the EnglishSpeakingWorld
class, has a big, fat mars
field, and that field’s value is “red planet”
. So that’s how you can force code to see outside the method it’s in — you use the Java keyword this
.
Telling a variable where to go
Years ago, when I lived in Milwaukee, Wisconsin, I made frequent use of the local bank’s automatic teller machines. Machines of this kind were just beginning to become standardized. The local teller machine system was named TYME, which stood for Take Your Money Everywhere.
I remember traveling by car out to California. At one point, I got hungry and stopped for a meal, but I was out of cash. So I asked a gas station attendant, “Do you know where there’s a TYME machine around here?”
So you see, a name that works well in one place could work terribly, or not at all, in another place. In Listings 10-8 and 10-9, I illustrate this point (with more than just an anecdote about teller machines).
Listing 10-8: Tale of Atomic City
import static java.lang.System.out;
class EnglishSpeakingWorld2 {
String mars;
void visitIdaho() {
out.println(“visitID is running:”);
mars = “ red planet”;
String atomicCity = “ Population: 25”;
out.println(mars);
out.println(atomicCity);
}
void visitNewJersey() {
out.println(“visitNJ is running:”);
out.println(mars);
//out.println(atomicCity);
// cannot resolve symbol
}
}
Listing 10-9: Calling the Code of Listing 10-8
class GetGoing2 {
public static void main(String args[]) {
EnglishSpeakingWorld2 e =
new EnglishSpeakingWorld2();
e.visitIdaho();
e.visitNewJersey();
}
}
Figure 10-9 shows a run of the code in Listings 10-8 and 10-9. Figure 10-10 shows a diagram of the code’s structure. The code for EnglishSpeakingWorld2
has two variables. The mars
variable, which isn’t declared inside a method, is a field. The other variable, atomicCity
, is a method-local variable and is declared inside the visitIdaho
method.
In Listing 10-8, notice where each variable can and can’t be used. When you try to use the atomicCity
variable inside the visitNewJersey
method, you get an error message. Literally, the message says cannot resolve symbol
. Figuratively, the message says, “Hey, buddy, Atomic City is in Idaho, not New Jersey.” Technically, the message says that the method-local variable atomicCity
is available only in the visitIdaho
method because that’s where the variable was declared.
So back inside the visitIdaho
method, you’re free to use the atomicCity
variable as much as you want. After all, the atomicCity
variable is declared inside the visitIdaho
method.
And what about Mars? Have you forgotten about your old friend, that lovely eighty-degrees-below-zero planet? Well, both the visitIdaho
and visitNewJersey
methods can access the mars
variable. That’s because the mars
variable is a field. That is, the mars
variable is declared in the code for the EnglishSpeakingWorld2
class but not inside any particular method. (In my stories about the names for things, remember that people who live in both states, Idaho and New Jersey, have heard of the planet Mars.)
The lifecycle of the mars
field has three separate steps:
When the EnglishSpeakingWorld2
class first flashes into existence, the computer sees String mars
and creates space for the mars
field.
When the visitIdaho
method is executed, the method assigns the value “red planet”
to the mars
field. (The visitIdaho
method also prints the value of the mars
field.)
When the visitNewJersey
method is executed, the method prints the mars
value once again.
In this way, the mars
field’s value is passed from one method to another.
Passing Parameters
A method can communicate with another part of your Java program in several ways. One of the ways is through the method’s parameter list. Using a parameter list, you pass on-the-fly information to a method as the method is being called.
So imagine that the information you pass to the method is stored in one of your program’s variables. What, if anything, does the method actually do with that variable? The following sections present a few interesting case studies.
Pass by value
According to my web research, the town of Smackover, Arkansas, has 2,232 people in it. But my research isn’t current. Just yesterday, Dora Kermongoos celebrated a joyous occasion over at Smackover General Hospital — the birth of her healthy, blue-eyed baby girl. (The girl weighs 7 pounds, 4 ounces, and is 21 inches tall.) Now the town’s population has risen to 2,233.
Listing 10-10 has a very bad program in it. The program is supposed to add 1 to a variable that stores Smackover’s population, but the program doesn’t work. Take a look at Listing 10-10 and see why.
Listing 10-10: This Program Doesn’t Work
class TrackPopulation {
public static void main(String args[]) {
int smackoverARpop = 2232;
birth(smackoverARpop)
;
System.out.println(smackoverARpop);
}
static void birth(int cityPop)
{
cityPop++;
}
}
When you run the program in Listing 10-10, the program displays the number 2,232 onscreen. After nine months of planning and anticipation and Dora’s whopping seven hours in labor, the Kermongoos family’s baby girl wasn’t registered in the system. What a shame!
The improper use of parameter passing caused the problem. In Java, when you pass a parameter that has one of the eight primitive types to a method, that parameter is passed by value.
Here’s what this means in plain English: Any changes that the method makes to the value of its parameter don’t affect the values of variables back in the calling code. In Listing 10-10, the birth
method can apply the ++
operator to cityPop
all it wants — the application of ++
to the cityPop
parameter has absolutely no effect on the value of the smackoverARpop
variable back in the main
method.
Technically, what’s happening is the copying of a value. (See Figure 10-11.) When the main
method calls the birth
method, the value stored in smackoverARpop
is copied to another memory location — a location reserved for the cityPop
parameter’s value. During the birth
method’s execution, 1 is added to the cityPop
parameter. But the place where the original 2232
value was stored — the memory location for the smackoverARpop
variable — remains unaffected.
Returning a result
You must fix the problem that the code in Listing 10-10 poses. After all, a young baby Kermongoos can’t go through life untracked. To record this baby’s existence, you have to add 1 to the value of the smackoverARpop
variable. You can do this in plenty of ways, and the way presented in Listing 10-11 isn’t the simplest. Even so, the way shown in Listing 10-11 illustrates a point: Returning a value from a method call can be an acceptable alternative to parameter passing. Look at Listing 10-11 to see what I mean.
Listing 10-11: This Program Works
class TrackPopulation2 {
public static void main(String args[]) {
int smackoverARpop = 2232;
smackoverARpop = birth(smackoverARpop)
;
System.out.println(smackoverARpop);
}
static int birth(int cityPop)
{
return cityPop + 1
;
}
}
After running the code in Listing 10-11, the number you see on your computer screen is the correct number, 2,233.
The code in Listing 10-11 has no new features in it (unless you call working correctly a new feature). The most important idea in Listing 10-11 is the return
statement, which also appears in Chapter 7. Even so, Listing 10-11 presents a nice contrast to the approach in Listing 10-10, which had to be discarded.
Pass by reference
In the previous section or two, I take great pains to emphasize a certain point — that when a parameter has one of the eight primitive types, the parameter is passed by value. If you read this, you probably missed the emphasis on the parameter’s having one of the eight primitive types. The emphasis is needed because passing objects (reference types) doesn’t quite work the same way.
When you pass an object to a method, the object is passed by reference. What this means to you is that statements in the called method can change any values that are stored in the object’s variables. Those changes do affect the values that are seen by whatever code called the method. Listings 10-12 and 10-13 illustrate the point.
Listing 10-12: What Is a City?
class City {
int population;
}
Listing 10-13: Passing an Object to a Method
class TrackPopulation3 {
public static void main(String args[]) {
City smackoverAR = new City();
smackoverAR.population = 2232;
birth(smackoverAR)
;
System.out.println(smackoverAR.population);
}
static void birth(City aCity)
{
aCity.population++
;
}
}
When you run the code in Listings 10-12 and 10-13, the output that you get is the number 2,233. That’s good because the code has things like ++
and the word birth in it. The deal is, adding 1 to aCity.population
inside the birth
method actually changes the value of smackoverAR.population
as it’s known in the main
method.
To see how the birth
method changes the value of smackoverAR.population
, look at Figure 10-12. When you pass an object to a method, the computer doesn’t make a copy of the entire object. Instead, the computer makes a copy of a reference to that object. (Think of it the way it’s shown in Figure 10-12. The computer makes a copy of an arrow that points to the object.)
In Figure 10-12, you see just one instance of the City
class, with a population variable inside it. Now keep your eye on that object as you read the following steps:
1. Just before the birth
method is called, the smackoverAR
variable refers to that object — the instance of the City
class.
2. When the birth
method is called and smackoverAR
is passed to the birth method’s aCity
parameter, the computer copies the reference from smackoverAR
to aCity
. Now aCity
refers to that same object — the instance of the City
class.
3. When the statement aCity.population++
is executed inside the birth
method, the computer adds 1 to the object’s population
field. Now the program’s one and only City
instance has 2233
stored in its population
field.
4. The flow of execution goes back to the main
method. The value of smackoverAR.population
is printed. But smackoverAR
refers to that one instance of the City
class. So smackoverAR.population
has the value 2233
. The Kermongoos family is so proud.
Returning an object from a method
Believe it or not, the previous sections on parameter passing left one nook and cranny of Java methods unexplored. When you call a method, the method can return something right back to the calling code. In previous chapters and sections, I return primitive values, such as int
values, or nothing (otherwise known as void). In this section, I return a whole object. It’s an object of type City
from Listing 10-12. The code that makes this happen is in Listing 10-14.
Listing 10-14: Here, Have a City
class TrackPopulation4 {
public static void main(String args[]) {
City smackoverAR = new City();
smackoverAR.population = 2232;
smackoverAR = doBirth(
smackoverAR)
;
System.out.println(smackoverAR.population);
}
static City doBirth(
City aCity)
{
City myCity = new City();
myCity.population = aCity.population + 1;
return myCity
;
}
}
If you run the code in Listing 10-14, you get the number 2,233. That’s good. The code works by telling the doBirth
method to create another City
instance. In the new instance, the value of population
is 2333
. (See Figure 10-13.)
After the doBirth
method is executed, that City
instance is returned to the main
method. Then, back in the main
method, that instance (the one that doBirth
returns) is assigned to the smackoverAR
variable. (See Figure 10-14.) Now smackoverAR
refers to a brand-new City
instance — an instance whose population is 2,233.
In Listing 10-14, notice the type consistency in the calling and returning of the doBirth
method:
The smackoverAR
variable has type City
. The smackoverAR
variable is passed to the aCity
parameter, which is also of type City
.
The myCity
variable is of type City
. The myCity
variable is sent back in the doBirth
method’s return statement. That’s consistent, because the doBirth
method’s header begins with the promise static City doBirth(
blah, blah, blah...
— the promise to return an object of type City
.
The doBirth
method returns an object of type City
. Back in the main
method, the object that the call to doBirth
returns is assigned to the smackoverAR
variable, and (you guessed it) the smackoverAR
variable is of type City
.
Aside from being very harmonious, all this type agreement is absolutely necessary. If you write a program in which your types don’t agree with one another, the compiler spits out an unsympathetic incompatible types
message.
Epilogue
Dora Kermongoos and her newborn baby daughter are safe, healthy, and resting happily in their Smackover, Arkansas, home.