Who's Afraid of Java?

Who's Afraid of Java? - the WWW version

ISBN: 0-12-339101-6

Copyright 1997, 1996 by AP Professional

Copyright 2000, 1997, 1996 by Chrysalis Software Corporation


Return to the table of contents


Return to my main page


Basics of Programming

Creative Programming?

After that necessary detour into the workings of the hardware, we can now resume our regularly scheduled explanation of the creative possibilities of computers. It may sound odd to describe computers as providing grand scope for creative activities: Aren't they monotonous, dull, unintelligent, and extremely limited? Yes, they are. However, they have two redeeming virtues that make them ideal as a canvas of invention: They are extraordinarily fast and amazingly reliable. These characteristics allow the creator of a program to weave intricate chains of thought and have a fantastic number of steps carried out without fail. We'll begin to explore how this is possible after we go over some definitions and objectives for this chapter.

Definitions

A keyword is a word defined in the Java language, such as if and while.

An identifier is a user-defined name; variable names are identifiers. Identifiers must not conflict with keywords such as if and while; for example, you cannot create a variable with the name while.

An operator is one of the facilities of Java that is built into the language rather than being added on later, and therefore can have a name that does not conform to the rules for identifiers. Examples are +, -, and =.

A statement is a complete operation understood by the Java compiler. Each statement is ended with a semicolon (;).

A token is a part of a program that the compiler treats as a separate unit. It's analogous to a word in English; a statement is more like a sentence. For example, String is a token, as is (. On the other hand, x = 5; is a statement.

An expression is one of the units of which a statement is made. It is made up of one or more tokens. In the statement i = k + 3;, "k + 3" is an expression composed of the three tokens k, +, and 3.

An if statement is a statement that causes its controlled statement to be executed if the condition specified in the if statement is true.

A while statement is a statement that causes its controlled statement to be executed while a specified condition is true.

A block is a group of statements that are considered one logical statement. A block is delimited by the "curly braces", { and }; the first of these symbols starts a block, and the second one ends the block. A block can be used anywhere that a statement can be used, and is treated in exactly the same way as if it were one statement.

Objectives for This Chapter

By the end of this chapter, you should:

  1. Understand what a program is and have some idea how one works.
  2. Understand how to get information into and out of a Java program.
  3. Understand how to use if and while to control the execution of a Java program.1
  4. Understand how to tell the compiler to treat a section of a Java program as one unit.
  5. Be able to understand a simple program I've written in Java.
  6. Be able to write a simple program in Java.

Speed Demon

The most impressive attribute of modern computers is, of course, their speed; as we have already seen, this is measured in MIPS (millions of instructions per second).

Of course, raw speed is not very valuable if we can't rely on the results we get. ENIAC, one of the first electronic computers, had a failure every few hours on the average; since the problems it was used to solve took about that much time to run, the likelihood that the results were correct wasn't very high. Particularly critical calculations were often run several times, and if the users got the same answer twice, they figured it was probably correct. By contrast, modern computers are almost incomprehensibly reliable. With almost any other machine, a failure rate of one in every million operations would be considered phenomenally low, but a computer with such a failure rate would make dozens or hundreds of errors per second.2

Blaming It on the Computer

On the other hand, if computers are so reliable, why are they blamed for so much that goes wrong with modern life? Who among us has not been the victim of an erroneous credit report, or a bill sent to the wrong address, or been put on hold for a long time because "the computer is down"? The answer is fairly simple: It's almost certainly not the computer that is really at fault. It may be the software, other equipment such as telephone lines, tape or disk drives, or any of the myriad "peripheral devices" that the computer uses to store and retrieve information and interact with the outside world. Usually, it's the software; when customer service representatives tell you that they can't do something obviously reasonable, you can bet that the problem is the software. For example, I once belonged to a 401K plan whose administrators provided statements only every three months, about three months after the end of the quarter; in other words, in July I found out how much my account had been worth at the end of March. To estimate how much I had in the meantime, I had to look up the share values in the newspaper and multiply by the number of shares. Of course, the mutual fund that issued the shares could tell its shareholders their account balances at any time of the day or night; however, the company that administered the 401K plan didn't bother to provide such a service, as it would have required doing some work.3 Needless to say, whenever I hear that "the computer can't do that" as an excuse for such poor service, I reply "Then you need some different programmers."

That Does Not Compute

All of this emphasis on computation, however, should not blind us to the fact that computers are not solely arithmetic engines. The most common application for which PCs are used is word processing, which is hardly a hotbed of arithmetical calculation. While we have so far considered only numeric data, this is a good illustration of the fact that computers also deal with another kind of information, which is commonly referred to by the imaginative term non-numeric variables. Numeric variables are those suited for use in calculations, such as in totalling a set of weights. On the other hand, non-numeric data are items that are not used in calculations like adding, multiplying, or subtracting: Examples are names, addresses, telephone numbers, Social Security numbers, bank account numbers, or drivers license numbers. Note that just because something is called a number, or even is composed entirely of the digits 0-9, does not make it numeric data by our standards. The question is how the item is used. No one adds, multiplies, or subtracts drivers license numbers, for example; they serve solely as identifiers and could just as easily have letters in them, as indeed some do.

For the present, though, let's stick with numeric variables. We've already discussed the short, but in the rest of this book we'll be using its big brother, the int, which is 4 bytes long and therefore can represent numbers from -2147483648 (-2^31) to 2147483647 (2^31-1). What can we do with variables of this type?

To do anything with them, we have to write a Java program, which consists primarily of a list of operations to be performed by the computer, along with directions that influence how these operations are to be translated into a form called "byte-code instructions", or just "byte codes" for short. This raises a couple of interesting points:

  1. What are byte codes, anyway?
  2. Why does our Java program have to be translated into byte codes?

Before we can answer these questions, we have to consider a more general notion, that of a "virtual computer". We'll discuss this immediately after adding some more terms to our vocabulary.

The Man behind the Curtain

Unlike many words in the vocabulary of computing, virtual has more or less retained its standard English definition: "That is so in essence or effect, although not formally or actually; admitting of being called by the name so far as the effect or result is concerned."4 In other words, a virtual computer would be something that acts just like a computer, but really isn't one. Who would want such a thing?

Apparently everyone, since virtual computer is just another name for what we have been calling software. This may seem a rash statement, but it really isn't. One of the most important mathematical discoveries (inventions?) of the twentieth century was Alan Turing's demonstration that it was possible to create a fairly simple computing device (called a Turing machine for some reason) that could imitate any other computing device. This machine works in the following way: You provide it with a description of the other computer you want it to imitate, and it follows those directions. Suppose we want a computer that calculates only trigonometric functions. Then we could theoretically write a set of instructions as to how such a computer would behave, feed it into a Turing machine, and have the Turing machine imitate the behavior of this theoretical "trigonometric computer".

This is undoubtedly interesting, but you may be wondering what it has to do with programming. Well, what we're actually doing when we write a Java program is creating instructions to be used by another program called the Java interpreter, describing the actions that would be taken by a hypothetical specialized computer that would solve the problem our program is designed to solve. When we run the program, the Java interpreter simulates these actions. In other words, our program is the definition of a particular virtual computer.

Of course, the Java interpreter can't really simulate any possible program, because there are limits in the amount of memory or disk space it has available, as well as limits on the speed of its execution. However, for problems that can be solved within those limits, it is a general-purpose computing facility that can be tailored to a particular problem by programming.

In the particular case of Java, there are actually two levels of virtuality; whereas your Java program is executed by the Java interpreter, the Java interpreter itself is executed by a real, physical computer component. As we've seen in the previous chapter, that component, the CPU (for Central Processing Unit), is the active ingredient inside a computer; the RAM and disk are merely the tools that it uses to store and retrieve data. In other words, the Java interpreter is a "virtual computer" that is simulated by the CPU, whereas a Java byte-code program is a "virtual computer" that can be simulated by the Java interpreter.

So now we can answer the question of what a byte-code instruction is: It's the primitive unit of computation in the Java interpreter, just as a machine instruction is the primitive unit of computation in a physical CPU.

Lost in Translation

Now we're ready to answer those questions that we put on hold for our little excursion into virtual reality. First, let's discuss byte codes in some more detail.

Like a physical CPU, the Java interpreter understands a limited set of instructions, each of which does a very simple task such as adding two numbers together or comparing two numbers to see which is greater. These are much simpler than the source-code statements that we write in our Java programs. Therefore, our source-code programs have to go through an intermediate step, called compilation, which converts them from source code to byte codes so that they can be executed by the Java interpreter.

It may not be obvious why we need both an interpreter and a compiler. It wasn't to Susan.

Susan: Can you have just an interpreter and no compiler? Do all interpreted languages then need a compiler?

Steve: Yes, you can have an interpreter without a compiler, but that's not very common because it is so inefficient; you don't want to do all the work to find out what each statement means every time you look at it, so virtually all interpreted languages translate the source code into a form that is easier to interpret at run time.

Susan: The interpreter doesn't work until run time?

Steve: Right.

The most basic tasks that the Java compiler performs are:5

  1. Converting variable names to numbers that will refer to areas of memory when the program is running. This allows us to use names for variables in our Java programs, while keeping the byte-code instructions simpler and easier to execute.
  2. Translating arithmetic and other operations (such as +, -, etc.) into the equivalent byte codes, including references to variables assigned in the previous step.

Susan had some questions about what the compiler actually does for us.

Susan: A byte-code instruction is in binary?

Steve: Yes, or hex.

Susan: So it attaches instructions to the variables?

Steve: Not exactly. We'll see shortly how it translates the named variables that we use in writing source code so they can be used when we're running a program.

This is probably a bit too abstract to be easily grasped, so let's look at an example. Figure littlenumeric shows some sample statements that do arithmetic calculations.

int i;

int j;

int k;

int m;


i = 5;

j = i * 3; // j is now 156k = j - i; // k is now 10

m = (k + j) / 5; // m is now 5

i = i + 1; // i is now 6

A little numeric calculation (Figure littlenumeric)

To enter such statements in the first place, follow the instructions in the "readme.txt" file on the CD in the back of the book.7 Once we have entered the statements for our program, we use the compiler to translate the programs we write into a form that the Java interpreter can perform; as defined in Chapter prologue.htm, the form we create is called source code, since it is the source of the program logic, whereas the form of our program that the Java interpreter can execute is called a byte-code program.

As I've mentioned previously, there are several types of variables, the int being only one of these types. Therefore, the compiler needs some explanatory material so that it can tell what types of variables you're using; that's what the first four lines of our little sample program fragment are for. Each line tells the compiler that the type of the variable i, j, k, or m is int, which specifies the range of values it can hold.8 After this introductory material, we move into the list of operations to be performed. This is called the executable portion of the program, as it actually causes the Java interpreter to do something when the program is executed; the operations to be performed, as mentioned previously, are called statements. The first one, i = 5;, sets the variable i to the value 5. A value such as 5, which doesn't have a name, but represents itself in a literal manner, is called (appropriately enough) a literal value.

This is as good a time as any for me to mention something that experienced programmers, especially C programmers, take for granted but has a tendency to confuse novices. This is the choice of the = sign to indicate the operation of setting a variable to a value, which is known technically as assignment. As far as I'm concerned, an assignment operation would be more properly indicated by some symbol suggesting movement of data, such as 5 => i;, meaning "store the value 5 into variable i". Unfortunately, it's too late to change the notation for the assignment statement, as such a statement is called, so you'll just have to get used to it. The = in a statement such as i = 5; means "set the variable on the left (in this case, i) to the value on the right (in this case, 5)".9 Now that I've warned you about that possible confusion, let's continue looking at the operations in the program. The next one, j = i * 3;, specifies that the variable j is to be set to the result of multiplying the current value of i by the literal value 3. The one after that, k = j - i;, tells the Java interpreter to set k to the amount calculated by subtracting i from j; that is, j - i. The most complicated line in our little program fragment, m = (k + j) / 5;, calculates m as the sum of adding k and j and dividing the result by the literal value 5. Finally, the line i = i + 1; sets i to the value of i plus the literal value 1.

This last may be somewhat puzzling; how can i be equal to i + 1? The answer is that an assignment statement is not an algebraic equality, no matter how much it may resemble one. It is a command telling the Java interpreter to assign a value to a variable. Therefore, what i = i + 1; actually means is "take the current value of i, add 1 to it, and store the result back into i." In other words, a Java variable is a place to store a value; the variable i can take on any number of values, but only one at a time; any former value is lost when a new one is assigned.

This notion of assignment was the topic of quite a few messages with Susan. Let's go to the first round.

Susan: I am confused with the statement i = i + 1;, when you have stated previously that i = 5;. So which one is it? How can there be two values for i?

Steve: There can't; that is, not at one time. However, i, like any other variable, can take on any number of values, one after another. First, we set it to 5; then we set it to 1 more than it was before (i + 1), so it ends up as 6.

Susan: Well, the example made it look as if the two values of i were available to be used by the Java interpreter at the same time. They were both lumped together as executable material.

Steve: After the statement i = 5;, and before the statement i = i + 1;, the value of i is 5. After the statement i = i + 1;, the value of i is 6. The key here is that a variable such as i is just our name for some area of memory that can hold only one value at one time. Does that clear it up?

Susan: So it is not like algebra? Then i is equal to an address of memory and does not really equate with a numerical value? Well, I guess it does when you assign a numerical value to it. Is that it?

Steve: Very close. A variable in Java isn't really like an algebraic variable, which has a value that has to be figured out and doesn't change in a given problem. A programming language variable is just a name for a storage location that can contain a value.

With any luck, that point has been pounded into the ground, so you won't have the same trouble that Susan did. Now let's look at exactly what an assignment statement does. If the value of i before the statement i = i + 1; is 5 (for example), then that statement will cause the Java interpreter to perform the following steps:10

  1. Take the current value of i (5).
  2. Add one to that value (6).
  3. Store the result back into i.

After the execution of this statement, i will have the value 6.

What's Going on Underneath?

In a moment we're going to dive a little deeper into how the Java interpreter accomplishes its task of manipulating data, such as we are doing here with our arithmetic program. First, though, it's time for a little pep talk for those of you who might be wondering exactly why this apparent digression is necessary. It's because if you don't understand what is going on under the surface, you won't be able to get past the "Sunday driver" stage of programming in Java. A good Java programmer needs to know a fair amount about the internal workings of the language, for reasons which will become very apparent later in the book. For the moment, you'll just have to take my word that working through these intricacies is essential; the payoff for a thorough grounding in these fundamental concepts of computing will be worth the struggle.

Underware?

I can almost hear the wailing and tooth gnashing out there. Do I expect you to deal with byte codes by yourself? You'll undoubtedly be happy to learn that this isn't necessary, as the compiler and the interpreter take care of these details. On the other hand, if you don't have some idea of how these programs work, you'll be at a disadvantage when you're trying to figure out how to make Java do what you want. Therefore, we're going to spend some time "playing compiler"; that is, I'll examine each statement and indicate what action the compiler might take as a result. I'll simplify the statements a bit to make the explanation simpler; you should still get the idea (I hope). Figure reallylittle illustrates the set of statements that I'll compile.11 However, before we start to analyze how the compiler does its job, we'll need to go over one of the fundamental methods that the Java interpreter and compiler use to organize their data: the stack.

Stacking the Deck

Variables in the program fragment we'll be considering are stored in a data structure called a stack; the name is intended to suggest the notion of stacking clean plates on a spring-loaded holder such as you might see in a cafeteria. The last plate deposited on the stack of plates will be the first one to be removed when a customer needs a fresh plate.

But what does this have to do with programming? I think this will become clearer when we see some examples. First, though, we'll need some new terms to describe the behavior of a stack.

  1. Push means to add a new item to the stack, causing the previously stored items to move down one location in the stack; that is, the item that was in slot 1 goes to slot 2, the entry in slot 2 goes to slot 3, and so on.
  2. Pop means to remove an item from the stack and store it in a specified destination, causing the other items in the stack to move up one location toward the top; that is, the item that was in slot 2 moves to slot 1, the one in slot 3 moves to slot 2, and so on.

A stack with one entry might look something like Figure stack1.

A stack with one entry (Figure stack1)


+--------------------+

TOP | 1234 |

+--------------------+

If we add (or push) another value on to the stack, say 999, the result would look like Figure stack2.

A stack with two entries (Figure stack2)


+--------------------+

TOP | 999 |

+--------------------+

2nd | 1234 |

+--------------------+

If we were to push one more item, this time with the value 1666, the result would look like Figure stack3.

A stack with three entries (Figure stack3)


+--------------------+

TOP | 1666 |

+--------------------+

2nd | 999 |

+--------------------+

3rd | 1234 |

+--------------------+

Now, if we retrieve (or pop) a value, we'll get the one on top; namely, 1666. Then the stack will look like it did in Figure stack2. The next value to be popped off the stack will be the 999, leaving us with the situation in Figure stack1 again. If we continue for one more round, we'll get the value 1234, leaving us with an empty stack.

As we'll see, the actual way that a stack is implemented is a bit different than is suggested by the "stack of plates" analogy, although the effect is exactly the same. Rather than keeping the top of the stack where it is and moving the data (a slow operation), the data are left where they are and the address stored in a stack pointer is changed, which is a much faster operation. In other words, whatever address the stack pointer is pointing to is by definition the "top of the stack".12 The idea of a stack led to some discussions with Susan. Here's the first installment:

Susan: Where is the stack?

Steve: The actual memory locations used to hold the items in the stack are just like any other locations in RAM; what makes them part of the stack is how they are used. As always, one memory location can hold only one item at a given time, so the locations used to hold entries on the stack cannot be simultaneously used for something else, like byte codes.

Susan: Where you say "what makes them part of the stack is how they are used." How is that?

Steve: RAM is RAM. It can be used to store programs, data on the stack, or other types of data that we'll get to later. What distinguishes these is how the memory is used, not what it's physically made of.

Susan: Yes, but what is different about the use of the RAM used in the stack?

Steve: It's used to hold the data in the stack, rather than byte codes or other kinds of data.

Susan: Then how does the stack pointer "talk" to the stack to change what it is pointing to?

Steve: It doesn't have to. Whatever the stack pointer is pointing to is by definition the top of the stack; therefore, changing the contents of the stack pointer changes the top of the stack.

Susan: So what makes the stack pointer change?

Steve: The interpreter, as it is executing the program. Each byte code instruction has a defined effect on the stack pointer (pushing one or more values, popping one or more values, doing both, or doing neither.

Susan: What do you mean as the interpreter is executing the program? Do you mean that it doesn't work until run time?

Steve: Yes.

Susan: Okay, then if this is what the interpreter does, what does the compiler do? Does it just compile the program into byte-codes that the interpreter can read?

Steve: Yes.

Susan: Then on stacks, is it like this? The stack stays still, and the pointer to the value's address moves.

Steve: The data stays where it is in memory, and the stack pointer points to different places in memory. Whatever it points to is the top of the stack.

Susan: Where is the stack pointer relative to the stack?

Steve: It isn't relative to the stack. It's a register, which doesn't have a memory address.

Susan: I don't get that.

Steve: A register is a storage area that is on the same chip as the CPU. Programs use registers to hold data items that are actively in use; data in registers can be accessed much faster than if it had to be retrieved from RAM every time it was needed.

Susan: But I thought that the stack was in the registers.

Steve: No, the stack is in RAM. The stack pointer is the register that indicates exactly where the top of the stack is in RAM.

Susan: Put that in the book again, darling. Repeat this many times, and then maybe I will remember it.

Steve: It's worth a try.

Compiler's-Eye View

Now we're just about ready to start analyzing a small program fragment to see how it is actually represented in Java byte codes. Here are the rules of this "game":

  1. All numbers in the Java program are decimal; all addresses of the byte codes are hexadecimal.
  2. Calculations use the stack to store intermediate values.
  3. Byte codes are stored at (hexadecimal) addresses starting at 0.
  4. All local variables are stored in a series of "local variable slots" in a reserved area of the stack.

Figure reallylittle shows the program fragment that we're going to compile.

int i;

int j;


i = 5;

j = i + 3;

A really little numeric calculation (Figure reallylittle)

Now we're ready to start compiling. The first statement, int i;, tells me to allocate storage for an int variable called i; since i is an int, it takes up 4 bytes of memory, which corresponds to one "slot" on the stack. Therefore, the "memory map" at the beginning of our program fragment will look like Figure compile1 so far, with the ?s indicating that the variable i hasn't been initialized yet; that will happen before the program fragment starts.

	                    Stack

+--------------------------------------------+

| Local Value Meaning |

| Variable |

| Slots |

| |

| +--------------+ |

| 1 | ???????? | i |

| +--------------+ |

| |

+--------------------------------------------+



Compiling, part 1 (Figure compile1)

As you might have guessed, this exercise was the topic of a considerable amount of discussion with Susan. Here's how it started:

Susan: So the first thing we do with a variable is to tell the address that its name is i, but no one is home, right? It has to get ready to accept a value. Could you put a value in it without naming it, just saying that the first entry on the stack has a value of 5? Why does it have to be called i first?

Steve: The reason that we use names instead of addresses is because it's much easier for people to keep track of names than it is to keep track of addresses. Thus, one of the main functions of a compiler is to allow us to use names that are translated into addresses for the computer's use.

Susan: What are those local variable slots?

Steve: They're the part of the stack that is used to store local variables.

Susan: So is the stack where the compiler puts the variables?

Steve: Yes, the compiler puts them in the local variable part of the stack.

Susan: Are those the places in the stack that do the arithmetic?

Steve: Close, but not quite: The compiler puts the variables in the local variable part of the stack and generates byte codes to transfer them from and to the active part of the stack, where the arithmetic byte codes can access them.

The second statement, int j;, tells me to allocate storage for an int variable called j. Just as with i, j takes 4 bytes of storage, or one slot position on the stack. As before, the ?s indicate that no value has been assigned to this variable yet; it will also be initialized to 0 at run time, so the resulting "memory map" as of the beginning of our program fragment will look like Figure compile2.

	                    Stack

+--------------------------------------------+

| Local Value Meaning |

| Variable |

| Slots |

| |

| +--------------+ |

| 1 | ???????? | i |

| +--------------+ |

| 2 | ???????? | j |

| +--------------+ |

| |

+--------------------------------------------+



Compiling, part 2 (Figure compile2)

The next line is blank, so we skip it. This brings us to the statement i = 5; which is an executable statement, so we need to generate one or more byte codes to execute it. Figure compile3 shows what the code for this program fragment looks like so far, with the statement being compiled listed as a comment before the byte codes that are responsible for executing it.

					Stack

+--------------------------------------------+

| Local Value Meaning |

| Variable |

| Slots |

| +--------------+ |

| 1 | ???????? | i |

| +--------------+ |

| 2 | ???????? | j |

| +--------------+ |

| |

+--------------------------------------------+



Code

+-----------------------------------------------------------+

| Address Byte Code Description |

| |

| // i = 5; |

| 00000000 iconst_5 Push 5 |

| 00000001 istore_1 Pop into i |

| |

+-----------------------------------------------------------+

Compiling, part 3 (Figure compile3)

I'm sure you're wondering what those iconst_5 and istore_1 instructions mean. Susan certainly did.

Susan: What is "iconst"?

Steve: A byte code instruction that pushes a (specified) constant on the stack. For example, iconst_5 means to push the value 5.

Susan: So "push 5" means it puts 5 to the top of the stack?

Steve: Right. The rest of the items on the stack stay on the stack, logically moving down one position.

As soon as we compile the rest of the code, we'll see how the Java interpreter performs arithmetic operations.

The last statement, j = i + 3;, is the most complicated statement in our program, and it's not that complicated. As with the previous statement, it's executable, which means we need to generate byte codes to execute it. The byte codes with addresses 2 through 5 are responsible for this operation, as we'll see in gory detail as soon as we get done compiling.

Figure compile4 shows what the "memory map" looks like now.

					Stack

+--------------------------------------------+

| Local Value Meaning |

| Variable |

| Slots |

| +--------------+ |

| 1 | 00000000 | i |

| +--------------+ |

| 2 | 00000000 | j |

| +--------------+ |

| |

+--------------------------------------------+



Code

+-----------------------------------------------------------+

| Address Byte Code Description |

| |

| // i = 5; |

| 00000000 iconst_5 Push 5 |

| 00000001 istore_1 Pop into i |

| |

| // j = i + 3; |

| 00000002 iload_1 Push i |

| 00000003 iconst_3 Push 3 |

| 00000004 iadd Pop top two stack |

| entries, push the sum |

| 00000005 istore_2 Pop into j |

| |

+-----------------------------------------------------------+

Compiling, part 4 (Figure compile4)

Here's the next installment of the byte-code discussion with Susan:

Susan: So iload_1 is putting i into the local variable slot #1.

Steve: That's pretty close, but not exactly right. Actually, the iload_1 instruction gets the value of the local variable in slot #1 (in this case, i) and pushes it onto the stack.

Susan: Then iload_1 is a retrieving function?

Steve: Yes, it retrieves the value of the local variable in slot #1 and makes it available for calculation (by pushing it on the stack).

Susan: What are the exact functions of iadd and istore and how do they do that?

Steve: I'm glad you asked me that question. First, iadd is the "integer add" instruction. It pops the top two entries off the stack, adds them together, and pushes the result back on the stack.

The istore instruction is sort of the opposite of iload; it pops a value from the stack and stores it in a local variable. In our example program, we're using the istore_2 instruction to store the result of our calculation into local variable #2 (that is, j).

How It All Stacks Up

Having examined what the compiler does at compile time with the preceding little program fragment, the next question is what happens when the compiled program is interpreted. When we start out, the stack and code areas of memory we're concerned with will look like Figure execute1.13


Stack

+--------------------------------------------+

| Local Value Meaning |

| Variable |

| Slots |

| +--------------+ |

| 1 | 00000000 | i |

| +--------------+ |

| 2 | 00000000 | j |

| +--------------+ |

| |

| Expression |

| Evaluation (empty) |

| Area |

| |

+--------------------------------------------+



Code

+-----------------------------------------------------------+

| Address Byte Code Description |

| |

| // i = 5; |

| 00000000 iconst_5 Push 5 |

| 00000001 istore_1 Pop into i |

| |

| // j = i + 3; |

| 00000002 iload_1 Push i |

| 00000003 iconst_3 Push 3 |

| 00000004 iadd Pop top two stack |

| entries, push the sum |

| 00000005 istore_2 Pop into j |

| |

+-----------------------------------------------------------+



Execution, part 1 (Figure execute1)

First of all, you should note that the variables i and j have been set to the default value for an int, namely, 0. This is guaranteed by the Java language specification.

Now we're just about ready to examine what all those byte codes do when we execute the program. Let's start at the beginning, with the two common ways that electronic calculators do arithmetic operations. The first, used by most calculators except for those made by Hewlett-Packard, is called algebraic operation. This method, as the name implies, works in a way similar to how we write arithmetic expressions in Java. For example, to calculate the average of two variables, i and j, we would write the expression (i + j)/2. At run time, the code would add i and j, then divide by 2 to get the answer.

While this method of evaluating arithmetic expressions is seemingly natural for humans, it is not the way that byte code programs work in Java. Instead, the Java compiler converts such expressions into a form similar to the one used by Hewlett-PackardTM calculators. This form is called RPN (for Reverse Polish Notation, because it was invented by the Polish logician Jan Lukasiewicz); it uses a stack to store values during the evaluation of an expression. The area of the stack used for this purpose is labeled as the "expression evaluation area" in the figures illustrating the execution of our program fragment.

As you might not be surprised to hear, Susan had some questions about this method of calculating arithmetic expressions.

Susan: What is the "expression evaluation area"?

Steve: The part of the stack used to store intermediate values during execution of the byte codes.

Susan: I need some more explanation of this.

Steve: The expression evaluation area is a part of the stack used during the calculation of an arithmetic expression. For example, in the statement "j = i + 3", the value of the expression "i + 3" is calculated in a couple of steps. First, the value of i is pushed onto the stack; then 3 is pushed onto the stack; finally the "add" instruction adds the top two elements of the stack (i and 3). Now that we have the value of i + 3 on the top of the stack, that value is stored into j. Does that help?

Susan: So the expression evaluation area is like your desktop? A place to work?

Steve: Possibly, but maybe a better analogy would be a blackboard that you calculate something on, but clean off once you have the final result.

Susan: How do I know which byte codes push and which ones pop?

Steve: You can look it up in The Java Virtual Machine Specification, by Tim Lindholm and Frank Yellin (Addison-Wesley, 1997). However, you won't normally need to do this, because the compiler will take care of it for you.

Now let's see exactly how the evaluation of our statements works. Starting out with an empty expression evaluation area, we execute the iconst_5 byte code, which pushes the constant int value 5 onto the stack. This leaves us with the situation in Figure execute2.

					Stack

+--------------------------------------------+

| Local Value Meaning |

| Variable |

| Slots |

| +--------------+ |

| 1 | 00000000 | i |

| +--------------+ |

| 2 | 00000000 | j |

| +--------------+ |

| |

| Expression |

| Evaluation |

| Area |

| |

| +--------------+ |

| 1 | 00000005 | 5 |

| +--------------+ |

+--------------------------------------------+



Code

+------------------------------------------------------------+

| Address Byte Code Description |

| |

| // i = 5; |

| 00000000 iconst_5 Push 5 |

| 00000001 istore_1 Pop into i |

| |

| // j = i + 3; |

| 00000002 iload_1 Push i |

| 00000003 iconst_3 Push 3 |

| 00000004 iadd Pop top two stack |

| entries, push the sum |

| 00000005 istore_2 Pop into j |

| |

+------------------------------------------------------------+

Execution, part 2 (Figure execute2)

The next step is to execute the istore_1 byte code, which pops the value from the top of the stack into local variable 1 (i); that is, it sets i to the value on the top of the stack, and removes that value from the stack. This leaves us with the situation in Figure execute3. Note that the value of i has been changed to 5, and the expression evaluation area of the stack is empty.

					Stack

+--------------------------------------------+

| Local Value Meaning |

| Variable |

| Slots |

| +--------------+ |

| 1 | 00000005 | i |

| +--------------+ |

| 2 | 00000000 | j |

| +--------------+ |

| |

| Expression |

| Evaluation (empty) |

| Area |

| |

+--------------------------------------------+



Code

+-----------------------------------------------------------+

| Address Byte Code Description |

| |

| // i = 5; |

| 00000000 iconst_5 Push 5 |

| 00000001 istore_1 Pop into i |

| |

| // j = i + 3; |

| 00000002 iload_1 Push i |

| 00000003 iconst_3 Push 3 |

| 00000004 iadd Pop top two stack |

| entries, push the sum |

| 00000005 istore_2 Pop into j |

| |

+-----------------------------------------------------------+

Execution, part 3 (Figure execute3)

Next, we start on the statement j = i + 3;. The Java interpreter will perform this operation by reloading the value of i (variable 1), pushing a literal 3 onto the stack, adding the top two elements of the stack, and finally storing the result into variable 2 (j). Let's go over that step by step, starting with the byte code iload_1, which pushes the value of local variable 1 (i) onto the stack. This leaves us with the situation in Figure execute4.

					Stack

+--------------------------------------------+

| Local Value Meaning |

| Variable |

| Slots |

| +--------------+ |

| 1 | 00000005 | i |

| +--------------+ |

| 2 | 00000000 | j |

| +--------------+ |

| |

| Expression |

| Evaluation |

| Area |

| |

| +--------------+ |

| 1 | 00000005 | i |

| +--------------+ |

+--------------------------------------------+



Code

+-----------------------------------------------------------+

| Address Byte Code Description |

| |

| // i = 5; |

| 00000000 iconst_5 Push 5 |

| 00000001 istore_1 Pop into i |

| |

| // j = i + 3; |

| 00000002 iload_1 Push i |

| 00000003 iconst_3 Push 3 |

| 00000004 iadd Pop top two stack |

| entries, push the sum |

| 00000005 istore_2 Pop into j |

| |

+-----------------------------------------------------------+

Execution, part 4 (Figure execute4)

Next, we execute the byte code iconst_3, which pushes the value 3 onto the stack. This leaves the situation shown in Figure execute5.

					Stack

+--------------------------------------------+

| Local Value Meaning |

| Variable |

| Slots |

| +--------------+ |

| 1 | 00000005 | i |

| +--------------+ |

| 2 | 00000000 | j |

| +--------------+ |

| |

| Expression |

| Evaluation |

| Area |

| |

| +--------------+ |

| 1 | 00000003 | 3 |

| +--------------+ |

| 2 | 00000005 | i |

| +--------------+ |

+--------------------------------------------+



Code

+-----------------------------------------------------------+

| Address Byte Code Description |

| |

| // i = 5; |

| 00000000 iconst_5 Push 5 |

| 00000001 istore_1 Pop into i |

| |

| // j = i + 3; |

| 00000002 iload_1 Push i |

| 00000003 iconst_3 Push 3 |

| 00000004 iadd Pop top two stack |

| entries, push the sum |

| 00000005 istore_2 Pop into j |

| |

+-----------------------------------------------------------+

Execution, part 5 (Figure execute5)

Next, we execute the iadd byte code, which pops the top two entries in the stack, adds them together, and pushes the sum. This leaves the situation shown in Figure execute6.

					Stack

+--------------------------------------------+

| Local Value Meaning |

| Variable |

| Slots |

| +--------------+ |

| 1 | 00000005 | i |

| +--------------+ |

| 2 | 00000000 | j |

| +--------------+ |

| |

| Expression |

| Evaluation |

| Area |

| |

| +--------------+ |

| 1 | 00000008 | i+3 |

| +--------------+ |

+--------------------------------------------+



Code

+-----------------------------------------------------------+

| Address Byte Code Description |

| |

| // i = 5; |

| 00000000 iconst_5 Push 5 |

| 00000001 istore_1 Pop into i |

| |

| // j = i + 3; |

| 00000002 iload_1 Push i |

| 00000003 iconst_3 Push 3 |

| 00000004 iadd Pop top two stack |

| entries, push the sum |

| 00000005 istore_2 Pop into j |

| |

+-----------------------------------------------------------+

Execution, part 6 (Figure execute6)

Finally, we execute the istore_2 byte code, which stores the top entry in the stack into local variable 2 (j). This leaves the situation shown in Figure execute7.

					Stack

+--------------------------------------------+

| Local Value Meaning |

| Variable |

| Slots |

| +--------------+ |

| 1 | 00000005 | i |

| +--------------+ |

| 2 | 00000008 | j |

| +--------------+ |

| |

| Expression |

| Evaluation (empty) |

| Area |

+--------------------------------------------+



Code

+-----------------------------------------------------------+

| Address Byte Code Description |

| |

| // i = 5; |

| 00000000 iconst_5 Push 5 |

| 00000001 istore_1 Pop into i |

| |

| // j = i + 3; |

| 00000002 iload_1 Push i |

| 00000003 iconst_3 Push 3 |

| 00000004 iadd Pop top two stack |

| entries, push the sum |

| 00000005 istore_2 Pop into j |

| |

+-----------------------------------------------------------+

Execution, part 7 (Figure execute7)

The difference between this situation and the last one is that the expression evaluation area of the stack is empty and the value of j has been set to 8.

Here's the next installment of my discussion with Susan on this topic:

Susan: Now this may sound like a very dumb question, but please tell me where 5 comes from? I mean if you are going to move the value of 5 onto the stack, where is 5 hiding to take it from and to put it in the stack? Is it stored somewhere in memory that has to be moved, or is it simply a function of the user just typing in that value?

Steve: No, it's not a dumb question at all. The 5 is part of the byte code iconst_5. You see, because small numbers (0 through 5 and minus 1) are so common in Java programs, the Java interpreter has special byte codes to push those values onto the stack. If the number were greater than 5, it would be stored as a literal value immediately following a byte code that means "push a literal value".

By the way, don't be misled by this example into thinking that all byte codes are 1 byte in length. It's just a coincidence that all of the ones I've used here are of that length. The actual size of a byte-code instruction can vary considerably; in fact, some byte-code instructions can be virtually unlimited in length. Most byte codes, however, take from 1 to 3 bytes.

A Cast of Characters

This should give you some idea of how numeric variables and values work. But what about non-numeric ones?

This brings us to the subject of two new variable types and the values they can contain. These are the char and its relative, the String. What are these good for, and how do they work?14 A variable of type char corresponds to 1 character of text, which in Java occupies 2 bytes (16 bits) of storage. Since a char has 16 bits, it can hold any of 65536 (2^16) values, which is the same number of values that a short can hold. So what's the difference between these two types?

The main purpose of a char is to represent an individual letter, digit, punctuation mark, "special character" (e.g., $, @, #, %, and so on), or one of the other "printable" and displayable units from which words, sentences, and other textual data such as this paragraph are composed.15 We don't need anywhere near 65,536 possibilities to represent any character in English, as well as a number of European languages; in fact 256 possibilities is enough to handle all of the characters in those languages, so in most programming languages other than Java a char is only 1 byte long.

However, the written forms of "ideographic" languages such as Chinese and Korean consist of far more than 256 characters, so 1 byte isn't going to do the trick for these languages. While they have been supported to some extent by schemes that switch among a number of sets of 256 characters each, such clumsy approaches to the problem made programs much more complicated and error prone. As the international market for software is increasing rapidly, it has become more important to have a convenient method of handling large character sets. To solve this problem, the Unicode standard, which uses 2 bytes per character, has been developed. This is the representation that Java uses for its chars.16

Even in an ideographic language, one char isn't good for much by itself, so we often use groups of them, called Strings, to represent a significant amount of text. Just as with numeric values, these variables can be set to literal values, which represent themselves. Figure basic00 is an example of how to specify and use each of these types we've just encountered.

code/basic00/basic0~1.jav

Some real characters and Strings (code\Basic00\Basic00.java) (Figure basic00)

This file, Basic00.java, is the source code for the first complete program we've seen, so there are a couple of new constructs that I'll have to explain to you. Before we get to the explanation, however, I should mention that one of the reasons that Java currently doesn't run under DOS or Windows 3.1 is that it requires long filenames, that is, ones that have more than 8 characters before the period or more than three characters after the period. In this case, of course, it is the extension, .java, that is not legal under DOS or Windows 3.1.

By the way, in case the program in Figure basic00 doesn't seem very useful, that's because it isn't; it's just an example of the syntax of defining and using variables and literal values. However, we'll use these constructs to do useful work later, so going over them now isn't a waste of time.

The first construct we have to examine is the line public class Basic00, which has three components. The first component of this line is the keyword public, which means that we're defining something that is generally available to any program that wants to use it. The particular kind of "something" that we're making available is specified by the second component of this line: the keyword class, which tells the Java compiler that we are beginning the definition of a class. What is a class? That's a very good question, because every program in Java is composed of the definitions of one or more classes. This is obviously a very important part of the language; however, you'll need a significant amount of background in other aspects of the Java language before its explanation is likely to mean very much to you, so I'm deferring that discussion until Chapter inventor.htm. For now, just take my word that every Java program has to start with a line telling the compiler the name of the class that we're defining.

As this brief explanation suggests, the third component of this line is Basic00, which is the name of the particular class we're defining in this program. We could call it whatever we wanted, but since it's the first example program in this chapter on the basics of programming, I named it Basic00 so that you and I could keep track of it more easily.

Once we have specified the name of the class that we're creating, we can define the piece of code that is going to actually do the work for us. That's the job of the next line:

public static void main( String args[ ] )

This will be the first line of every main function that we will examine in this book. Eventually I'll be able to explain exactly what every part of this line means, but you don't have enough background yet. Therefore, you'll have to take my word that everything on that line will eventually mean something.

However, there is one thing I can explain: the meaning of main. Java has a rule that execution always starts at the place called main. Since this is where we want our program to start executing, we have to call it by that name; marking the place where we want to start is the main purpose of the line we're discussing.

You may also be puzzled by the function of the other statements in this program. If so, you're not alone. Let's see the discussion that Susan and I had about that topic.

Susan: Okay, in the example why did you have to write c2 = c1;? Why not B? Why make one thing the same thing as the other? Make it different. Why would you even want c2=c1; and not just say c1 twice, if that is what you want?

Steve: It's very hard to think up examples that are both simple enough to explain and realistic enough to make sense. You're right that this example doesn't do anything useful; I'm just trying to introduce what both the char type and the String type look like.

Susan: Come to think of it, what does c1='A'; have to do with the statement s1= "Congratulations! ";? I don't see any relationship between one thing and the other.

Steve: This is the same problem as the last one. They have nothing to do with one another; I'm using an admittedly contrived example to show how these variables are used.

Susan: I am glad now that your example of chars and Strings (put together) didn't make sense to me. That is progress; it wasn't supposed to.

What does this useless but hopefully instructive program do? As is always the case, we have to tell the compiler what the types of our variables are before we can use them. In this case, c1 and c2 are of type char, whereas s1, s2, and s3 are Strings. After taking care of these formalities, we can start to use the variables. In the first executable statement, c1 = 'A';, we set the char variable c1 to a literal value, in this case a capital A; we need to surround this with single quotation marks (') to tell the compiler that we mean the letter A rather than a variable named A. In the next line, c2 = c1;, we set c2 to the same value as c1 holds, which of course is 'A' in this case. The next executable statement, s1 = "Congratulations! ";, as you might expect, sets the String variable s1 to the value "Congratulations! ", which is a String literal.17 A String literal is a type of literal that we use to assign values to variables of type String. In the statement s1 = "Congratulations! "; we use a quotation mark, in this case the double quote ("), to tell the compiler where the literal value starts and ends.

You may be wondering why we need two different kinds of quotes in these two cases. There really isn't a very good reason that I can think of other than to allow the compiler to tell whether we're using a compatible type of literal for a char or a String variable. As far as I can tell, this distinction is left over from C and C++, where it made more sense, but for reasons that aren't relevant here.

The Right Type

As the above discussion suggests, every variable in Java has a type. While some languages allow the same variable to be used in different ways at different times, in Java any given variable always has the same type; for example, a char variable can't change into an int. At first glance, it seems that it would be much easier for programmers to be able to use variables any way they like; why is Java so restrictive?

The Java type system, as this feature of a language is called, is specifically designed to minimize the risk of misinterpreting or otherwise misusing a variable. It's entirely too easy in some languages to change the type of a variable without meaning to; the resulting bugs can be very difficult to find, especially in a large program. In Java, the usage of a variable can be checked by the compiler. This static type checking allows the compiler to tell you about many errors that otherwise would not be detected until the program is running (dynamic type checking). This is particularly important in systems that need to run continuously for long periods of time. While you can reboot your machine if your word processor crashes due to a run-time error, this is not acceptable as a solution for errors in the telephone network, for example.

Of course, you probably won't be writing programs demanding the degree of reliability that the telephone network requires any time soon, but strict static type checking is still worthwhile in helping eliminate errors at the earliest possible stage in the development of our programs.

You might get the impression from this discussion that Java is suitable for developing large applications that have to run indefinitely. Is this true?

Reality Check

Most other books I've seen on Java contain a statement something like the following, often at roughly this point in the exposition:

Java is the be-all and end-all of computer languages. It is infinitely superior to every other language, especially C++, which has served its purpose now that its obvious successor, Java, has been invented. Java is suitable for every possible application, and most impossible ones. Furthermore, Java is a very simple language that you can learn in a few minutes, probably in your sleep.18
This is considerably at variance with the truth. Java has some advantages over other languages, including C++. It also has some serious drawbacks that make it eminently unsuitable for many types of applications, especially large ones that have to operate for a long time without fail; for example, it would not be a good idea to write programs to control the telephone network in Java. I'll explain some of these drawbacks as we run across them in this book.

As for the claim of how simple Java is: I can't agree with that either. Java is not significantly simpler than other computer languages, and in particular it's not any simpler than the part of C++ that you should use to design new programs (as contrasted with the "dark corners" of C++ that you can stumble into when dealing with old programs). However, you don't have to take my word for the complexity of Java; you should have a pretty good idea of how complex it is by the time you get through this book!

Some Strings Attached

After that info-mercial for the advantages of static type checking (and deflation of some of the hype about Java), we can resume our examination of Strings. You may have noticed that there's a space character at the end of the String "Congratulations! ". That's another reason why we have to use a special character like " (the double quote) to mark the beginning and end of a String; how else would the compiler know whether that space is supposed to be part of the String or not? The space character is one of the non-printing characters (or non-display characters) that controls the format of our displayed or printed information; imagine how hard it would be to read this book without space characters! While we're on the subject, I should also tell you about some other characters that have special meaning to the compiler. They are listed in Figure specialchar.

Name Graphic Use

single quote ' surrounds a single-character value
double quote " surrounds a multi-character value
semicolon ; ends a statement
curly braces { } groups statements together
parentheses ( ) surrounds part of a statement19
backslash \ tells the compiler that the next
character should be treated
differently from the way that
it would normally be treated20

Special characters for program text (Figure specialchar)

I compiled Figure specialchar at the instigation of guess who.

Susan: How about you line up all your cute little " ' \ ; things and just list their meanings? I forget what they are by the time I get to the next one. Your explanations of them are fine, but they are scattered all over the place; I just want one place that has all the explanations.

Steve: That's a good idea; I think I will.

Our next task will be to see how we get the values of our Strings and chars to show up on the screen.

In and Out

Most programs need to interact with their users, both to ask them what they want and to present the results when they are available. The computer term for this topic is I/O (short for "input/output"). We'll start by getting information from the keyboard and displaying it on the screen; later, we'll go over the more complex I/O functions that allow us to read and write data on the disk.

The final line of code in Figure basic00, System.out.println( s1 + s2 + c2 + s3);, displays information on the screen. In this case, the output will be "Congratulations! You got an A on the test.". How does this occur exactly?

The first construct on this line is System.out, which is a predefined Java variable that is "connected" to the screen. This variable has several built-in ways to display information; the one we're using here is called println, which is short for "print and move to the next line". The value of the expression inside the parentheses after println will be displayed on the screen (the print part of println), and the cursor will be moved to the beginning of the next line (the ln part of println).println

That explains why we'll see some output on the screen, but it doesn't explain the exact output that we'll see, which depends on what's inside the parentheses. In this case, that's s1 + s2 + c2 + s3. I don't blame you if you have some trouble understanding how we can "add" Strings and chars together; that's not exactly intuitively obvious to the casual observer.

What the + means here actually is something like "add", but more precisely it means "add some text data to the end of some other text data". In the current case, that whole expression produces the following sequence of events:

  1. Take the contents of s2, and add that to the end of the contents of s1. Since s1 contains "Congratulations! " and s2 contains "You got an ", the result will be the value "Congratulations! You got an ".
  2. Take the results of the previous operation and add the contents of c1 ('A') to its end, resulting in the value "Congratulations! You got an A".
  3. Take the results of the previous operation and add the contents of s3 (" on the test") to its end, resulting in the value "Congratulations! You got an A on the test."

Finally, the resulting value will be displayed on the screen and the cursor will be moved to the next line.

Susan had an incisive observation about this example program.

Susan: What if the student got a 'B'? Then the second sentence would read "You got an B", which is incorrect.

Steve: That's a good point. I think I'll make fixing that problem into an exercise.

So much for (simple) output. Input from the keyboard is almost as simple. Let's modify our little sample to use it, as shown in Figure basic02.

code/basic02/basic0~1.jav

Simple input (code\Basic00\Basic02.java) (Figure basic02)

There are a couple of new things in this program. Let's start at the top with the line import WAJ.*;. I'll explain exactly what that line means later, but for now it's sufficient to know that this tells the Java compiler where to find some code that I've written to make it easier for you to write simple Java programs.

The line System.out.print("What is your name? "); is almost like the simple output statement we've seen before, with two exceptions. First, it displays a constant String value on the screen ("What is your name? "), rather than the calculated value we saw earlier. Second, instead of using the println operation, it uses print. The difference between these two is simply that print doesn't move the cursor to the next line, as println does. In the current case, we want to let the user of the program type his or her answer in on the same line as the question rather than on the next line, so this is appropriate.

The next line that has anything unfamiliar is the one that says s4 = RWVar.readString(System.in);. You should be able to guess that the first part of that line, s4 =, sets the String variable s4 to some value, but what value is that exactly?

It's whatever you type in when you see the question What is your name? on the screen. That's what RWVar.readString is for: It takes input up to the next ENTER, and turns it into a String. In this case, we use that String to set the value of the String variable on the left of the equals sign. There are similar ways to read other kinds of variables, like ints, as we'll see later.

But what about System.in? As its name suggests, this is the counterpart to System.out; it supplies characters from the keyboard rather than writing characters to the screen.

Susan had some questions about these little programs, beginning with the question of case sensitivity.

Susan: Are the words such as char and public case sensitive? I had capitalized a few of them just out of habit because they begin the sentence and I am not sure if that was the reason the compiler gave me so many error messages. I think after I changed them I reduced a few messages.

Steve: Everything in Java is case sensitive. That includes keywords like char, public, and so on, as well as your own variables. That is, if you have a variable called Name and another one called name, those are completely different and unrelated to one another. This also applies to System.in and System.out; you have to write them just as they appear here, or the compiler won't be able to figure out what you mean.

Susan: What does RWVar stand for?

Steve: "Reading and writing variables".

Susan: Why is the word System used?

Steve: Because those variables (System.out and System.in) are supplied by the system.

Susan: The WAJ.* is confusing to me; what does it do?

Steve: It tells the compiler that we want to be able to use some facilities that are stored under the name WAJ (which is short for "Who's Afraid of Java?"). That's where I've put RWVar, so if we hadn't included the line import WAJ.*, we wouldn't be able to use RWVar.readString and the other RWVar facilities.

If Only You Knew

In our examples so far, the program always executes the same statements in the same order. However, any real program is going to need to alter its behavior according to the data it is processing. For example, in a banking application, it might be necessary to send out a notice to a depositor whenever the balance in a particular account drops below a certain level; or perhaps the depositor would just be charged some exorbitant fee in that case. Either way, the program has to do something different depending on the balance. In particular, let's suppose that the "Absconders and Defaulters National Bank" has a minimum balance of $10,000. Furthermore, let's assume that if you have less than that amount on deposit, you are charged a $20 "service charge". However, if you are foolish enough to leave that ridiculous amount of money on deposit, then they will graciously allow you to get away with not paying them for the privilege of lending them your money (without interest, of course). To determine whether or not you should be charged for your checking account, the bank can use an if statement, as shown in Figure if.statement.

code/basic03/basic0~1.jav

Using an if statement (code\Basic03\Basic03.java) (Figure if.statement)

This program starts by displaying the line

Please enter your bank balance:

on the screen. Then it uses the line balance = RWVar.readInt(System.in); to allow you to type in your balance, followed by the key (so it knows when you're done). This RWVar.readInt facility is similar to our previous use of RWVar.readString, except that it read an int rather than a String value. Of course, System.in still supplies data from the keyboard.

Next, the conditional statement checks whether you're a "good customer". If your balance is less than $10,000, the next statement is executed, which displays the lineThis explanation assumes that the "10000" is the balance in dollars. Of course, this doesn't account for the possibility of balances that aren't a whole number of dollars. I'll mention a possible solution to this problem later.

If the condition is false (that is, you have at least $10,000 in the bank), the computer skips the statement that asks you to remit $20; instead, it executes the one after the else, which tells you to have a nice day. That's what else is for; it specifies what to do if the condition specified in the if statement is false (that is, not true). If you typed in a number 10000 or higher, the program would display the line22

Have a nice day!

You don't have to specify an else if you don't want to. In that case, if the if condition isn't true, the program just goes to the next statement as though the if had never been executed.

While We're on the Subject

The while statement is another way of affecting the order of program execution. This conditional statement executes the statement under its control as long as a certain condition is true. Such potentially repeated execution is called a loop; a loop controlled by a while statement is called, logically enough, a while loop. Figure basic04 is a program that uses a while loop to challenge the user to guess a secret number from 0 to 9, and keeps asking for guesses until the correct answer is entered.

code/basic04/basic0~1.jav

Using a while statement (code\Basic04\Basic04.java) (Figure basic04)

There are a few wrinkles here that we haven't seen before. Although the while statement itself is fairly straightforward, the meaning of its condition != isn't intuitively obvious. However, if you consider the problem we're trying to solve, you'll probably come to the (correct) conclusion that != means "not equal", since we want to keep asking for more guesses while the Guess is not equal to our Secret number.23 Since there is a comparison operator that tests for "not equal", you might want to know how to test for "equal" as well. The answer is that you can test whether two int values are equal by using the == operator; we'll see in the next chapter why we can't use = for this task.

Would an if statement with an else clause would serve as well as the while? After all, if is used to select one of two alternatives, and the else could select the other one. The answer is that this would allow the user to take only one guess; the while loop lets the user try again as many times as needed to get the right answer.

Now you should have enough information to be able to write a simple program of your own, as Susan asked to do at this point.

Susan: Based on what you have presented in the book so far, send me a setup, an exercise for me to try to figure out how to program, and I will give it a try. I guess that is the only way to do it. I can't even figure out a programmable situation on my own. So if you do that, I will do my best with it, and that will help teach me to think. (Can that be?) Now, if you do this, make it simple, and no tricks.

Of course, I did give her the exercise she asked for (exercise 1), but also of course, that didn't end the matter. She decided to add her own flourish, which resulted in exercise 2.

Separate but Possibly Equal

It would be convenient to compare two variables of any type by just using == or !=, the former to tell whether they are equal and the latter whether they are unequal. Unfortunately, this won't work. As we'll see in much more detail later, most types of variables in Java fall in one of two categories: primitive (sometimes called native) and user-defined. Strings are an exception, being sort of a cross between these two types, acting more like the latter. You can indeed compare primitive variables such as ints and chars by using the == or != operators; however, to compare non-primitive variables, including Strings, you have to use the equals facility. In particular, to compare whether two Strings, a and b, have the same value, you have to write if (a.equals(b)).

Does this seem much clumsier than just writing if (a == b), as you would do to compare two ints? Many diehard Java supporters claim that it's much better to have two different ways to compare variables, depending on whether they are actually part of the language (primitive) or are added on afterwards (user-defined and Strings). Personally, I think it would be better to be able to treat all types of variables in the same way, but maybe I'm missing something here.24

How to Do the Exercises

We're ready for the exercises that Susan asked for, along with some others of the same general level of difficulty. To write programs to solve these exercises, see the section titled "Writing and compiling your own programs" in the file "\readme.txt" on the CD in the back of the book.

Once you have followed those instructions to write and run a program, it may work the first time you try them. However, if it doesn't (which is quite likely), you will need more information about what's happening in the program. Fortunately, Java compilers come with a debugger, which you can use to trace execution in your program. To use the debugger for your program, follow the instructions in the section titled "Using the debugger" in the file "\readme.txt" on the CD in the back of the book.

Exercises

  1. Write a program that asks the user to type in the number of people that are expected for dinner, not counting the user. Assuming that the number typed in is n, display a message that says "A table for (n+1) is ready.". For example, if the user types 3, display "A table for 4 is ready.".
  2. Modify the program from exercise 1 to display an error message if the number of guests is more than 20.
  3. Write a program that asks the user to type in his or her name and age. If the age is less than 47, then indicate that the user is a youngster; otherwise, that he or she is getting on in years.
  4. Write a program that asks the user whether Susan is the world's most tenacious novice. If the answer is "yes", then acknowledge the user's correct answer; if the answer is "no", then indicate that the answer is erroneous. If neither "yes" nor "no" is typed in, chastise the user for not following directions.
  5. Write a program that calculates how much extra allowance a teenager can earn by doing extra chores. Her allowance is calculated as $10 if she does no extra chores; she gets $1 additional for each extra chore she does.
  6. Modify the program in Figure basic00 to ask for the grade and display it correctly whether it is an A, B, C, D, or F.

Answers to exercises can be found at the end of the chapter.

Just up the Block

Our most recent programming example has contributed another item to our arsenal of programming weapons; namely, the ability to group several statements into one logical section of a program. That's the function of the curly braces, { and }. The first one of these starts such a section, called a block, and the second one ends the block. Because the two statements after the while are part of the same block, they are treated as a unit; both are executed if the condition in the while is true, and neither is executed if it is false. A block can be used anywhere that a statement can be used, and is treated in exactly the same way as if it were one statement.25

At the Fair

Now we're ready to write a program that vaguely resembles a solution to a real problem. We'll start with a simple, rural type of programming problem.

Imagine that you are at a county fair. The contest for the heaviest pumpkin is about to get underway, and the judges have asked for your help in operating the "pumpkin scoreboard". This device has one slot for the current pumpkin weight (the CurrentWeight slot), and another slot for the highest weight so far (the HighestWeight slot); each slot can hold three digits from 0 to 9 and therefore can indicate any weight from 0 to 999. The judges want you to maintain an up-to-date display of the current weight and of the highest weight seen so far. The weights are expressed to the nearest pound. How would you go about this task?

Probably the best way to start is by setting the number in both slots to the first pumpkin weight called out. Then, as each new weight is called out, you change the number in the CurrentWeight slot to match the current weight; if it's higher than the number in the HighestWeight slot, you change that one to match as well. Of course, you don't have to do anything to the HighestWeight slot when a weight less than the previous maximum is called out, because that pumpkin can't be the winner. How do we know when we are done? Since a pumpkin entered in this contest has to have a weight of at least 1 pound, you enter 0 as the weight when the contest is over. At that point, the number in the HighestWeight slot is the weight of the winner.

The procedure you have just imagined performing can be expressed a bit more precisely by the following algorithm:

  1. Ask for the first weight.
  2. Set the number in the CurrentWeight slot to this value.
  3. Copy the number in the CurrentWeight slot to the HighestWeight slot.
  4. Display both the current weight and the highest weight so far (which are the same, at this point).
  5. While the CurrentWeight value is greater than 0 (that is, there are more pumpkins to be weighed), do steps 5a to 5d.
    1. Ask for the next weight.
    2. Set the number in the CurrentWeight slot to this weight.
    3. If the number in the CurrentWeight slot is greater than the number in the HighestWeight slot, copy the number in the CurrentWeight slot to the HighestWeight slot.
    4. Display the current weight and the highest weight so far.
  6. Stop. The number in the HighestWeight slot is the weight of the winner.

Figure pumpkin is the translation of our little problem into Java. Susan had a question about the formatting of the output statement System.out.println("Highest weight " + HighestWeight);.

Susan: Why do we need both "Highest weight" and HighestWeight in this line?

Steve: Because "Highest weight" is displayed on the screen to tell the user that the following number is supposed to represent the highest weight seen so far. On the other hand, HighestWeight is the name of the variable that holds that information, so including HighestWeight in the output statement will result in displaying the highest weight we've seen so far on the screen. Of course, the same analysis applies to the next line, which displays the label "Current weight" and the value of the variable CurrentWeight.

  English							Java

-------------------------------------------------------------------------------------------------------------------

First, we have to tell the compiler what we're up to in this program.

-------------------------------------------------------------------------------------------------------------------

Tell the compiler |

where to find the |

RWVar input and |

output code | import WAJ.*;

|

We're defining | public class Pump1

a class named Pump1 |

|

This is the main | {

part of the program | public static void main( String args[ ] )

|

Start of program | {

|

Define variables | int CurrentWeight;

| int HighestWeight;

|

-------------------------------------------------------------------------------------------------------------------

Here's the start of the "working" code:

-------------------------------------------------------------------------------------------------------------------

|

Ask for the first |

weight | System.out.print("Please enter the first weight: ");

|

Set the number in |

the CurrentWeight |

slot to the value |

entered by the user | CurrentWeight = RWVar.readInt(System.in);

|

Copy the number in |

the CurrentWeight |

slot to the |

HighestWeight slot | HighestWeight = CurrentWeight;

|

Display the current | System.out.println("Current weight " + CurrentWeight);

and highest weights | System.out.println("Highest weight " + HighestWeight);

|

A Java Program (code\Pump1\Pump1.java) (Figure pumpkin)

While the number in |
the CurrentWeight |
slot is greater |
than 0 (i.e., there |
are more pumpkins |
to be weighed) | while (CurrentWeight > 0)
|
Start repeated | {
steps |
|
Ask for the next |
weight | System.out.print("Please enter the next weight: ");
|
Set the number in |
the CurrentWeight |
slot to this value | CurrentWeight = RWVar.readInt(System.in);
|
If the number in |
the CurrentWeight |
slot is more than |
the number in the |
HighestWeight slot, | if (CurrentWeight > HighestWeight)
|
then copy the |
number in the |
CurrentWeight slot |
to the |
HighestWeight slot | HighestWeight = CurrentWeight;
|
Display the current | System.out.println("Current weight "+CurrentWeight);
and highest weights | System.out.println("Highest weight "+HighestWeight);
|
End repeated steps |
in while loop | }
|
-------------------------------------------------------------------------------------------------------------------
We've finished the job; now to clean up
-------------------------------------------------------------------------------------------------------------------
|
End of main part of |
program | }
|
End of class | }
|
-------------------------------------------------------------------------------------------------------------------
Figure
pumpkin continued

Susan had some questions about variable names.

Susan: Tell me again what the different ints mean in this figure. I am confused; I just thought an int held a variable like i. What is going on when you declare HighestWeight an int? So do the "words" HighestWeight work in the same way as i?

Steve: An int is a variable. The name of an int is made up of one or more characters; the first character must be a letter or an underscore (_), whereas any character after the first must be either a letter, an underscore, a dollar sign, or a digit from 0 to 9. To define an int, you write a line that gives the name of the int. This is an example: int HighestWeight;.

Susan: OK, but then how does i take 4 bytes of memory and how does HighestWeight take up 4 bytes of memory? They look so different, how do you know that HighestWeight will fit into an int?

Steve: The length of the names that you give variables has nothing to do with the amount of storage that the variables take up. After the compiler gets through with your program, there aren't any variable names; each variable that you define in your source program is represented by the address of some area of storage. If the variable is an int, that area of storage is 4 bytes long; if it's a char (or a short), the area of storage is 2 bytes long.

Susan: Then where do the names go? They don't go "into" the int?

Steve: A variable name doesn't "go" anywhere; it tells the compiler to set aside an area of memory of a particular length that you will refer to by a given name. If you write int xyz; you're telling the compiler that you are going to use an int (that is, 4 bytes of memory) called xyz.

Susan: If that is the case, then why bother defining the int at all?

Steve: So that you (the programmer) can use a name that makes sense to you. If the compiler had to assign names itself, it wouldn't be very likely to give variables names that you would like!

The topic of the import statement was the cause of some discussion with Susan. Here's the play by play:

Susan: Is import a command?

Steve: Right; it's a command to the compiler.

Susan: Then what are the words we have been using for the most part called? Are those just called code or just statements? Can you make a list of commands to review?

Steve: The words that are defined in the language, such as if, while, for, and the like, are called keywords. User-defined names such as variable names are called identifiers.

Susan: So import WAJ.* is a code to tell the compiler that it is using info from the WAJ library?

Steve: Essentially correct; to be more precise, when we import WAJ.*, we're telling the compiler to look into the WAJ directory for definitions that we're going to use.

Susan: Then that WAJ file contains the secondary code of byte codes to transform RWVar.readInt and RWVar.readString into something workable?

Steve: Actually, it's in the WAJ directory, not the WAJ file, but you're on the right track.

Susan: So the import statement file directs the compiler to that section in the library where that byte code is stored? In other words, it is like telling the compiler to look in section XXX to find the byte code?

Steve: Right.

Finally, the closing curly brace, }, tells the compiler that it can stop compiling the current block, which in this case is the one called main. Without this marker, the compiler would tell us that we have a missing }, which of course would be true.

Novice Alert

Susan decided a little later in our collaboration that she wanted to try to reproduce this program just by considering the English description, without looking at my solution. She didn't quite make it without peeking, but the results are illuminating nevertheless.

Susan: What I did was to cover your code with a sheet of paper and just tried to get the next line without looking, and then if I was totally stumped, I would look. Anyway, when I saw that if statement, then I knew what the next statement would be but I am still having problems with writing backwards. For example:

if (CurrentWeight > HighestWeight)

HighestWeight = CurrentWeight;

That is so confusing because we just want to say that if the current weight is higher than the highest weight, then the current weight will be the new highest weight, so I want to write CurrentWeight = HighestWeight. Anyway, when I really think about it, I know it makes sense to do it the right way; I'm just having a hard time thinking like that. Any suggestions on how to think backward?

Steve: What that statement means is "set HighestWeight to the current value of CurrentWeight". The point here is that = does not mean "is equal to"; it means "set the variable to the left of the = to the value of the expression to the right of the =".

Now it's time for some review on what we've covered in this chapter.

Review

We started out by discussing the tremendous reliability of computers; whenever you hear "it's the computer's fault", the overwhelming likelihood is that the software is to blame rather than the hardware. Then we took a look at the fact that, although computers are calculating engines, many of the functions for which we use them don't have much to do with numeric calculations; for example, the most common use of computers is probably word processing, which doesn't use much in the way of addition or subtraction. Nevertheless, we started out our investigation of programming with numeric variables, which are easier to understand than non-numeric ones. To use variables, we need to write a Java program, which consists primarily of a list of operations to be performed by the computer, along with directions that influence how these operations are to be translated into byte codes.

That led us into a discussion of why and how our Java program is translated into byte codes by a compiler. We examined an example program that contained simple source code statements, including some that define variables and others that use those variables and constants to calculate results. We covered the symbols that are used to represent the operations of addition, subtraction, multiplication, division, and assignment, which are +, -, *, /, and =, respectively. Whereas the first four of these should be familiar to you, the last one is a programming notion rather than a mathematical one. This may be confusing because the operation of assignment is expressed by the = sign, but is not the same as mathematical equality. For example, the statement x = 3; does not mean "x is equal to 3", but rather "set the variable x to the value 3". Then we spent some time pretending to be a compiler, to see how a simple Java program looks from that point of view, in order to improve our understanding of what the compiler does with our programs. This exercise involved keeping track of the locations of variables and instructions, and watching the effect of the instructions on the stack and variables. During this exploration of the machine, we got acquainted with the byte-code representation of instructions, which is the actual form of a program that the Java interpreter can understand. After a detailed examination of what the compiler does with our source code at compile time, we followed what would happen at run time (that is, if the sample program were actually executed by the Java interpreter).

Then we began to look at two data types that can hold non-numeric data, namely, the char and the String. The char occupies to 2 bytes of storage, corresponding to one character of data. Examples of appropriate values for a char variable include letters (a-z, A-Z), digits (0-9), and special characters (e.g., ! @ # $ %), as well as "nonprintable" characters such as the "space", which causes output to move to the next character position on the screen.

One char isn't much information, so we often want to deal with groups of them as a single unit; an example would be a person's name. This is the province of the String variable type: Variables of this type can handle an indefinitely long group of chars.

At the beginning of our sample program for Strings and chars, we encountered the line public static void main(String args[ ]), which indicates where we want to start executing our program. A Java program always starts execution at the place indicated by such a line.

As we continued looking at the sample program for Strings and chars, we saw how to assign literal values to both of these types, and noticed that two different types of quotes are used to mark off the literal values: the single quote ('), which is used in pairs to surround a literal char value consisting of exactly one char, such as 'a'; and the double quote ("), which is used in pairs to surround a literal String value such as "This is a test".

This led us to the discussion of the way in which the compiler regulates our access to variables by their type, which is defined at compile time. This is called the type system; Java uses this static type checking to help make Java programs more robust than programs written in languages that use dynamic type checking, where these errors are not detected until run time.

After a short discussion of some of the special characters that have a predefined meaning to the compiler, we took an initial glance at the mechanisms that allow us to get information into and out of the computer, known as I/O. We looked at the println function, which provides display on the screen when coupled with the built-in destination called System.out. Immediately afterwards, we encountered the input function RWVar.inputString and its partner System.in, which team up to give us input from the keyboard.

Next, we went over some program organization concepts, including the if statement, which allows the program to choose between two alternatives; the while statement, which causes another statement to be executed while some condition is true; and the block, which allows several statements to be grouped together into one logical statement. Blocks are commonly used to enable several statements to be controlled by an if or while statement.

At last we were ready to write a simple program that does something resembling useful work, and we did just that. The starting point for this program, as with all programs, was to define exactly what the program should do; in this case, the task was to keep track of the pumpkin with the highest weight at a county fair. The next step was to define a solution to this problem in precise terms. Then we broke the solution down into steps small enough to be translated directly into Java. Of course, the next step after that was to do that translation. Finally, we went over the Java code, line by line, to see what each line of the program did.

Now that the review is out of the way, we're about ready to continue with some more Java in Chapter morebas.htm. First, though, let's step back a bit and see where we are right now.

Conclusion

We've come a long way from the beginning of this chapter. Starting from basic information on how the hardware works, we've made it through our first actual, runnable program. By now, you should have a much better idea whether you're going to enjoy programming (and this book). Assuming you aren't discouraged on either of these points, let's proceed to gather some more tools, so we can undertake a bigger project.

Answers to Exercises

  1. Susan's answer to this problem follows, after a short discussion about formatting the output of this program to make it look better. While we're on the topic of formatting, the reason that this program uses two lines to produce the sentence "Please type in the number of guests of your dinner party." is so that the program listing will fit on the page properly. If you prefer, you can combine those into one line that says System.out.print("Please type in the number of guests of your dinner party. ");. Of course, this also applies to the next exercise.
  2. Here's that conversation about formatting the output of this program to make it look better:
Steve: By the way, you might want to add a " " in front of the is in is ready, so that the number doesn't run up against the is. That would make the line look like this: System.out.println("A table for " + n + " is ready. ");

Susan: Okay.

And Figure first.dinner, as promised, is Susan's answer to the first dinner party exercise.

code/basic05/basic0~1.jav

  

First dinner party program (code\Basic05\Basic05.java) (Figure first.dinner)

To use the debugger for this program, follow the instructions in the section titled "Using the debugger" in the file "\readme.txt" on the CD in the back of the book. These instructions assume that you've installed the examples on drive C:, so that the location of this program is "c:\whosj\code\Basic05".

  1. Susan didn't have too much trouble with this one, but did have some questions on how to use else. First, here's our discussion.

Steve: Congratulations on getting your program to work!

Susan: Now, let me ask you this: Can you ever modify else? That is, could I have written else (n>20), or does else always stand alone?

Steve: You can make the controlled block of an if statement or an else statement another if or else. In fact, you can have as many "nested" if or else statements as you wish; however, it's best to avoid very deep nesting because it tends to confuse the next programmer who has to read the program.

Figure else.if is an example of an else whose controlled block is an if statement.

if (x < y)

{

System.out.println("x is less than y");

else

{

if (x > y)

System.out.println("x is greater than y");

else

System.out.println("x must be equal to y!");

}

}

else if example (Figure else.if)

As promised, Figure second.dinner is Susan's answer to exercise 2.

code/basic06/basic0~1.jav

  

Second dinner party program (code\Basic06\Basic06.java) (Figure second.dinner)

To use the debugger for this program, follow the instructions in the section titled "Using the debugger" in the file "\readme.txt" on the CD in the back of the book. These instructions assume that you've installed the examples on drive C:, so that the location of this program is "c:\whosj\code\Basic06".

  1. The program should look like Figure name.age.

code/basic07/basic0~1.jav

  

Name and age program (code\Basic07\Basic07.java) (Figure name.age)

One point that might be a bit puzzling in this program is why it's not necessary to use println in the lines that send data to System.out before we ask the user for input. For example, in the sequence

System.out.print("What is your age? ");

age = RWVar.readInt(System.in);

how do we know that the literal String "What is your name? " has been displayed on the terminal before the user has to type in the answer? Obviously, it would be hard for the user to answer our request for information without a clue as to what we're asking for.

As it happens, this is handled by the RWVar.readInt facility. When we use that facility to do output to the screen and input from the keyboard, we can be sure that any screen output we have already requested will be displayed before any input is requested from the user via the keyboard.

  1. Figure novice shows Susan's program, which is followed by our discussion.

code/basic08/basic0~1.jav

  

Novice program (code\Basic08\Basic08.java) (Figure novice)

Susan: Steve, look at this. It even runs!

Also, I wanted to ask you one more question about this program. I wanted to put double quotes around the words yes and no in the third output statement because I wanted to emphasize those words, but I didn't know if the compiler could deal with that so I left it out. Would that have worked if I had?

Steve: Not if you just added quotes, because " is a special character that means "beginning or end of literal String". Here's what you would have to do to make it work:

System.out.println("Please answer with either \"yes\" or \"no\".");

The \ is a way of telling the compiler to treat the next character differently from its normal usage. In this case, we are telling the compiler to treat the special character " as "not special"; that is, \" means "just the character double quote, please, and no nonsense". This is called an escape, because it allows you to get out of the trap of having a " mean something special. We also use the \ to tell the compiler to treat a "non-special" character as "special"; for example, we use it to make up special characters that don't have any visual representation. An example is '\n', the "newline" character, which means "start a new line on the screen".

Susan: So if we want to write some character that means something "special", then we have to use a \ in front of it to tell the compiler to treat it like a "regular" character?

Steve: Right.

Susan: And if we want to write some character that is "regular" and make it do something "special", then we have to use a \ in front of it to tell the compiler that it means something "special"? That's weird.

Steve: It may be weird, but that's the way it works.

Susan: I just now got it. I was going to say, why would you put the first quotation mark before the slash, but now I see. Since you are doing a newline character, you have to have quotes on both sides to surround it which you don't usually have to do because the first quotes are usually started at the beginning of the sentence, and in this case the quote was already ended. OK, thanks for clearing that up.

Steve: You've got it.

Susan: Another thing I forgot is how you refer to the statements in () next to the if keywords; what do you call the info that is in there?

Steve: The condition.

To use the debugger for this program, follow the instructions in the section titled "Using the debugger" in the file "\readme.txt" on the CD in the back of the book. These instructions assume that you've installed the examples on drive C:, so that the location of this program is "c:\whosj\code\Basic08".

  1. Figure allowance is Susan's version of this program.

code/basic09/basic0~1.jav

  

Allowance program (code\Basic09\Basic09.java) (Figure allowance)

To use the debugger for this program, follow the instructions in the section titled "Using the debugger" in the file "\readme.txt" on the CD in the back of the book. These instructions assume that you've installed the examples on drive C:, so that the location of this program is "c:\whosj\code\Basic09".

  1. Figure basic10 is an answer to this problem.

code/basic10/basic1~1.jav

  

Grading program (code\Basic10\Basic10.java) (Figure basic10)

To use the debugger for this program, follow the instructions in the section titled "Using the debugger" in the file "\readme.txt" on the CD in the back of the book. These instructions assume that you've installed the examples on drive C:, so that the location of this program is "c:\whosj\code\Basic10".

Footnotes

  1. Please note that capitalization counts in Java, so IF and WHILE are not the same as if and while. You have to use the latter versions.
  2. However, we haven't yet eliminated the possibility of hardware errors, as the floating-point flaw in early versions of the PentiumTM processor illustrates. In rare cases, the result of the divide instruction in those processors was accurate to only about 5 decimal places rather than the normal 16 to 17 decimal places.
  3. This was apparently against the plan administrator's principles.
  4. Oxford English Dictionary, first current definition (4).
  5. The compiler also does a lot of other work for us, which we'll get into later.
  6. The // marks the beginning of a comment, which is a note to you or another programmer; it is ignored by the compiler. For those of you with BASIC experience, this is just like REM (the "remark" keyword in that language); anything after it on a line is ignored.
  7. By the way, blank lines are ignored by the compiler; in fact, you can even run all the statements together on one line if you want to. That won't confuse the compiler. but it will make it much harder for someone reading your code later to understand what you're trying to do. Programs aren't written just for the compiler but also for other people; therefore, it is important to write them so that they can be understood by those other people. One very good reason for this is that more often than you might think, those "other people" turn out to be you, six months later.
  8. Other kinds of variables can hold different ranges of values; we'll go over them in some detail in future chapters.
  9. At the risk of boring experienced programmers, let me reiterate that = does not mean "is equal to"; it means "set the variable to the left of the = to the value of the expression to the right of the =." In fact, there is no equivalent in Java to the mathematical notion of equality. We have only the assignment operator = and the comparison operator ==, which we will encounter later in this chapter. The latter is used in if statements to determine whether two expressions have the same value. All of the valid comparison operators are listed in Figure
  10. comparisonfig.
  11. If you have any programming experience whatever, you may think that I'm spending too much effort on this very simple point. I can report from personal experience that it's not necessarily easy for a complete novice to grasp. Furthermore, without a solid understanding of the difference between an algebraic equality and an assignment statement, that novice will be unable to understand how to write a program.
  12. As I've mentioned previously, blank lines are ignored by the compiler; you can put them in freely to improve readability.
  13. Please note that the address that the stack occupies in the following diagrams is arbitrary. The actual address where the stack is located in your program is determined by the interpreter in combination with the operating system.
  14. The next byte code to be executed will be bold.
  15. In case you were wondering, the most common pronunciation of char has an a like the a in "married", while the ch sounds like "k".
  16. As we will see shortly, not all characters have visible representations; some of these "nonprintable" characters are useful in controlling how our printed or displayed information looks.
  17. The Unicode standard is actually a "small" version of a standard that uses 32 bits per character, for the day when Unicode doesn't have sufficient capacity; that should take care of any languages that alien civilizations might introduce to our planet.
  18. Please note that there is a space (blank) character at the end of that String literal, after the exclamation point (!). That space is part of the literal value.
  19. I know I left out the part about its curing cancer, but I don't want to go overboard.
  20. I'll be more specific later, when we have seen some examples.
  21. For example, if you wanted to insert a " in a String, you would have to use \", because just a plain " would indicate the end of the String. That is, if you were to set a String to the literal "This is a \"String\".", it would display as: This is a "String".

  22. Please note that you cannot include the "," in a number in your programs, whether you're writing the program or entering data when it runs. You have to type "10,000" as "10000" or you'll get an error either at compile time or when the program is running.
  23. You may be wondering why we need parentheses around the expression Guess != Secret. The conditional expression has to be in parentheses so that the compiler can tell where it ends and the statement to be controlled by the while begins.
  24. And maybe pigs can fly.
  25. If you look at someone else's Java program, you're likely to see a different style for lining up the {} to indicate where a block begins and ends. As you'll notice, my style puts the { and } on separate lines rather than running them together with the code they enclose, to make them stand out, and indents them further than the conditional statement. I find this the clearest, but this is a matter where there is no consensus. The compiler doesn't care how you indent your code or whether you do so at all; it's a stylistic issue.


Return to the table of contents


Return to my main page