Table of Contents

Copyright © 2005 by Stuart Reges and Marty Stepp

Chapter 1: Introduction to Programming and Simple Java Programs

Chapter 2: Primitive Data and Definite Loops

Chapter 3: Introduction to Parameters and Objects

Chapter 4: Interactive Programs and Conditional Execution

Chapter 5: Program Logic and Indefinite Loops

Chapter 6: File Processing


Chapter 7: Arrays

Chapter 8: Defining Classes

Chapter 9: Inheritance, Polymorphism, Interfaces, and Object-Oriented Design

Chapter 10: Graphical User Interfaces


Chapter 1
Introduction to Programming and Simple Java Programs

Copyright © 2005 by Stuart Reges and Marty Stepp

1.1 Introduction

This chapter introduces some basic terminology and discusses Java's status as a programming language. The chapter closes with a brief look at Java, examining how to write simple but structured programs that produce output.

1.2 A Brief History of Programming Languages

The first major programming languages were developed in the 1950's and ever since then programmers and computer scientists have fought over which language is best, a tradition referred to as "the great language wars." People become very passionate about what they see as the good aspects or bad aspects of particular programming languages.

Going back to the 1960's we find four programming languages that had great influence and that are still in use today:

Each of these languages continued into the 1970's, but other languages emerged to meet other needs. The C programming language was developed for people writing systems software like operating systems and device drivers. The Pascal language was developed primarily as a teaching language that emphasized a philosophy known as structured programming.

As we moved into the 1980's it became increasingly clear that a programming approach known as object oriented programming would be very important. The first major object oriented language was known as Smalltalk but it never gained popularity in industry. Many people attempted to extend or improve Pascal into this direction with languages like Modula 2, Modula 3, Oberon, Object Pascal and Delphi, but these languages also never quite took off.

The major success came from a computer scientist named Bjarne Strousstrup who decided to leverage the popularity of C by adding object oriented features and calling the superset language C++. C++ has been a major success in industry and continues to be a significant development language. C++ has also been a popular choice for educators.

As we moved into the 1990's the internet and the world wide web emerged as new priorities. A researcher at Sun Microsystems named Jim Gosling was working on a language for what are known as embedded systems (systems operating on stand-alone devices like cell phones) but Sun realized that his language would also be well-suited to web applications. The rest is history. Java became the language of choice for developing programs known as applets that could be attached to web pages. Since then Java has expanded to become an important language both in industry and in education. Although C++ came close, Java is the first language to achieve the nearly universal appeal that Pascal had in the 1980's as the language of choice for introductory computer science at colleges and universities.

While all of this has been going on a small but dedicated minority of computer scientists has been exploring ways to extend the ideas from the Lisp programming language, a style of programming known as functional programming. All sorts of functional programming languages have been developed over the years including ML, Haskell and Scheme. A small number of schools, inspired by the curriculum at MIT, developed introductory computer science classes in Scheme. The advocates of this approach point out that the simplified syntax of Scheme has allowed them to spend much more time on core computer science concepts rather than fretting over trivial language details, but the approach has never gained widespread popularity.

Another parallel thread that has developed over the years has been the emergence of scripting languages like Perl, JavaScript and PHP. These languages have their origins in much older text processing languages like Snobol and Icon that date back to the 1960's but they have become increasingly popular ever since the creation of the web because they help solve the problem of managing interactive web pages. Newer languages like Python, Ruby and OCaml attempt to fill this niche with languages that are general-purpose enough to be used for many other applications as well.

A close cousin to Java was invented in the late 1990's by Microsoft Corporation. They set about creating a modern language that would combine the best aspects of C++ and Java. The resulting language is called C# and looks a lot like Java. Since its release Sun has modified Java to give it some of the features of C#. Thus, these two languages are now competing with each other, which is likely to keep them fairly similar.


1.3 Some Basic Terminology

You tell the computer what to do by writing a program.

Program

A set of instructions that are to be carried out by a computer.

The word "code" describes program fragments ("these four lines of code") or the act of programming ("Let's code this into Java"). Once a program has been written, you can execute it.

Program Execution

The act of carrying out the instructions contained in a program.

The process of execution is often called running. It can be used as a verb, "When my program runs it does something strange. . .," or as a noun, "The last run of my program produced these results. . ." Programs collectively are known as "software." The physical devices they run on are collectively known as "hardware."

At the most basic level, computer programs are expressed as a series of numbers known as the machine language of the computer. In the early days programmers entered numbers like these directly into the computer. Obviously this is a tedious and confusing way to program a computer and we have invented all sorts of mechanisms to simplify this process.

Modern programmers write in what are known as high-level programming languages. All of the languages mentioned in the previous section are high-level languages. These programs cannot be run directly on a computer. They first have to be translated into a different form by a special program known as a compiler.

Compiler

A program that translates a computer program written in one language into an equivalent program in another language (often, but not always, translating into machine language).

A compiler that translates directly into machine language creates a program that can be executed directly on the computer. We refer to such a program as an executable and we refer to such compilers as "native compilers" because they compile code to the lowest possible level (the native machine language of the computer). Most C and C++ compilers are native compilers that create executables.

This approach works well when you know exactly what computer you want your program to execute on. But what if you want to execute a program on many different computers? Using this approach you'd need a compiler that generates different machine language output for each different computer. The designers of Java decided to use a different approach. They cared a lot about being able to run on many different computers because they wanted to have a language that worked well for the web. People who write applets want those programs to run on many different computers. So instead of compiling into machine language, Java programs are compiled into what are known as Java bytecodes. These bytecodes represent an intermediate level. They aren't quite as high-level as Java but they also aren't quite as low-level as machine language. The key thing is that one set of bytecodes can execute on many different machines.

Java bytecodes are similar to machine language. In fact, they are the machine language of a theoretical computer known as the Java Virtual Machine or JVM.

Java Virtual Machine (JVM)

A theoretical computer whose machine language is the set of Java bytecodes.

This isn't an actual machine but it's similar to actual machines. By compiling down to this level, there isn't much work left to turn the Java bytecodes into actual machine instructions.

In the Java programming language, nothing can exist outside of what is called a class.

Class

A unit of code that is the basic building block of Java programs.

The notion of a "class" is much richer than this as we'll see when we get to chapter 8, but for now all we need to know is that each of our Java programs will be stored in a class.

The diagram below summarizes the process of compilation. A Java program is given to the Java compiler to produce a compiled class file.

       input           given to                 produces
    +---------+      +----------+      +------------------------+
    |  Java   |      |   Java   |      |     compiled Java      |
    | program | ---> | compiler | ---> | class file (bytecodes) |
    +---------+      +----------+      +------------------------+
To actually execute a Java class file, you need another program that will execute the Java bytecodes. Such programs are known generically as "Java runtimes" and the standard environment distributed by Sun is known as the Java Runtime Environment.

Java Runtime Environment (JRE)

A program that executes compiled Java class files.

Most people have a Java runtime on their computer even if they don't know about it. For example, Apple's OS X includes a Java runtime and the standard Windows installer from Microsoft installs a Java runtime.

1.4 The Programming Environment

You must become familiar with your computer setup before you start programming. Each computer provides a different environment for program development, but there are some common elements that deserve comment.

The basic unit of storage on most computers is a file. You will create files whose contents are Java programs. The standard convention is to use the extension ".java" for these files. When you compile a Java program, the resulting Java bytecodes are stored in a file with the same name and the extension ".class".

Most Java programmers use what are known as Integrated Development Environments or IDEs that provide an all-in-one environment for creating, editing, compiling and executing program files. Some of the more popular choices for introductory computer science classes are eclipse, DrJava, BlueJ and TextPad. Your instructor will tell you what environment you should be using.

No matter what environment you are using, you will follow the same basic three steps:

  1. type in a program as a Java class
  2. compile the program file
  3. run the compiled version of the program
For example, you might type in the program file below. Don't worry about the details of this program right now. We will explore that in the next section.

        public class Hello {
            public static void main(String[] args) {
                System.out.println("Hello, world!");
            }
        }
As the first line indicates, this is a public class called Hello. Java requires that the class name and the file name match. So because this class is called Hello, it must be stored in a file called Hello.java.

Once you have typed in a program file, you move to step 2 and compile it. The command to compile will be different in each different development environment, but the process is the same. You have to submit your class file to the compiler for translation (typical commands are "compile" or "build"). There might be errors, in which case you'd have to go back to the editor and fix the errors and try to compile again.

Once you have successfully compiled your program, you are ready to move to step 3 by running the program. Again, the command to do this will differ from one environment to the next, but the process is similar (typical commands are "run" or "go").

This particular program involves what is known as the "console window" or "console".

Console Window

A special text-only window in which Java programs interact with the user.

The console window is a classic way to interact with computers where the computer types text on the screen and sometimes waits for the user to type responses. This is known as console or terminal interaction. The text typed by the computer in the console window is known as the output of the program. Anything typed by the user is known as the console input.

Most of the sample programs in this book involve console interaction. It might be old-fashioned, but it is simple. Keeping the interaction simple will allow us to focus our attention and effort on other aspects of programming. For those who are interested, chapter 10 describes how to write programs that use a more modern kind of interface known as a Graphical User Interface or GUI.

1.5 And Now--Java

It's time to look at a complete Java program. Brian Kernighan and Dennis Ritchie started a tradition in computer science that when you describe a new programming language, you should start with a program that produces a single line of output with the words "Hello world." They included this in a 1978 book describing what was then a fairly new programming language they had created called C. Kernighan and Ritchie and their book "The C Programming Language" have both been affectionately referred to as "K&R" ever since.

The hello world tradition has been broken by many authors of Java books because the program turns out not to be short and simple when written in Java.

        public class Hello {
            public static void main(String[] args) {
                System.out.println("Hello, world!");
            }
        }
This defines a class called Hello. Sun has established the convention that class names always begin with a capital letter, which makes it easy to recognize that something is the name of a class. Recall that Java requires that the class name and the file name match, so this would have to be stored in a file called Hello.java. Fortunately, you don't have to understand all of the details of this program just yet. But you do need to understand the basic structure.

Remember that the basic unit of code in Java is the class. Every program must be contained within a Java class. The basic form of a Java class is as follows.

public class <name> { <program code> } The description above is known as a "syntax template" because it describes the basic form of a Java construct. Each time we see a new element of Java, we'll begin by looking at its syntax template. By convention, we use the characters less-than ("<") and greater-than (">") in a syntax template to indicate items that need to be filled in. In this case the name of the class and the program code both need to be filled in.

The first line of the class is known as the class header. The keyword "public" in the header indicates that this class is available to anyone to use. Notice that the program code in a class is enclosed in curly-brace characters. The curly-brace characters are used in Java to group together related bits of code.

Grouping Characters

The curly brace characters ("{" and "}"), used in Java to group together related lines of code.

In this case, the curly braces are indicating that "everything defined inside these braces is part of this public class."

So what exactly can appear inside the curly braces? What can be contained in a class? All sorts of things, but for now, we'll limit ourselves to what are known as methods. Methods are the next smallest unit of code in Java and they represent a single action or calculation to be performed.

Method

A program unit that represents a particular action or computation.

Simple methods are like verbs. They command the computer to perform some action. Inside the curly braces for a class, you can define several different methods. At a minimum, a complete program requires a special method that is known as the "main" method. It has the following syntax:

public static void main(String[] args) { <statements> } Just as the first line of a class is known as a class header, the first line of a method is known as a method header. The header for main is rather complication. Most people memorize this as a kind of magical incantation. You want to open the door to Ali Baba's cave? You say, "Open sesame." You want to create an executable Java program? You say, "public static void main(String[] args)." A group of Java teachers makes fun of this with a web site called www.publicstaticvoidmain.com. Memorizing magical incantations is never satisfying, especially for computer scientists who like to know everything that is going on in their programs, but this is a place where Java shows its ugly side and we just have to live with it. Fortunately, by the time you finish this book, you'll understand every part of this incantation.

Notice that the main method has a set of curly braces of its own. They are again used for grouping, saying that everything that appears between the braces is part of the main method. The lines in between the curly braces specify the series of actions to perform in executing the program. We refer to these as the "statements" of the program. Just as you put together an essay by stringing together complete sentences, you put together a method by stringing together statements.

Statement

An executable snippet of code that represents a complete command.

The sample "hello world" program has just a single statement that is known as a "println" statement:

    System.out.println("Hello, world!");
Notice that this statement ends with a semicolon. The semicolon has a special status in Java. It is used to terminate statements in the same way that periods are used to terminate sentences in English.

Statement Terminator

The semicolon character (";"), used in Java to termminate statements.

In the "hello world" program there is just a single command to produce a line of output, but consider the following variation called Hello3 that has three lines of code to be executed in the main method.

        public class Hello3 {
            public static void main(String[] args) {
                System.out.println("Hello, world!");
                System.out.println();
                System.out.println("This program produces three lines of output.");
            }
        }
Notice that there are three semicolons in the main method, one at the end of each of the three println statements. The statements are executed in the order in which they appear from first to last, so the program above produces the following output.

        Hello, world!
        
        This program produces three lines of output.
Let's summarize the different levels we just looked at: It may seem odd to put the opening curly brace at the end of a line rather than on a line by itself. Some people would use this style of indentation for the program instead:

        public class Hello2
        {
            public static void main(String[] args)
            {
                System.out.println("Hello, world!");
            }
        }
Different people will make different choices about the placement of curly braces. The style I use is known as K&R style, named after the same Kernighan and Ritchie who started the hello world tradition. Sun Microsystems also chose K&R style for their official Java coding conventions, so all Java code you see from Sun will be formatted in this way. But the other style has its advocates. Often people will passionately argue that one way is much better than the other, but really these are matters of personal taste because each choice has some advantages and some disadvantages. You should choose a style that you are comfortable with and then use it consistently.

Now that we have seen an overview of the structure, let's examine some of the details of Java programs in greater detail.

1.5.1 String Literals (Strings)

Often in writing Java programs we want to include some literal text that we want to output. For example, in the "hello world" program, we had a particular phrase that we wanted the program to send to the console window as output. Programmers have traditionally referred to such text as a "string" because it is composed of a sequence of characters that we string together. The Java language specification refers to these as "string literals", although programmers often simply refer to them as "strings".

In Java you specify a string literal by surrounding the literal text in quotation marks, as in:

    "This is a bunch of text surrounded by quotation marks."
You use double quotation marks, not single quotation marks. The following is not a string literal.

    'Bad stuff here.'
But the following is a string literal.

    "This is a quote even with 'these' quotes inside."
String literals must not span more than one line of a program. The following is not a string literal.

    "This is really
     bad stuff
     right here."

1.5.2 System.out.println

As you have seen, the main method of a Java program contains a series of statements for the computer to carry out. They are executed sequentially starting with the first statement, then the second, then the third and so on until the final statement has been executed. One of the simplest and most common statements is to make a call on System.out.println to produce a line of output. This is another magical incantation that people tend to just memorize as if it were one word. Google lists over 180 thousand web pages that mention System.out.println. The key thing to know about System.out.println is that we use it to produce a line of output that is sent to the console window.

The simplest form of the println has nothing inside parentheses and produces a blank line of output:

    System.out.println();
You need to include the parentheses even if you don't have anything to put inside them. Notice the semicolon at the end of the line. All statements in Java must be terminated with a semicolon.

More often you use a call on println to output a line of text:

    System.out.println("This is a call on the println method.");
This statement commands the computer to produce the following line of output.

    This is a call on the println method.
Each println statement produces a different line of output. These three statements

    System.out.println("This is the first line of output.");
    System.out.println();
    System.out.println("And this is the third, below a blank line.");
will produce these three lines of output:

    This is the first line of output.

    And this is the third, below a blank line.

1.5.3 Escape Sequences

Any system that involves quoting text will lead you to certain difficult situations. For example, string literals are contained inside of quotation marks, so how could you include a quotation mark inside a string literal? String literals also aren't allowed to break across lines, so how would you include a line break inside a string literal?

The solution is that you can embed what are known as "escape sequences" in a string literal. These are two-character sequences that are used to represent special characters. They all begin with the backslash character ("\"). The table below lists some of the more common escape sequences.

Sequence Represents
\ttab character
\nnewline character
\"quotation mark
\\backslash character
\fformfeed (go to top of new page)

Keep in mind that each of these two character sequences actually stands for just a single character. For example, if you were to execute the following statement:

    System.out.println("How many \"characters\" does this \\ produce?");
You would get the following output:

    How many "characters" does this \ produce?
The string literal in the println has three escape sequences that are each two characters long, but they each produce a single character of output.

Even though you can't have string literals that span multiple lines, you can use the "\n" escape sequence to embed newline characters in a string. This leads to the odd situation where a single println can produce more than one line of output.

For example, if you execute this statement:

    System.out.println("This one line\nproduces 3 lines\nof output.");
You will get the following output:
    This one line
    produces 3 lines
    of output.
The println itself produces one line of output and the string literal contains two newline characters in the middle that cause the line of output to be broken up into a total of three lines of output.

This is another programming habit that tends to vary according to taste. Some people (including me) find it hard to read string literals that have "\n" in them, but other people prefer to write fewer lines of code. Once again, you should make up your own mind about when to use the newline escape sequence.

1.5.4 Identifiers and Keywords

The words used in a Java program are called identifiers. Identifiers must start with a letter and can then be followed by any number of letters and digits. The following are legal identifiers.

    first          hiThere        numStudents    twoBy4
The following are illegal identifiers.

    two+two        hi there       hi-There       2by4
The Java language specification defines the set of letters to include the underscore and dollar-sign characters ("_" and "$"), which means that the following are legal identifiers as well.

    two_plus_two    _count      $2donuts        MAX_COUNT
Java has conventions for capitalization that are followed fairly consistently in the Java class libraries. All class names should begin with a capital letter, as with the Hello, Hello2 and Hello3 classes in the previous section. The names of methods and variables should begin with lowercase letters, as in method main. Constant values appear in all uppercase letters, as in the constant PI that is defined in the Math class. When putting several words together, we capitalize the first letter of each word. This technique does not work for constants, however, because all letters are supposed to be uppercase. In the case of constants we use underscore characters to separate words.

For example, suppose that you were going to put together the words "all my children" into an identifier. Depending upon what the identifier is used for, you'd turn this into:

Java is case sensitive, so the identifiers "class", "Class", "CLASS" and "cLASs" are all considered different words. Keep this in mind as you read error messages from the compiler. People are good at understanding what you are saying even if you make little mistakes like changing the capitalization of a word. The Java compiler becomes hopelessly confused when you misspell even a single word.

Don't hesitate to use long identifiers. The more descriptive your names, the easier it will be for people (including yourself) to read your programs. Long identifiers are worth the time they take to type. Java's String class, for example, has a method called compareToIgnoreCase and Java's JButton class includes constants with names like HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY.

Java has a set of predefined identifiers called keywords that are reserved for a particular use. As you read this book, you will learn many of these keywords and what they are used for. You can only use keywords for their intended purpose. You must be careful to avoid using these words for definitions that you make. For example, if you name a variable or method "short" or "try," this will cause a problem, because short and try are reserved keywords. Here is the complete list of reserved keywords.

        abstract    default    if            private      this
        boolean     do         implements    protected    throw
        break       double     import        public       throws
        byte        else       instanceof    return       transient
        case        extends    int           short        try
        catch       final      interface     static       void
        char        finally    long          strictfp     volatile
        class       float      native        super        while
        const       for        new           switch
        continue    goto       package       synchronized

1.5.5 More Examples

Consider this program:

        public class NewFun {
            public static void main(String[] args) {
                System.out.println("This is a program");
                System.out.println("with four statements.");
                System.out.println("Each terminated");
                System.out.println("by a semicolon.");
            }
        }
This program is contained in a class called NewFun that has the familiar main method in it. The computer executes the statements sequentially. Control flows from one statement to the next in a forward direction, from the first statement after the opening curly brace of the main method to the last statement just before the closing curly brace of the main method. Each of the four statements is terminated with a semicolon.

Here is a more complicated program example. Notice that it uses two empty println statements to produce blank lines.

        public class DrawFigures1 {
            public static void main(String[] args) {
                System.out.println("   /\\");
                System.out.println("  /  \\");
                System.out.println(" /    \\");
                System.out.println(" \\    /");
                System.out.println("  \\  /");
                System.out.println("   \\/");
                System.out.println();
                System.out.println(" \\    /");
                System.out.println("  \\  /");
                System.out.println("   \\/");
                System.out.println("   /\\");
                System.out.println("  /  \\");
                System.out.println(" /    \\");
                System.out.println();
                System.out.println("   /\\");
                System.out.println("  /  \\");
                System.out.println(" /    \\");
                System.out.println("+------+");
                System.out.println("|      |");
                System.out.println("|      |");
                System.out.println("+------+");
                System.out.println("|United|");
                System.out.println("|States|");
                System.out.println("+------+");
                System.out.println("|      |");
                System.out.println("|      |");
                System.out.println("+------+");
                System.out.println("   /\\");
                System.out.println("  /  \\");
                System.out.println(" /    \\");
            }
        }
Below is the output it generates. Notice that the program has double backslash characters ("\\") but the output has single backslash characters. This is an example of an escape sequence as described in section 1.7.2.

           /\
          /  \
         /    \
         \    /
          \  /
           \/
        
         \    /
          \  /
           \/
           /\
          /  \
         /    \
        
           /\
          /  \
         /    \
        +------+
        |      |
        |      |
        +------+
        |United|
        |States|
        +------+
        |      |
        |      |
        +------+
           /\
          /  \
         /    \

1.6 Program Errors

In 1949 Maurice Wilkes, an early pioneer of computing, expressed a sentiment that still rings true today.

As soon as we started programming, we found out to our surprise that it wasn't as easy to get programs right as we had thought. Debugging had to be discovered. I can remember the exact instant when I realized that a large part of my life from then on was going to be spent in finding mistakes in my own programs.

--Maurice Wilkes

You will also have to face this reality as you learn to program. You're going to make mistakes just like every other programmer in history and you're going to need strategies for eliminating those mistakes. Fortunately the computer itself can help you with some of the work.

There are three kinds of errors that you'll encounter as you write programs:

Syntax errors will be caught by the compiler as it attempts to translate your program from Java into bytecodes. Java has rules that determine its syntax or grammar and the compiler will tell you if you break these rules. Human beings tend to be fairly forgiving. For example, we might find it odd but we generally understand Master Yoda when he says, "Unfortunate that you rushed to face him...that incomplete was your training. Not ready for the burden were you."

The Java compiler will be far less forgiving. If you misplace a single semicolon in your program, you can send the compiler into a tail spin of confusion. And once confused, the compiler might get very confused. A good rule of thumb to follow is that the first error reported by the compiler is the most important one to pay attention to. The rest might be the result of that first error. Many programmers don't even bother to look at errors beyond the first.

A program that generates compilation errors cannot be executed. If you submit your program to the compiler and have errors reported, you must fix the errors and resubmit the program to the compiler. You will not be able to proceed until your program is free of compilation errors.

One of the most common syntax errors is to misspell a word.

Common Syntax Error: Misspelled Words

Spell each word correctly including the use of proper capitalization.

Suppose, for example, that we replace the println statement in the "hello world" program with the following:

    System.out.pruntln("Hello, world!");
When we try to compile this program, it generates the following error message:

        Hello.java:3: cannot find symbol
        symbol  : method pruntln(java.lang.String)
        location: class java.io.PrintStream
                System.out.pruntln("Hello, world!");
                          ^
        1 error
The first line of this output indicates that the error occurs in the file Hello.java on line 3 and that the error is that the compiler "cannot find symbol". The second line indicates that the symbol it can't find is a method called "pruntln". That's because there is no such method. The method is called "println".

This error message can take slightly different forms depending upon what you have misspelled. For example, if you forget to capitalize the word "System":

        system.out.println("Hello, world!");
You will get the following error message:

        Hello.java:3: package system does not exist
                system.out.println("Hello, world!");
                      ^
        1 error
Again the first line indicates that the error occurs in line 3 of the file Hello.java. The error message is slightly different here, indicating that it can't find a package called "system". The second and third lines of this error message include the original line of code with an arrow pointing to where the compiler got confused. The compiler errors are not always the clearest, but if you pay attention to where the error is pointing, you have a pretty good sense of about where the error occurs.

Another common syntax error is to forget to close a string literal.

Common Syntax Error: Not closing a string literal

Every string literal has to have an opening quote and a closing quote.

For example, you might say:

    System.out.println("Hello, world!);
This produces two different error messages even though there is only one underlying error:

        Hello.java:3: unclosed string literal
                System.out.println("Hello, world!);
                                   ^
        Hello.java:4: ')' expected
            }
            ^
        2 errors
In this case, the first error message is quite clear, including an arrow pointing at the beginning of the string literal that wasn't closed. The second error message was caused by the first. Because the string literal was not closed, the compiler didn't notice the right parenthesis and semicolon that appear at the end of the line. This is an example of a single syntax error generating several different error messages. Because this happens often, it is best to fix the first error message each time and recompile to find the next error.

Logic errors are called bugs. Computer programmers use words like bug-ridden and buggy to describe poorly written programs. The term dates back to an old story about a group of programmers couldn't figure out what was wrong with their programs, until they opened up the computer and found a moth trapped inside. The process of finding and eliminating bugs from programs is called debugging.

The form that a bug takes will vary a lot. Sometimes your program will simply behave improperly. For example, it might produce the wrong output. Other times it will ask the computer to perform some task that is clearly a mistake, in which case your program will have a runtime error that stops it from executing.

1.7 Some Problem-Solving Terminology

Brian Kernighan, one of the creators of the C programming language, has said that, "Controlling complexity is the essence of computer programming." People have only a modest capacity for detail. We can't solve complex problems all at once. Instead, we structure our problem-solving by dividing the problem into manageable pieces and conquering each piece individually. We often use the term "decomposition" to describe this principle as applied to programming.

Decomposition

A separation into discernible parts, each of which is simpler than the whole.

With older programming languages like Kernighan's C, decomposition involves dividing a complex task into a set of subtasks. This is very verb or action oriented, dividing up the overall action into a series of smaller actions. This technique is called procedural decomposition.

Java was designed for a different kind of decomposition that is more noun or object oriented. Instead of thinking of the problem as a series of actions to be performed, we think of it as a collection of objects that have to interact.

As a computer scientist you should be familiar with both types of problem solving. This book begins with procedural decomposition and devotes many chapters to mastering various aspects of the procedural approach. Only after you have thoroughly practiced procedural programming will we turn our attention back to object decomposition and object oriented programming.

As an example of procedural decomposition, consider the problem of baking a cake. You can divide this problem into the following subproblems.

        Make the batter.
        Bake the cake.
        Make the frosting.
        Frost the cake.
Each of these four tasks has details associated with it. To make the batter, for example, you:

        Mix the dry ingredients.
        Cream the butter and the sugar.
        Beat in the eggs.
        Stir in the dry ingredients.
Thus, you divide the overall task into subtasks and further divide these subtasks into smaller subtasks. Eventually, you reach descriptions that are so simple they require no further explanation (i.e., primitives).

A diagram of this partial solution would look like this:

                                               Make Cake
                                                   |
                     +-----------+-----------------+----------------------+
                     |           |                 |                      |
                Make Batter     Bake         Make Frosting           Frost Cake
                     |
         +--------+------+-------+
         |        |      |       |
        Mix     Cream   Beat    Stir
        Dry     Butter  In      In Dry
        Ingr.   Sugar   Eggs    Ingr.
"Make Cake" is the highest-level operation of all. It is defined in terms of four lower-level operations called "Make Batter," "Bake," "Make Frosting," and "Frost Cake." The "Make Batter" operation is defined in terms of even lower-level operations. I use diagrams like this throughout the book. They are called structure diagrams, and are intended to show how a problem is broken down into subproblems. In this diagram, you can also tell in what order operations are performed by reading left to right. That will not be true of most structure diagrams. To determine the actual order that sub-programs are performed, you will usually have to refer to the program itself.

A related set of terms is bottom-up and top-down. To make a cake, you might start at the top and reason, "I can make a cake by first making batter, then baking it, then making frosting, and finally putting the frosting on the cake. I could make the batter by first . . .." This is a top-down solution; it starts at the highest level and proceeds downward. A bottom-up solution involves reasoning like, "What can I do now? I could put together dry ingredients. If I then creamed the butter and sugar, and then beat in eggs, and then mixed in dry ingredients, I'd have some batter. If I then baked that batter, I'd have an unfrosted cake. If I then . . .." This is a bottom-up solution because it starts with low-level operations and puts them together to form higher-level operations.

One final problem solving term has to do with the process of programming. Professional programmers develop programs in stages. Instead of trying to produce a complete working program all at once, they choose some piece of the problem to implement first. Then another piece is added and another and another. The overall program is built up slowly, piece by piece. This process is known as iterative enhancement or stepwise refinement.

Iterative Enhancement

The process of producing a program in stages, adding new functionality at each stage.

1.8 Static Methods

Java is designed for objects and programming in Java usually involves decomposing a problem into various objects, each with methods that perform particular tasks. We will see how this works eventually, but for now, we are going to explore procedural decomposition. This will allow us to postpone some of Java's details while we learn about programming in general.

The first decomposition construct we will study is known as a static method.

Static Method

A unit of procedural decomposition. We typically break a class into several static methods, each of which solve some piece of the overall problem.

It is cumbersome and confusing to have to include the keyword "static" for each of these methods, but even though static methods require a bit more work to declare, they are much easier to understand in terms of how they work.

You have already seen a static method in the programs we have written called main. Recall that the main method has the following form:

public static void main(String[] args) { <statement>; <statement>; ... <statement>; } We will be writing static methods that have a similar structure:


public static void <name>() { <statement>; <statement>; ... <statement>; } For example, below is a static method that produces a box as output.

public static void drawBox() { System.out.println("+------+"); System.out.println("| |"); System.out.println("| |"); System.out.println("+------+"); } The first line is known as the method header. It begins with the keyword "public" to indicate that this method is available to anyone who wants to use it. For now, we will declare everything as public. The keyword "static" indicates that this is a static (procedural style) method. The word "void" indicates that this is an action-oriented method that doesn't return any value. The word "drawBox" is the name of the method and the empty parentheses indicate that you don't pass any values when you call the method.

After the header you see a series of println statements that make up the body of this static method. As in method main, the statements of this method are executed in order from first to last when the method is called.

By defining the method drawBox, we have given a simple name to this sequence of println statements. It's as if we said to the Java compiler, "Hey, whenever I tell you to 'drawBox', I really mean that you should execute those println statements in the drawBox method." We execute static methods by "calling" them.

Method Call

A command to call another method, which causes its statements to be executed.

For the drawBox method, we would call it by saying:

    drawBox();
For example, consider this program.


        public class DrawBoxes {
            public static void main(String[] args) {
                drawBox();
                System.out.println();
                drawBox();
            }
        
            public static void drawBox() {
                System.out.println("+------+");
                System.out.println("|      |");
                System.out.println("|      |");
                System.out.println("+------+");
            }
        }
The program is stored in a class called DrawBoxes and it has two methods in it. The first method is the familiar method main and the second is method drawBox. As with any Java program, execution starts with the main method. We execute each of the statements listed in the main method from first to last. Then we're done with execution. But this main method includes two different calls on the drawBox method. So this program is going to do three different things: execute drawBox, execute a println, execute drawBox again.

When the computer executes the call on the drawBox method, it transfers control over to the method and executes each of its statements in order from first to last. Then it continues with whatever statement comes after the method call. The diagram below illustrates what happens when the first call is made.

    public static void main(String[] args) {
        drawBox();
              \
               \
                +-------------------------------------+
                | public static void drawBox() {      |
                |     System.out.println("+------+"); |
                |     System.out.println("|      |"); |
                |     System.out.println("|      |"); |
                |     System.out.println("+------+"); |
                | }                                   |
                +-------------------------------------+
               /
              /
        System.out.println();
        drawBox();
    }
The transfer of control to the drawBox method causes its four println methods to be executed first. Then we return to the main method and execute its println method. Then we call drawBox again and execute its four println statements a second time.

The net effect is that we could instead have executed the following main program:


    public static void main(String[] args) {
        System.out.println("+------+");
        System.out.println("|      |");
        System.out.println("|      |");
        System.out.println("+------+");
	System.out.println();
        System.out.println("+------+");
        System.out.println("|      |");
        System.out.println("|      |");
        System.out.println("+------+");
    }
This version is simpler in terms of its flow of control, but the other version avoids the redundancy of having the same println statements appear multiple times. It also gives a better sense of the structure of the solution. In the original version it is clear that there is a subtask that we have called drawBox that is being performed twice.

Java allows you to define methods in any order you like. We started with main at the top and then had the drawBox method, but the program would work just as well if we switch the order. Method main is always the starting point for program execution and from that starting point we can determine the order in which other methods are called.

1.9 Case Study: DrawFigures2

Earlier in the chapter we saw a program called DrawFigures1 that produced the following output.

           /\
          /  \
         /    \
         \    /
          \  /
           \/
        
         \    /
          \  /
           \/
           /\
          /  \
         /    \
        
           /\
          /  \
         /    \
        +------+
        |      |
        |      |
        +------+
        |United|
        |States|
        +------+
        |      |
        |      |
        +------+
           /\
          /  \
         /    \
It did so with a long sequence of println statements in the main method. If we look closely at this output, we can see that there is a structure to it that would be desirable to capture in our program structure. The output is divided into three subfigures. Each of those subfigures has individual elements as well and some of those elements appear in more than one of the three subfigures.

We can eliminate the redundancy and better indicate the structure of the program by dividing it into static methods.

        public class DrawFigures2 {
            public static void main(String[] args) {
                drawDiamond();
                drawX();
                drawRocket();
            }
        
            public static void drawDiamond() {
                drawCone();
                drawV();
                System.out.println();
            }
        
            public static void drawX() {
                drawV();
                drawCone();
                System.out.println();
            }
        
            public static void drawRocket() {
                drawCone();
                drawBox();
                System.out.println("|United|");
                System.out.println("|States|");
                drawBox();
                drawCone();
                System.out.println();
            }
        
            public static void drawBox() {
                System.out.println("+------+");
                System.out.println("|      |");
                System.out.println("|      |");
                System.out.println("+------+");
            }
        
            public static void drawCone() {
                System.out.println("   /\\");
                System.out.println("  /  \\");
                System.out.println(" /    \\");
            }
        
            public static void drawV() {
                System.out.println(" \\    /");
                System.out.println("  \\  /");
                System.out.println("   \\/");
            }
        }
The program appears in a class called DrawFigures2 and has seven static methods defined within it. The first static method is the usual method main, which calls three methods. These three methods called by main appear next. These methods in turn call three other methods that appear next.

Here is a structure diagram that shows which static methods main calls and which static methods are called by each of them:

           +----------------------------main------------------------+
           |                             |                          |
           |                             |                          |
    +-drawDiamond-+               +----drawX----+            +-drawRocket--+
    |             |               |             |            |             |
    |             |               |             |            |             |
 drawCone       drawV           drawV        drawCone      drawCone     drawBox
As you can see, this program has three levels of structure, two levels of decomposition. The overall task is split into three subtasks. And each of those subtasks has two subtasks.

A program with methods has a more complex flow of control than one without them but the rules are still fairly simple. Remember that when a method is called, the computer executes the statements in the body of that method. Then, control proceeds to the statement after the method call. Also remember that we always start with the main method, executing its statements from first to last.

So in executing program DrawFigures2, we first execute its main method. That, in turn, first executes the body of method drawDiamond. Then drawDiamond executes methods drawCone and drawV (in that order). When drawDiamond finishes executing, control shifts to the next statement in the body of the main program, the call on method drawX.

A complete breakdown of the flow of control from static method to static method in DrawFigures2 follows.

         1st        main
         2nd            drawDiamond
         3rd                drawCone
         4th                drawV
         5th            drawX
         6th                drawV
         7th                drawCone
         8th            drawRocket
         9th                drawCone
        10th                drawBox
        11th                drawBox
        12th                drawCone
The order in which you define methods does not have to parallel the order in which they are executed. The order of execution is determined by the body of method main and by the bodies of methods called from main. A static method declaration is like a dictionary entry--it defines a word, but it does not specify how the word will be used. The body of this main says to first execute drawDiamond, then drawX, then drawRocket. This is the order of execution, regardless of the order they were defined in.

Java allows you to define methods in any order you like. We started with main at the top and worked our way down to lower and lower level methods. This is a popular approach to take, but many people prefer the opposite, having the low level methods first and having main at the end. Java doesn't care what order you use, so you can decide for yourself and do what you think is best.

Method structure adds to program readability. A well-structured solution is easier to comprehend, and the methods themselves become a means of explaining a program. Also, programs with methods are more flexible, more easily adapted to a similar but different task. For example, you can take the seven methods defined in DrawFigures2 and write the following new program to produce a larger and more complex output file. Building static methods to create new commands increases your flexibility without adding unnecessary complication.

    drawCone();
    drawCone();
    drawRocket();
    drawX();
    drawRocket();
    drawDiamond();
    drawBox();
    drawDiamond();
    drawX();
    drawRocket();

1.10 Comments and Readability

While static methods address some of the need for explaining how a program works, they are not enough. The layout of a program can also enhance its readability. Java is a free-format language. This means you can put in as many or as few spaces and blank lines as you like, as long as you put at least one space or other punctuation mark between words. The following program is legal, but hard to read.

        public class Ugly{public static void main(String[] args)
        {System.out.println("How short I am!");}}
Here are some simple rules to follow that will make your programs more readable.

Using these rules to rewrite the program above yields the following.

    public class Ugly {
        public static void main(String[] args){
            System.out.println("How short I am!");
        }
    }
Well-written Java programs are often quite readable, but there will still be times when you will want to include some explanations that are not part of the program itself. You can annotate programs by putting comments in them.

There are two comment forms in Java. In the first form you open the comment with a slash followed by a star and you close it by a star followed by a slash:

    /* like this */
You must not put spaces between the slashes and the asterisks:

    / * this is bad * /
You can put almost any text you like, including carriage returns inside the comment characters.

    /* Thaddeus Martin
       Assignment #1
       Instructor:  Professor Walingford
       Grader:      Bianca Montgomery       */
The only thing you aren't allowed to put inside a comment is the comment end character(s). The following is not legal.

    /* This comment has an asterisk/slash /*/ in it
       which prematurely closes the comment   */
You must be very careful to close all of your comments. Consider the following.

    /* This is a bad program.

    public class Bad {
        public static void main(String[] args){
            System.out.println("Hi there.");
        }
    } /* end of program */
This is not a program; it is one long comment. Because the comment on the first line is not closed, the entire program is swallowed up.

Java provides a second comment form that helps you to avoid such mistakes. You can use two slashes in a row to indicate that everything from that point until the end of the line is a comment. For example, you can put a comment after a statement:

    System.out.println("You lose.");  // too bad!
Or you can create a one-line comment:

    // give an intro to the user
    System.out.println("Welcome to the game of blackjack.");
    System.out.println();
    System.out.println("Let me explain the rules.");

You can also create blocks of such comments:

    // Thadeous Martin
    // Assignment #1
    // Instructor:  Professor Walingford
    // Grader:      Bianca Montgomery
Some people prefer to use the other comment form in a case like this when you know you will have multiple lines of text in the comment, but it is safer to use the form above because you don't have to remember to close the comment. Plus, it makes the comment stand out. Again, this is a place where you will have to decide for yourself which style you prefer.

Don't confuse comments with the text of the println statements. The text of your comments will not be displayed as output when the program executes. The comments are to help examine and understand the program.

It is a good idea to include comments at the beginning of each class file to indicate what the class does. You might also want to include information about who you are and who your grader is. You should also comment each method to indicate what it does.

Java has a particular style of comments known as Javadoc comments. They have a more complex format to them but they have the advantage that you can use a program to extract the comments to make html files suitable for reading with a web browser.

1.11 Chapter Summary

1.12 Self-Check Exercises

  1. Which of the following can be used in a Java program as identifiers?
        println         first-name   AnnualSalary   "hello"     ABC
        42isTheAnswer   for          sum_of_data    _average    B4
    
  2. The following program contains at least 10 syntax errors. What are they?
            public class LotsOf Errors {
                public static main(String args) {
                    System.println("Hello, world!);
                    message()
                }
            
                public static void message {
                    System.out println("This program surely cannot";
                    System.out.println("have any so-called "errors" in it");
                }
  3. Consider the following program, saved into a file named Example.java :
            public class Example {                                           //  1
                public static void displayRule() {                           //  2
                    System.out println("The first rule ");                   //  3
                    System.out.println("of Java Club is,");                  //  4
                    System.out.println();                                    //  5
                    System.out.println("you do not talk about Java Club.");  //  6
                }                                                            //  7
                                                                             //  8
                public static void main(String[] args) {                     //  9
                    System.out.println("The rules of Java Club.");           // 10
                    displayRule();                                           // 11
                    displayRule();                                           // 12
                }                                                            // 13
            }                                                                // 14

    What would happen if each of the following changes was made to the Example program? Treat each change independently of the others. For example: no effect, syntax error, or different program output.

  4. What is the output produced from the following statements?
        System.out.println("\"Quotes\"");
        System.out.println("Slashes \\//");
        System.out.println("How '\"confounding' \"\\\" it is!");
    
  5. What is the output produced from the following statements?
        System.out.println("Shaq is 7\'1\"");
        System.out.println("The string \"\" is an empty message.");
        System.out.println("\\'\"\"");
    
  6. What is the output produced from the following statements?
        System.out.println("Dear \"DoubleSlash\" magazine,");
        System.out.println("\tYour publication confuses me.  Is it a");
        System.out.println("\\\\ slash or a //// slash that I should use?");
        System.out.println("\nSincerely,");
        System.out.println("Susan \"Suzy\" Smith");
    
  7. What series of System.out.println statements would produce the following output?
        "Several slashes are sometimes seen,"
        said Sally.  "I've said so myself."  See?
        \ / \\ // \\\ ///
    
  8. What series of System.out.println statements would produce the following output?
        This is a test of your
        knowledge of "quotes" used
        in 'string constants.'
    
        You're bound to "get it right"
        if you read the section on
        ''quotes.''
    
  9. What series of System.out.println statements would produce the following output?
        What is the difference between
        a ' and a "?  Or between a " and a \"?
    
        One is what we see when we're typing our program.
        The other is what appears on the "console."
    
  10. Write a complete Java program that prints the following output:
        //////////////////////
        || Victory is mine! ||
        \\\\\\\\\\\\\\\\\\\\\\
    

  11. Write a complete Java program that prints the following output:
          \/
         \\//
        \\\///
        ///\\\
         //\\
          /\
    
  12. Write a complete Java program that prints the following output:
        A well-formed Java program has a main
        method with { and } braces.
    
        A System.out.println statement has ( and )
        and usually a String that starts and ends
        with a " character.  (But we type \" instead!)
    
  13. What is the output of the following program? You may wish to draw a structure diagram first.
            public class Strange {
                public static void first() {
                    System.out.println("Inside first method");
                }
            
                public static void second() {
                    System.out.println("Inside second method");
                    first();
                }
            
                public static void third() {
                    System.out.println("Inside third method");
                    first();
                    second();
                }
            
                public static void main(String[] args) {
                    first();
                    third();
                    second();
                    third();
                }
            }
  14. What would have been the output of the preceding program if the third method had contained the following statements?
        public static void third() {
            first();
            second();
            System.out.println("Inside third method");
        }
    
  15. What would have been the output of the Strange program if the main method had contained the following statements? (Use the original version of third, not the modified version from the most recent exercise.)
        public static void main(String[] args) {
            second();
            first();
            second();
            third();
        }
    
  16. What is the output of the following program? You may wish to draw a structure diagram first.
            public class Confusing {
                public static void method2() {
                    method1();
                    System.out.println("I am method 2.");
                }
            
                public static void method3() {
                    method2();
                    System.out.println("I am method 3.");
                    method1();
                }
            
                public static void method1() {
                    System.out.println("I am method 1.");
                }
            
                public static void main(String[] args) {
                    method1();
                    method3();
                    method2();
                    method3();
                }
            }
    
  17. What would have been the output of the preceding program if the method3 method had contained the following statements?
        public static void method3() {
            method1();
            method2();
            System.out.println("I am method 3.");
        }
    
  18. What would have been the output of the Confusing program if the main method had contained the following statements? (Use the original version of method3, not the modified version from the most recent exercise.)
        public static void main(String[] args) {
            method2();
            method1();
            method3();
            method2();
        }
    

  19. Write a complete Java program that prints the following output. Use at least one static method besides main to help you.
        //////////////////////
        || Victory is mine! ||
        \\\\\\\\\\\\\\\\\\\\\\
        || Victory is mine! ||
        \\\\\\\\\\\\\\\\\\\\\\
        || Victory is mine! ||
        \\\\\\\\\\\\\\\\\\\\\\
        || Victory is mine! ||
        \\\\\\\\\\\\\\\\\\\\\\
        || Victory is mine! ||
        \\\\\\\\\\\\\\\\\\\\\\
    
  20. Write a program that displays the following output:
       _______
      /       \
     /         \
     -"-'-"-'-"-
     \         /
      \_______/
    
  21. Modify the program from the previous exercise so that it displays the following output. Use static methods as appropriate.
       _______
      /       \
     /         \
     \         /
      \_______/
     -"-'-"-'-"-
       _______
      /       \
     /         \
     \         /
      \_______/
     -"-'-"-'-"-
     \         /
      \_______/
       _______
      /       \
     /         \
     -"-'-"-'-"-
     \         /
      \_______/
    
  22. Write a Java program that generates the following output. Use static methods to show structure and eliminate redundancy in your solution.

    Note that there are two rocket ships next to each other. What redundancy can you eliminate using static methods? What redundancy cannot be eliminated?

           /\       /\
          /  \     /  \
         /    \   /    \
        +------+ +------+
        |      | |      |
        |      | |      |
        +------+ +------+
        |United| |United|
        |States| |States|
        +------+ +------+
        |      | |      |
        |      | |      |
        +------+ +------+
           /\       /\
          /  \     /  \
         /    \   /    \
    
  23. Write a Java program that generates the following output. Use static methods to show structure and eliminate redundancy in your solution.
        *****
        *****
         * *
          *
         * *
    
        *****
        *****
         * *
          *
         * *
        *****
        *****
    
          *
          *
          *
        *****
        *****
         * *
          *
         * *
    
  24. The following program is legal under Java's syntax rules, but it is difficult to read because of its layout and lack of comments. Reformat it using the rules given in this chapter, and add a comment header at the top of the program.
            public class GiveAdvice{ public static
            void main(String[  ]args){ System.out.println (
            "Programs can be easy or difficult");  System.out.println(
            "to read, depending upon their format."
            );  System.out.println()
            ;  System.out.println("Everyone, including yourself, will be");System.out.println
            (
                  "happier if you choose to format your");System.out.println("Programs."); }
                  }
  25. The following program is legal under Java's syntax rules, but it is difficult to read because of its layout and lack of comments. Reformat it using the rules given in this chapter, and add a comment header at the top of the program.
            public 
            class Messy{public 
            static void main(String[]args){message (   )
              ;System.out.println()   ; message ( );}   public static void 
            message() { System.out.println(
                "I really wish that"
                );System.out.println
            ("I had formatted my source")
            ;System.out.println("code correctly!");}}
    

1.13 Programming Problems

  1. Write a program to spell out MISSISSIPPI using block letters like the following:

            MMM        MMM
             MMM      MMM
             MMMM    MMMM
             MM MM  MM MM
             MM  MMMM  MM
             MM   MM   MM
             MM        MM
             MM        MM
            MMM        MMM
    
  2. Write a program that produces several letters. You should create low-level static methods that write out individual paragraphs and then write high-level static methods that produce letters by combining paragraphs.

  3. Write a program that produces as output a song. Use static methods for each verse and the refrain.

  4. Write a program that produces as output the words of "The Twelve Days of Christmas." (hint: static methods simplify this task.)

Stuart Reges
Marty Stepp
Last modified: Tue Jan 3 00:14:17 PDT 2006

Chapter 2
Primitive Data and Definite Loops

Copyright © 2005 by Stuart Reges and Marty Stepp

2.1 Introduction

Now that you know something about the basic structure of Java programs, you are ready to start solving problems with some complexity. You will still be restricted to programs that produce output, but we will begin to explore some of the aspects of programming that require problem solving skills.

The first half of the chapter fills in three important areas. First, it examines expressions: how simple computations are expressed in Java, particularly those involving numeric data. Second, it discusses program elements called variables that can change in value as the program executes. Third, it details the workings of the print and println statements that produce output.

The second half of the chapter introduces your first control structure: the for loop. You use this structure to repeat actions in a program. This is useful whenever you find a pattern in a complex task like the creation of a complex figure, because you can use a for loop to repeat an action that creates a particular pattern. The challenge is finding each pattern and figuring out what repeated actions will reproduce it.

The for loop is a flexible control structure that can be used for many tasks, but in this chapter we will be looking at its use in what are known as "definite loops" where you know exactly how many times you want to perform a particular task. In chapter 5 we will discuss how to write what are known as "indefinite loops" where you don't know in advance how many times to perform a task.

2.2 Data and Types

Programs manipulate information and information comes in many forms. Java is what we call a strongly typed language, which means that Java requires us to be very explicit about what kind of information we intend to manipulate. Everything that we manipulate in a Java program will be of a certain type and we will constantly find ourselves telling Java what types of data we intend to use.

A decision was made early in the design of Java to have two very different kinds of data: primitive data and objects. The designers admit that this decision was made purely on the basis of performance to make Java programs run faster. This is unfortunate because it means that we have to learn two sets of rules about how data works. But this is one of those times when you simply have to pay the price if you want to use an industrial strength programming language. To make this a little easier, we will study the primitive data types first in this chapter and then turn our attention to objects in chapter 3.

There are eight primitive data types in Java but we will explore only four of them. The four types we will explore are listed below.

Commonly Used Primitive Types in Java
Type Description Examples
int integers (whole numbers) 42, -3, 18, 20493, 0
double real numbers 7.35, 14.9, -19.83423
char single characters 'a', 'X', '!'
boolean logical values true, false

It may seem odd to have one type for integers and another type for real numbers. Isn't every integer a real number? The answer is yes, but these are fundamentally different types of numbers. The difference is so great that we make this distinction even in English. We don't ask, "How much sisters do you have?" or "How many do you weigh?" We realize that sisters come in discrete integer quantities (0 sisters, 1 sister, 2 sisters, 3 sisters, and so on) and we use the word "many" for integer quantities ("How many sisters do you have?"). Similarly, we realize that weight can vary by tiny amounts (175 pounds versus 175.5 pounds versus 175.25 pounds and so on) and we use the word "much" for these real-valued quantities ("How much do you weigh?").

In programming this distinction is even more important because integers and reals are represented in a different way in the computer's memory. Integers are stored exactly while reals are stored as approximations with a limited number of digits of accuracy. We will see that this leads to round-off errors when you use real values.

The name "double" for real values isn't very clear. It's an accident of history that we have this name in much the same way that we still talk about "dialing" a number on our telephones even though modern telephones don't have a dial. The C programming language introduced a type called "float" for storing real numbers (short for "floating point number"). But floats had limited accuracy and another type was introduced called "double," short for "double precision" (double the precision of a simple float). As memory has become cheaper, people have moved more towards using double as the default for floating-point values. In hindsight, it might have been better to use the word "float" for what is now called double and then used a word like "half" for the values with less accuracy, but it's tough to change habits that are so ingrained. So programming languages will continue to use the word "double" for floating point numbers and people will still talk about dialing people on the phone even if they've never touched a telephone dial.


2.3 Expressions

In Java, you refer to values by using an expression.

Expression

A simple value or a set of operations that produces a value.

The simplest expression is a specific value, like 42 or 28.9. We call these "literal values" or "literals". More complex expressions involve combining simple values. Suppose, for example, that you want to know how many bottles of water you have. If you have two 6-packs, four 4-packs and 2 individual bottles, then you could compute the total number of bottles with the following expression:

        (2 * 6) + (4 * 4) + 2
Notice that we use the asterisk to represent multiplication and that we use parentheses to group parts of the expression. The computer determines the value of an expression by evaluating it.

Evaluation

The process of obtaining the value of an expression.

The value obtained when an expression is evaluated is called the result.

Complex expressions are formed using operators. An operator is a special symbol (like "+" or "*") used to indicate an operation to be performed on one or more values. The values used in the expression are called operands. For example, consider the following simple expressions:

        3 + 29
        4 * 5

The operators here are the "+" and "*" and the operands are simple numbers:

           3        +        29
           |        |         |
        operand  operator  operand

           4        *         5
           |        |         |
        operand  operator  operand
When you form complex expressions, these simpler expressions can in turn become operands for other operators. For example, consider the following expression:

        (3 + 29) - (4 * 5)
Which has two levels of operators:

          (3        +        29)     -      (4        *         5)
           |        |         |      |       |        |         |
        operand  operator  operand   |    operand  operator  operand
           |                  |      |       |                  |
           +------------------+      |       +------------------+
                 operand          operator          operand
The plus operator has simple operands of 3 and 29 and the times operator has simple operands of 4 and 5, but the minus operator has operands that are each parenthesized expressions with operators of their own. Thus, complex expressions can be built from smaller expressions. At the lowest level you have simple numbers. These are used as operands to make more complex expressions, which in turn can be used as operands in even more complex expressions.

There are many things you can do with expressions. One of the simplest things you can do is to print them using a println statement. For example, if you say:

        System.out.println(42);
        System.out.println(2 + 2);
you will get the following two lines of output:

        42
        4
Notice that for the second println, the computer evaluates the expression (adding 2 and 2) and prints the result (in this case, 4).

You will see many different operators as you progress through the book, all of which can be used to form expressions. Expressions can be arbitrarily complex, with as many operators as you like. For that reason, when we tell you, "An expression can be used here," we will often point out that we mean "arbitrary expressions" to emphasize that you can use complex expressions as well as simple values.

2.3.1 Literals

The simplest expressions refer to values directly using what are known as literals. An integer literal (considered to be of type int) is a sequence of digits with or without a leading sign:

3 482 -29434 0 92348 +9812 A floating point literal (considered to be of type double) will include a decimal point, as in:

298.4 0.284 207. .2843 -17.452 -.98 Floating point literals can also be expressed in scientific notation (a number followed by "e" followed by an integer) as in:

3.84e92 1e-5 2.458e12 The first value above represents 3.84 times 10 to the 92nd power. The second number represents 10 to the -5 power. The third represents 2.458 times 10 to the 12th power.

Character literals (of type char) are enclosed in single quotation marks and can include just one character:

'a' 'm' 'X' '!' '3' '\\' All of these are of type char. Notice that the last example uses an escape sequence to represent the backslash character. You can even refer to the single quotation character using an escape sequence:

'\'' The primitive type boolean has just two literals:

true false

2.3.2 Arithmetic Operators

The basic arithmetic operators are as follows.

Arithmetic Operators in Java
Operator Meaning Example Result
+ addition 2 + 2 4
- subtraction 53 - 18 35
* multiplication 3 * 8 24
/ division 4.8 / 2.0 2.4
% remainder or mod 19 % 5 4

The addition and subtraction operators should be familiar to you. The asterisk as a multiplication operator might be a surprise for nonprogrammers but doesn't take long to get used to. Division is also fairly familiar, although as we'll see, there are really two different division operations. The remainder or mod operation is the one that will be most unfamiliar to people.

Division presents a problem in the domain of integers. When you divide 119 by 5, for example, you do not get an integer. Therefore, integer division is expressed as two different integers--a quotient and a remainder:

        119
        ---  =  23 (quotient) with 4 (remainder)
         5
In terms of the arithmetic operators:

        119 / 5 evaluates to 23
        119 % 5 evaluates to  4

If you remember long-division, you remember doing calculations like this:

               31
           _______
        34 ) 1079
             102
             ----
               59
               34
               --
               25
Here, dividing 1079 by 34 yields 31 with a remainder of 25. Using arithmetic operators, the problem would be described like this:

        1079 / 34 evaluates to 31
        1079 % 34 evaluates to 25
It takes a while to get used to the integer division operators. For the division operator ("/"), the key thing to keep in mind is that it truncates anything after the decimal point. So if you imagine computing an answer on a calculator, just think of ignoring anything after the decimal point.

        19 / 5 is 3.8 on a calculator, so 19 / 5 evaluates to 3
        207 / 10 is 20.7 on a calculator, so 203 / 10 evaluates to 20
        3 / 8 is 0.375 on a calculator, so 3 / 8 evaluates to 0
The remainder operator is usually referred to as the "mod operator" or simply "mod". The mod operator lets you know how much was left unaccounted for by the truncated division operator. You have to think in terms of what the truncated division operator gives as a result and then you can determine what is left over. For example, given the examples above, we'd compute the mod results as follows.

        3 * 5 is 15, so 19 % 5 is 4
        20 * 10 is 200, so 207 % 10 is 7
        0 * 8 is 0, so 3 % 8 is 3
In each case, we figure out how much of the number is accounted for by the first division operator. The mod operator tells you about the excess.

You can get a result of 0 for the mod operator. This happens when one number goes evenly into another. For example:

        28 % 7 is 0 because 4 * 7 is 28
        95 % 5 is 0 because 19 * 5 is 95
        44 % 2 is 0 because 22 * 2 is 44
The mod operator has many useful applications in computer programs. Below are just a few ideas.

For floating point values (values of type double) the division operator does what we consider "normal" division. So even though the expression 119/5 evaluates to 23, the expression 119.0/5.0 evaluates to 23.8. The remainder operator can be used with doubles as well as with ints and it has a similar meaning. You consider how much is left over when you take away as many "whole" values as you can. For example, the expression 10.2 % 2.4 evaluates to 0.6 because you can take away four 2.4's from 10.2, leaving you with 0.6 left over.

2.3.3 Precedence

Java expressions are like complex noun phrases in English. Such phrases are subject to ambiguity, as in, "the man on the hill by the river with the telescope." Is the river by the hill or by the man? Is the man holding the telescope, or is the telescope on the hill, or is the telescope in the river? You don't know how the various parts are grouped together.

You can get the same kind of ambiguity if parentheses aren't used to group the parts of a Java expression. For example, the expression 2 + 3 * 4 has two operators. Which is performed first? You could interpret this two ways:

        (2 + 3) * 4 = 5 * 4 = 20
        2 + (3 * 4) = 2 + 12 = 14
The first of these evaluates to 20 while the second evaluates to 14. To deal with the ambiguity, Java has rules of precedence that determine how the various parts are grouped together.

Precedence

The relative importance of one operator over another.

Rules of precedence are applied when the grouping of operators in an expression is ambiguous. An operator with low precedence is evaluated after operators of higher precedence. Within a given level of precedence the operators are evaluated in one direction, usually left to right.

For arithmetic expressions there are two levels of precedence. The multiplicative operators (*, /, %) have a higher level of precedence than the additive operators (+, -). So the expression 2 + 3 * 4 is interpreted as:

        2 + 3 * 4 = 2 + (3 * 4) = 2 + 12 = 14
Within the same level of precedence, arithmetic operators are evaluated from left to right. This often doesn't make a difference in the final result, but occasionally it does. Consider, for example, the expression:

        40 - 25 - 9
which evaluates as follows:

        40 - 25 - 9 = (40 - 25) - 9 = 15 - 9 = 6
You would get a different result if the second minus were evaluated first.

You can always override precedence with parentheses. For example, if you really want the second minus to be evaluated first, you can force that to happen by introducing parentheses:

        40 - (25 - 9)
will evaluates as follows:

        40 - (25 - 9) = 40 - 16 = 24
With arithmetic we also have what are known as "unary" plus and minus, with a single operand. For example, we can find the negation of 8 by asking for "-8". These unary operators have a higher level of precedence, which allows you to form expressions like the following:

        12 * -8
which evaluates to -96.

We will see many operators in the next few chapters. Below is a precedence table that has the arithmetic operators. As we learn about more operators, we'll update this table to include them as well. Higher in this table means higher in precedence.

Java Operator Precedence
Description Operators
unary operators +, -
multiplicative operators *, /, %
additive operators +, -

Before we leave this topic, let's look at a complex expression and see how it is evaluated step by step. Consider the following expression:

        13 * 2 - 209 / 10 % 5 + 2 * 2
It has a total of six operators: two multiplications, one division, one mod, one subtraction and one addition. The multiplications, division and mod will be performed first because they have higher precedence and they will be performed from left-to-right order because they are all at the same level of precedence:

        13 * 2 + 239 / 10 % 5 - 2 * 2
        \----/
          26   + 239 / 10 % 5 - 2 * 2
                 \------/
          26   +    23    % 5 - 2 * 2
                    \-------/
          26   +        3     - 2 * 2
                                \---/
          26   +        3     -   4
Now we evaluate the additive operators from left to right:

          26   +        3     -   4
          \-------------/
                29            -   4
                \-----------------/
                        25

2.3.4 Mixing Types, Conversion and Casting

We often find ourselves mixing values of different types and wanting to convert from one type to another. Java has simple rules that avoid confusion and provides a mechanism for requesting that a value be converted from one type to another.

We often find ourselves mixing ints and doubles. We might, for example, ask Java to compute 2 * 3.6. This involves the integer literal 2 and the floating point literal 3.6. In this case Java converts the integer into a floating point and performs this computation entirely in floating point. If Java encounters an int where it was expecting a double, it always converts the int to a double.

This becomes particularly important when you form expressions that involve division. If the two operands are both of type int, then Java will use integer (truncated) division. If either of the two operands is of type double, however, then it will do real-valued (normal) division. For example, 23 / 4 evaluates to 5 but all of the following evaluate to 5.75: 23.0 / 4, 23. / 4, 23 / 4.0, 23 / 4., 23. / 4., 23.0 / 4.0.

Sometimes you want Java to go the other way, converting a double into an int. You can ask Java to do this with what is known as a cast. Think of it as "casting a value in a different light." You request a cast by putting the name of the type you want to cast to in parentheses in front of the thing you want to cast. For example, if you say:

(int) 4.75 you will get the integer 4. When you cast a double value to an int it simply truncates anything after the decimal point. If you want to cast the result of an expression, you have to be careful to use parentheses. For example, suppose that you have some books that are 0.15 feet wide and you want to know how many of them would fit in a bookshelf that is 2.5 feet wide. You could do a straight division of 2.5 / 0.15, but that evaluates to a floating point result of 16 and change. You don't care about the change. You want to compute the 16 part. You might form the following expression:

(int) 2.5 / 0.15 but this evaluates to the wrong answer. That's because the cast is applied to whatever comes right after it, which is the value 2.5. So this casts 2.5 into the integer 2, divides by 0.15 and evaluates to 13 and change, which isn't an integer and isn't the right answer. You want to form this expression:

(int) (2.5 / 0.15) This performs the division first to get 16 and change and then it casts that value to an int by truncating and evaluates to the int value 16, which is the answer you're looking for.

In a later chapter we will explore the details of type char and we will see that every character has a corresponding integer value. Java is willing to automatically convert a value of type char into an int whenever it is expecting an int. For example, you can form the following rather strange expression:

2 * 'a' Java sees a value of type char where it was expecting an int, but it's happy to convert this for you, substituting the integer value 97 for 'a' and computing the result as 194.

Java will not, however, automatically convert in the other direction. That is because the type char is in a sense a subset of the type int. Every char value has a corresponding int, but not every int has a corresponding char. That means that Java would always be able to turn a char into an int, but might not always be able to turn an int into a char. So in this case Java insists on a cast. This allows you to form expressions like the following:

(char) ('a' + 3) Inside the parentheses we have a combination of a char value and an int value, so Java converts the char to an int (97) and then performs the addition operation, yielding the value 100. This integer is then converted to a char value because of the cast, which yields the character 'd' (the character that appears 3 later in the sequence than the character 'a'). This turns out to be a useful kind of expression to form, as we'll see in a later chapter.

2.4 Variables and Assignment

Primitive data can be stored in the computer's memory in a variable.

Variable

A memory location with a name and a type that stores a value.

Think of the computer's memory as being like a giant spreadsheet that has many cells where data can be stored. When you tell Java that you want to have a variable, you are asking it to set aside one of those cells for this new variable. Initially the cell will be empty, but you will have the option to store a value in the cell. And as with spreadsheets, you will have the option to change the value in that cell later if you want to.

Java is a little more picky than a spreadsheet in that it requires you to tell it exactly what kind of data you are going to store in that cell. For example, do you want to store an integer, in which case you use type int, or do you want to store a real value, in which case you use type double? You have to tell Java this in advance. You also have to decide on a name to use when you want to refer to this memory location. The normal rules of Java identifiers apply (must start with a letter and can then have any combination of letters and digits). The standard convention in Java is to start variable names with a lower case letter, as in "number" or "digits", and to capitalize any subsequent words, as in "numberOfDigits".

The process of requesting that Java set aside a new variable is known as declaring the variable. Each variable is declared just once. If you declare the variable more than once, you will get an error message from the Java compiler. Simple variable declarations are of the following form:

<type> <name>; as in:

        int first;
        int second;
        int sum;
Notice that variable declarations end with a semicolon just like statements do. These declarations can appear anywhere a statement can occur.

Notice that the declaration indicates the type and the name of the variable. Once a variable is declared, Java sets aside a memory location to store its value. Using this simple form of variable declaration, Java does not store an initial value in the memory location. So given the three declarations above, Java would allocate three memory locations for the variables and would have them initially not store a value:

              +---+                    +---+                 +---+
        first | ? |             second | ? |             sum | ? |
              +---+                    +---+                 +---+
We refer to these as uninitialized variables and they are similar to blank cells in a spreadsheet. So how do we get values into those cells? The easiest way to do so is using an assignment statement. The general syntax of the assignment statement is:

<variable> = <expression>; as in:

first = 47; This statement stores the value 47 in the memory location for the variable first. When the statement executes, the computer first evaluates the expression on the right side; then, it stores the result in the memory location for the given variable. So after executing this statement, memory would look like this:

              +----+                    +---+                 +---+
        first | 47 |             second | ? |             sum | ? |
              +----+                    +---+                 +---+
The variable first has now been initialized, but the variables second and sum are still uninitialized. The following assignment statement involves a more complex expression:

second = 37 - 18 + 3; Here, arithmetic operators form an expression of type int. The expression evaluates to 22, giving the value 22 to the variable second. Thus, after executing this statement memory looks like this:


              +----+                    +----+                 +---+
        first | 47 |             second | 22 |             sum | ? |
              +----+                    +----+                 +---+
Expressions need not contain only literals:

sum = first + second; To calculate the value of this expression, the computer adds together the current values of the variables first and second and comes up with the answer 69. This value is stored in the variable sum.

              +----+                    +----+                 +----+
        first | 47 |             second | 22 |             sum | 69 |
              +----+                    +----+                 +----+
Keep in mind that these are like values stored in the cells of a spreadsheet. That means that you have the option to change a cell to store some new value. For example, you might turn around and execute this statement:

        sum = 23;
which would overwrite the old value of sum with this new value for sum:

              +----+                    +----+                 +----+
        first | 47 |             second | 22 |             sum | 23 |
              +----+                    +----+                 +----+
As its name implies, a variable can take on different values at different times.

Even though Java uses the equals sign for assignment, don't confuse this with a statement of equality. This is a command to perform an action. For example, this is not a legal assignment statement:

        47 = first;
This does not fit the template. The left side must be a variable and in this statement the left side is an integer literal.

The assignment statement does not represent an algebraic relationship. In algebra if you say something like:

        x = y + 2
you are saying something very different from a statement like:

        second = first + 2;
When you say that x is equal to y plus 2, you are stating a fact for now and forever. If x changes, y will change accordingly. The assignment statement, on the other hand, is a command to perform an action at some moment in time. It does not represent a lasting relationship between variables. Consider this short program:

        public class Sample {
            public static void main(String[] args) {
                int first;
                int second;
                first = 13;
                second = first + 2;
                first = 50;
                System.out.println(first);
                System.out.println(second);
            }
        }
This program outputs the values 50 and 15, which obviously don't have the algebraic relationship that one is 2 more than the other. Let's see why.

Initially first and second are uninitialized:

              +---+                    +---+
        first | ? |             second | ? |
              +---+                    +---+
The first assignment statement gives a value to the variable first:

              +----+                   +---+
        first | 13 |            second | ? |
              +----+                   +---+
and the second gives an initial value to the variable second:

              +----+                   +----+
        first | 13 |            second | 15 |
              +----+                   +----+
While there now exists an algebraic relationship between first and second similar to x = y + 2, this relationship is temporary because the third assignment statement reassigns the value of first:

              +----+                   +----+
        first | 50 |            second | 15 |
              +----+                   +----+
The variable second is no longer 2 more than first. The last assignment statement wipes out the old value of first and leaves second unchanged. This temporary quality of the assignment statement is sometimes difficult for beginners to grasp. Remember that in algebra you reason about permanent relationships, whereas in programming you reason about sequences of actions. The best habit to get into is to read the assignment statement's equals sign as "gets" or "becomes."

One very common assignment statement that points out the difference between algebraic relationships and program statements is:

first = first + 1; This statement says, "first gets the value of first plus one." This may seem a rather odd statement, but you should be able to decipher it given the rules outlined above. Suppose that the current value of first is 19. To execute the statement, you first evaluate the expression to obtain the result 20. The computer stores this value in the variable named on the left, in variable first. Thus, this statement adds one to the value of the variable. We refer to this as incrementing the value of first. It is a fundamental programming operation because it is the programming equivalent of counting (1, 2, 3, 4 and so on). The following statement is a variation that counts down, which we call decrementing a variable:

first = first - 1;

2.4.1 Other Forms of Variable Declaration and Assignment

In the last section we saw the simplest form of variable declaration and assignment, but Java is a complex language that provides a lot of flexibility to programmers. It wouldn't be a bad idea to stick with the simplest form while you are learning, but you'll come across these other forms as you read other people's programs, so you'll want to understand what they mean.

The first variation is that Java allows you to provide an initial value to a variable at the time that you declare it. The syntax is as follows:

<type> <name> = <expression>; as in:

        int x = 23;
        int y = 2 + 3 * 8;
        int z = x * y - 1;
The statements above have the same effect as having three declarations followed by three assignment statements:

        int x;
        int y;
        int z;
        x = 23;
        y = 2 + 3 * 8;
        z = x * y - 1;
So this variation is like a combination of declaration and assignment in one line of code.

Another variation is to declare several variables all of the same type. The syntax is as follows:

<type> <name>, <name>, <name>, ..., <name>; as in:

        int x, y, z;
Notice that the type appears just once at the beginning of the declaration. So the example above declares three different variables all of type int.

The final variation is a mixture of the previous two. You can declare multiple variables all of the same type and you can initialize them at the same time. For example, you could say:

        int x = 8, y = 17, z = 9;
This not only declares the three integer variables x, y and z, it also gives them initial values (8, 17 and 9, respectively). Java even allows you to mix initializing and not initializing, as in:

        int x, y = 17, z = 9;
This declares three integer variables called x, y and z and provides an initial value to two of them (y and z). The variable x would be uninitialized by this declaration.

One of the things to keep in mind as you learn is that you declare any given variable just once. You can assign it as many times as you like once you've declared it, but the declaration appears just once. Think of variable declaration as being like checking into a hotel and assignment as being like going in and out of your room. You have to check in first to get your room key, but then you can come and go as often as you like. If you tried to check in a second time, the hotel would be likely to ask you if you really want to pay for a second room.

If Java sees you declaring a variable more than once, it generates a compiler error. For example, if you say:

        int x = 13;
        System.out.println(x);
        int x = 2;
        System.out.println(x);
The first line is okay. It declares an integer variable called x and initializes it to 13. The second line is okay because it simply prints the value of x. But the third line will generate an error message that "x is already defined". If you want to change the value of x, then you need to use a simple assignment statement instead of a variable declaration:

        int x = 13;
        System.out.println(x);
        x = 2;
        System.out.println(x);
Common Syntax Error: Multiple Variable Declarations

Any given variable should be declared just once. If you want to change the value of the variable after it has been declared, use a simple assignment statement.

We have been referring to the "assignment statement" but in fact assignment is an operator, not a statement. When you assign a value to a variable, the overall expression evaluates to the value just assigned. That means that you can form expressions that have assignment operators embedded within them. Unlike most other operators, the assignment operator evaluates from right to left. This allows programmers to write statements like the following:

        int x, y, z;
        x = y = z = 2 * 5 + 4;
Because the assignment operator evaluates right-to-left, this is equivalent to:
        x = (y = (z = 2 * 5 + 4));
The expression "2 * 5 + 4" evaluates to 14. This value is assigned to z. But that in turn evaluates to 14, which is then used to assign a value to y. And that assignment evaluates to 14 as well, which is used to assign a value to x. So all three variables will be assigned the value 14 as a result of this statement.

While you can do assignments like these, it's not clear that you really want to do assignments like these. The chained assignment statement above is not that difficult to read, but try to figure out what the following statement does:

        x = 3 * (y = 2 + 2) / (z = 2);
It's easier to see what is going on when you write this as three separate statements:

        y = 2 + 2;
        z = 2;
        x = 3 * y / z;

2.4.2 String Concatenation

You saw in chapter 1 that you can output string literals using System.out.println. You can also output numeric expressions using System.out.println:

System.out.println(12 + 3 - 1); This statement causes the computer to first evaluate the expression, which yields the value 14, and then to write 14 to the console window. Often you want to output more than one value on a line. Unfortunately, you can only pass one value to println. To get around this, Java provides a simple mechanism called "concatenation" for putting several pieces together into one long string literal.

String Concatenation

Combining several strings or other textual data into a single string.

The plus operator is used to concatenate the pieces together. Consider, for example, the following expression:

        "I have " + 3 + " things to concatenate"
You have to pay close attention to the quotation marks in an expression like this to keep track of which parts are "inside" a string literal and which are outside. This expression begins with the text "I have " (including a space). Then we see a plus sign and the integer literal 3. Java converts the integer into a textual form ("3") and concatenates the two pieces together to form "I have 3". Following the 3 is another plus and another string literal " things to concatenate" (which starts with a space). This piece is glued onto the end of the other string to form the string "I have 3 things to concatenate".

Because this expression produces a single concatenated string, we can include it in a println statement:

        System.out.println("I have " + 3 + " things to concatenate");
which would produce a single line of output:

        I have 3 things to concatenate
We often use string concatenation to report the value of a variable. Consider, for example, the following program that computes the number of hours, minutes and seconds in a standard year.

        public class Time {
            public static void main(String[] args) {
                int hours = 365 * 24;
                int minutes = hours * 60;
                int seconds = minutes * 60;
                System.out.println("Total hours in a year   = " + hours);
                System.out.println("Total minutes in a year = " + minutes);
                System.out.println("Total seconds in a year = " + seconds);
            }
        }
Notice that the three println commands at the end each have a string literal concatenated with a variable. The program produces the following output.

        Total hours in a year   = 8760
        Total minutes in a year = 525600
        Total seconds in a year = 31536000
You can use concatenation to form arbitrarily complex expressions. For example, if you had variables x, y and z and you wanted to write them out in coordinate format with parentheses and commas you could say:

System.out.println("(" + x + ", " + y + ", " + z + ")"); If x, y and z have the values 8, 19 and 23, respectively, then this statement would output the string "(8, 19, 23)".

The plus used for concatenation has the same level of precedence as the normal arithmetic plus operator. This can lead to some confusion. Consider, for example, the following expression:

        2 + 3 + " hello " + 7 + 2 * 3
This expression has four plus operators and one multiplication operator. Because of precedence, we evaluate the multiplication first:

        2 + 3 + " hello " + 7 + 2 * 3
                                \---/
        2 + 3 + " hello " + 7 +   6
That may seem like an odd thing to do, but that's what the precedence rule says. We don't evaluate any addition operators until we've first evaluated all of the multiplicative operators. So once we've taken care of the multiplication, we're left with the four addition operators. These will be evaluated from left to right.

In evaluating the first addition, we find ourselves asked to add together two integer values. The overall expression involves a string, but this little subexpression has just two integers. As a result, we perform integer addition.

        2 + 3 + " hello " + 7 +   6
        \---/
          5   + " hello " + 7 +   6
The next addition involves adding the integer 5 to the string literal " hello ". If either of the two operands is a string, then we perform concatenation. So in this case, we convert the integer into a text equivalent ("5") and glue the pieces together to form a new string value:

          5   + " hello " + 7 +   6
          \-------------/
            "5 hello "    + 7 +   6
You might think that Java would add together the 7 and 6 the way we did with the 2 and 3 that were added together to make 5. But it doesn't work that way. The rules of precedence are simple and Java follows them with simple-minded consistency. Precedence tells us that addition operators are evaluated left to right. So first we add the string "5 hello " to 7. That is another combination of a string and an integer, so Java converts the integer to its textual equivalent ("7") and concatenates the two parts together to form a new string:

            "5 hello "    + 7 +   6
            \---------------/
               "5 hello 7"    +   6
Now there is just a single addition to perform which again involves a string/int combination. We convert the integer to its textual equivalent ("6") and concatenate the two parts together to form a new string:

               "5 hello 7"    +   6
               \------------------/
                   "5 hello 76"
Clearly such expressions can be confusing. You can avoid much of this confusion by adding parentheses to the original expression. For example, if we really did want Java to add together the 7 and 6 instead of concatenating them separately, we could have written the original expression in a much clearer way as:

        (2 + 3) + " hello " + (7 + 2 * 3)
Because of the parentheses, Java will evaluate the two numeric parts of this expression first and then concatenate the results with the string in the middle. This expression evaluates to "5 hello 13".

2.4.3 Print versus Println

Java has a variation of the println command called print that allows you to produce output on the current line without going to a new line of output. The println command really does two different things: it sends output to the current line and then it positions to the beginning of a new line. The print command does only the first of these. Thus, a series of print commands will generate output all on the same line. Only a println command will cause the current line to be completed and a new line to be started. For example, consider these six statements:

System.out.print("Hi Ho, "); System.out.print("my man."); System.out.print("Where are you bound? "); System.out.println("My way, I hope."); System.out.print("This is"); System.out.println(" for the whole family!"); These statements produce two lines of output. Remember every println statement produces exactly one line of output. Because there are two println statements here, there are two lines of output. After the first statement executes, the current line looks like this:

        Hi ho,
               ^
The arrow below the output line indicates the position of the output cursor. It is at the end of this line. Notice that it is preceded by a space. That is because the string literal in the print command ends with a space. Java will not insert a space for you unless you specifically request it. After the next print, the line looks like this:

        Hi ho, my man.
                      ^
The output cursor doesn't have a space before it now because the string literal in the print command ends in a period, not a space. After the next print, the line looks like this:

        Hi ho, my man.Where are you bound?
                                            ^
There is no space between the period and the word "Where" because there was no space in the print commands. But the string literal in the third statement has spaces at the end and as a result the output cursor is positioned two spaces after the question mark. After the next statement executes, the output looks like this:

        Hi ho, my man.Where are you bound?  My way, I hope.

        ^
Because this fourth statement is a println command, it finishes the output line and positions the cursor at the beginning of the second line. The next statement is another print that produces this:

        Hi ho, my man.Where are you bound?  My way, I hope.
        This is
               ^
The final println completes the second line and positions the output cursor at the beginning of a new line:

        Hi ho, my man.Where are you bound?  My way, I hope.
        This is for the whole family!

        ^
These six statements are equivalent to these two single statements:

System.out.println("Hi ho, my man.Where are you bound? My way, I hope."); System.out.println("This is for the whole family!"); It seems a bit silly to have both the print and the println commands for producing lines like these, but you will see that there are more interesting applications of print.

It is possible to have empty println command:

System.out.println(); Because there is nothing inside of parentheses to be written to the output line, this positions the output cursor to the beginning of the next line. If there are print commands before this empty println, it finishes out the line made by those print commands. If there are no previous print commands, it produces a blank line. An empty print command is meaningless and is illegal.

2.4.4 Other Operators

In addition to the standard assignment operator, Java has several special operators that are useful for a particular family of operations that are common in programming. You often find yourself increasing a variable by a particular amount which we call incrementing. You also often find yourself decreasing a variable by a particular amount, which we call decrementing. To accomplish this you write statements like the following:

x = x + 1; y = y - 1; z = z + 2; You also often find yourself wanting to double or triple a variable or to reduce its value by a factor of 2, in which case you might write code like the following:

x = x * 2; y = y * 3; z = z / 2; Java has a shorthand for these situations. You glue together the operator character (+, -, *, etc) with the equals to get a special assignment operator (+=, -=, *=, etc). This allows you to rewrite assignments statements like the ones above as follows:

x += 1; y -= 1; z += 2; x *= 2; y *= 3; z /= 2; This is yet another detail to learn about Java, but the code can be clearer to read. Think of a statement like "x += 2" as saying "add 2 to x." That's more concise than saying "x = x + 2."

Java has an even more concise way of expressing this for the particular case where you want to increment by 1 or decrement by 1 in which case you can use the operators "++" and "--". For example, you can say:

x++; y--; There are actually two different forms of each of these because you are also allowed to put the operator in front of the variable:

++x; --y; The two versions of "++" are known as the pre-increment (++x) and post-increment (x++) operators. The two versions of "--" are similarly known as the pre-decrement (--x) and post-decrement (x--) operators. The pre versus post distinction doesn't matter when you include them as statements by themselves, as in these two examples. The difference comes up when you embed these inside of more complex expressions. The post versions evaluate to the old value of the variable, the value it had before the operation was performed. The pre versions evaluate to the new value of the variable, the value it has after the operation is performed.

Consider, for example, the following code fragment:

int x = 10; int y = 20; int z = ++x * y--; What value is z assigned? The answer is 220. The third assignment increments x to 11 and decrements y to 19, but in computing its own value it uses the new value of x (++x) times the old value of y (y--), which is 11 times 20 or 220.

There is a simple mnemonic to remember this. When you see "x++" read it as "give me x, then increment" and read "++x" as "increment, then give me x." Here's another memory device that might help. Just remember that C++ is a bad name for a programming language. The expression "C++" would be interpreted as, "Evaluate to the old value of C and then increment C." In other words, even though you're trying to come up with something new and different, you're really stuck with the old awful language. The language you want is ++C, because then you'd improve the language and you'd get to work with the new and improved language rather than the old one. Some people have suggested that perhaps Java is ++C.

The "++" and "--" operators were first introduced in the C programming language. Java has them because the designers of the language decided to use the syntax of C as the basis for Java syntax. Many languages have made the same choice, including C++ and C#. There is almost a sense of pride among C programmers that these operators allow you to write extremely concise code. Many other people feel that they can make code unnecessarily complex. In this book you will see that I almost always use these operators as a separate statement so that it is obvious what is going on. Now that we've seen a number of new operators, it is worth revisiting the issue of precedence. Below is an updated precedence table including the assignment operators and the increment and decrement operators.

Java Operator Precedence
Description Operators
unary operators ++, --, +, -
multiplicative operators *, /, %
additive operators +, -
assignment operators =, +=, -=, *=, /=, %=

2.4.5 Variables and Types

We have seen that when you declare a variable, you must tell Java what type of value it will be storing. For example, you might declare a variable of type int for integer values or of type double for real values. The situation is fairly clear when you have just integers or just reals, but what happens when you start mixing the types? For example, the following code is clearly okay.

        int x;
        double y;
        x = 2 + 3;
        y = 3.4 * 2.9;
We have an integer variable that we assign an integer value and a double variable that we assign a double value. But what if we try to do it the other way around?

        int x;
        double y;
        x = 3.4 * 2.9;  // illegal
        y = 2 + 3;      // okay
As the comments indicate, you can't assign an integer variable a double value, but you can assign a double variable an integer value. Let's consider the second case first. The expression "2 + 3" evaluates to the integer 5. This isn't a double, but every integer is a real value, so it is easy enough for Java to convert the integer into a double. The technical term is that Java "promotes" the integer into a double.

The other direction is more problematic. The expression "3.4 * 2.9" evaluates to the double value 9.86. This can't be stored in an integer because it isn't an integer. If you wanted to perform this kind of operation, you'd have to tell Java how to convert this into an integer. As described earlier, you can cast to an int, which will truncate anything after the decimal point:

        x = (int) (3.4 * 2.9);  // now legal
This statement first evaluates 3.4 * 2.9 to get 9.86 and then truncates to get the integer 9.

2.5 The For Loop

Programming often involves specifying redundant tasks. The for loop helps to avoid such redundancy. Suppose you want to write out the squares of the first 5 integers. You can say:

        public class WriteSquares {
            public static void main(String[] args) {
                System.out.println(1 + " squared = " + (1 * 1));
                System.out.println(2 + " squared = " + (2 * 2));
                System.out.println(3 + " squared = " + (3 * 3));
                System.out.println(4 + " squared = " + (4 * 4));
                System.out.println(5 + " squared = " + (5 * 5));
            }
        }
This produces the following output:

        1 squared = 1
        2 squared = 4
        3 squared = 9
        4 squared = 16
        5 squared = 25
This is a tedious solution to the problem. The program has five statements that are very similar. They are all of the form:

System.out.println(number + " squared = " + (number * number)); where number is either 1, 2, 3, 4, or 5. The for loop allows you to avoid such redundancy. Here is an equivalent program using a for loop:

        public class WriteSquares2 {
            public static void main(String[] args) {
                for (int i = 1; i <= 5; i++) {
                    System.out.println(i + " squared = " + (i * i));
                }
            }
        }
This program initializes a variable called i to the value 1. Then it repeatedly executes the println statement as long as the variable i is less than or equal to 5. After each println, it evaluates the expression i++ to increment i.

The general syntax of the for loop is as follows:

for (<initialization>; <test>; <update>) { <statement>; <statement>; ... <statement>; } You always include the keyword "for" and the parentheses. Inside the parentheses you have three different parts separated by semicolons: the initialization, the test and the update. Then you have a set of curly braces that enclose a set of statements. The for loop controls these statements inside the curly braces, so we refer to them as the "controlled" statements or the "body" of the loop. The idea is that we execute the body multiple times, as determined by the combination of the other three parts.

The diagram below indicates the steps that Java follows to execute a for loop. It performs whatever initialization you have requested once before the loop begins executing. Then it repeatedly performs the test you have provided. If the test evaluates to true, then it executes the controlled statements once and executes the update part. Then it performs the test again and if it again evaluates to true, it executes the statements again and evaluates the update again. Notice that the update is performed after the controlled statements are executed. When the test evaluates to false, Java is done executing the loop and moves on to whatever statement comes after the loop.

                    +-----------------------------+
                    | perform initialization once |
                    +-----------------------------+
                                   |
                                   V
                           +---------------+
              +----<---no--| is test true? |--yes--->--+
              |            +---------------+           |
              |                ^                       V
              V                |      +-----------------------------------+
              |                |      | execute the controlled statements |
              |                ^      +-----------------------------------+
              V                |                       |
              |                |                       V
              |                ^             +--------------------+
              V                |             | perform the update |
              |                |             +--------------------+
              |                ^                       |
              V                |                       V
              |                |                       |
              |                +---<-----<-------<-----+
              V
    +-------------------+
    | execute statement |
    |  after for loop   |
    +-------------------+
Let's trace the for loop of the WriteSquares2 program.

        initialization: variable i is allocated and initialized to 1

        test: is 1 <= 5?  The answer is yes, so we enter the loop
        statement: we execute the println with i equal to 1
        update: we increment i, which becomes 2

        test: is 2 <= 5?  The answer is yes, so we enter the loop
        statement: we execute the println with i equal to 2
        update: we increment i, which becomes 3

        test: is 3 <= 5?  The answer is yes, so we enter the loop
        statement: we execute the println with i equal to 3
        update: we increment i, which becomes 4

        test: is 4 <= 5?  The answer is yes, so we enter the loop
        statement: we execute the println with i equal to 4
        update: we increment i, which becomes 5

        test: is 5 <= 5?  The answer is yes, so we enter the loop
        statement: we execute the println with i equal to 5
        update: we increment i, which becomes 6

        test: is 6 <= 5?  The answer is no, so we are finished
The for loop is the first example of a control structure, a syntactic structure that controls other statements. Java allows great flexibility in deciding what to include in the initialization part and the update and this allows us to use the for loop to solve all sorts of programming tasks. For right now, we will restrict ourselves to a particular kind of loop that declares and initializes a single variable that is used to control the loop. This variable is often referred to as the control variable of the loop. In the test we compare the control variable against some final desired value and in the update we change the value of the control variable, most often incrementing it by 1. Such loops are very common in programming. By convention, we often use variable names like i, j and k for such loops.

Each execution of the controlled statement of a loop is called an iteration of the loop, as in, "The loop halted after four iterations." Iteration also refers to looping in general, as in, "I solved the problem using iteration."

Consider another for loop:

        for (int i = -100; i <= 100; i++) {
            System.out.println(i + " squared = " + (i * i));
        }
This loop executes a total of 201 times producing the squares of all the integers between -100 and +100 inclusive. The values used in the initialization and the test, then, can be any integers. They can, in fact, be arbitrary integer expressions:

        for (int i = (2 + 2); i <= (17 * 3); i++) {
            System.out.println(i + " squared = " + (i * i));
        }
This loop will generate the squares between 4 and 51 inclusive. The parentheses around the expressions are not necessary, but improve readability. Consider the following loop:

        for (int i = 1; i <= 30; i++) {
            System.out.println("+--------+");
        }
This loop generates 30 lines of output, all exactly the same. This loop is slightly different from the previous one because the statement controlled by the for loop makes no reference to the control variable. Thus:

        for (int i = -30; i <= -1; i++) {
            System.out.println("+--------+");
        }
generates exactly the same output. The behavior of such a loop is determined solely by the number of iterations it performs. The number of iterations is given by:

        (ending value) - (starting value) + 1
It is much simpler to see that the first of these loops iterates 30 times, so it is better to use the simpler form. In general, if we want a loop to iterate exactly n times, we will use one of two standard loops. The first standard form looks like the ones we have seen above:

for (int <variable> = 1; <variable> <= n; i++) { <statement>; <statement>; ... <statement>; } It's pretty clear that this loop executes n times because it starts at 1 and continues as long as it is less than or equal to n. Often, however, it is more convenient to start our counting at 0 instead of 1. That requires a change in the loop test to allow us to stop when n is 1 less:

for (int <variable> = 0; <variable> < n; i++) { <statement>; <statement>; ... <statement>; } Notice that in this form when we initialize the variable to 0, we test whether it is strictly less than n. Either form will execute exactly n times, although we will see some situations where the 0-based loop works better.

Let's consider some borderline cases. What happens if you say:

        for (int i = 1; i <= 1; i++) {
            System.out.println("+--------+");
        }
According to our rule, it should iterate once and it does. It initializes the variable i to 1 and tests to see if this is less than or equal to 1, which it is. So it executes the println, increments i and tests again. The second time it tests, it finds that i is no longer less than or equal to 1, so it stops executing. What about this loop:

        for (int i = 1; i <= 0; i++) {
            System.out.println("+--------+");
        }
This loop performs no iterations at all. It will not cause an execution error; it just won't execute the controlled statement. The variable is initialized to 1 and we test to see if it is less than or equal to 0. It isn't, so we don't execute the controlled statement at all.

When you construct a for loop, you can include more than one statement inside the curly braces. Consider, for example, the following code:

        for (int i = 1; i <= 20; i++) {
            System.out.println("Hi!");
            System.out.println("Ho!");
        }
This will produce 20 pairs of lines, the first of which has the word "Hi" on it and the second of which has the word "Ho".

When a for loop controls a single statement, you don't have to include the curly braces. The curly braces are required only for situations like the one above where you have more than one statement that you want the loop to control. But the Sun coding convention is to include the curly braces even for a single statement and we follow that convention in the examples in this book. This becomes one less detail to worry about, which is helpful when you're learning Java. The fact that Sun recommends it makes it all the more compelling.

2.5.1 For Loops within For Loops

The for loop controls a statement, and the for loop is itself a statement. This makes the following legal:

        for (int i = 1; i <= 10; i++) {
            for (int j = 1; j <= 5; j++) {
                System.out.println("Hi there.");
            }
        }
This is probably easier to read from the inside out. The println statement produces a single line of output. The j loop executes this statement 5 times, producing 5 lines of output. The outer loop executes the inner loop 10 times, which produces 10 sets of 5 lines, or 50 lines of output. The code above, then, is equivalent to:

        for (int i = 1; i <= 50; i++) {
            System.out.println("Hi there.");
        }
This example shows that a for loop can be controlled by another for loop. Such a loop is called a nested loop. This example wasn't very interesting because the nested loop can be eliminated. Let's look at a more interesting nested loop that does something useful:

        for (int i = 1; i <= 6; i++) {
            for (int j = 1; j <= 10; j++) {
                System.out.print("*");
            }
            System.out.println();
        }
When you write code that involves nested loops, you have to be careful to indent the code just right to make the structure clear. At the outermost level, the code above is a simple for loop that executes 6 times:

        for (int i = 1; i <= 6; i++) {
            ...
        }
We use indentation for the statements inside this for loop to make it clear that they are the body of this loop. Inside we find two statements: another for loop and a println. Let's look at the inner for loop:

        for (int j = 1; j <= 10; j++) {
            System.out.print("*");
        }
This loop is controlled by the outer for loop, which is why it is indented, but it itself controls a statement (the print statement), so we end up with another level of indentation to indicate that the print is controlled by the inner for loop, which in turn is controlled by the outer for loop.

So what does this inner loop do? It prints ten stars on the current line of output. They all appear on the same line of output because we are using a print instead of a println. Notice that after this loop we perform a println:

        System.out.println();
The net effect of the for loop followed by the println is that we get a line of output with 10 stars on it. But remember that these statements are contained in an outer loop that executes 6 times. So we end up getting 6 lines of output, each with 10 stars:

        **********
        **********
        **********
        **********
        **********
        **********
Let's examine one more variation. In the code above, the inner for loop always does exactly the same thing. It prints exactly 10 stars on a line of output. But what happens if we change the test for the inner for loop to make use of the outer for loop's control variable (i)?

        for (int i = 1; i <= 6; i++) {
            for (int j = 1; j <= i; j++) {
                System.out.print("*");
            }
            System.out.println();
        }
The outer for loop has the same structure as before. We use the control variable i which is initialized to 1 and which we increment until it becomes larger than 6. In other words, the outer loop causes us to do the following:

        execute the controlled statements with i equal to 1
        execute the controlled statements with i equal to 2
        execute the controlled statements with i equal to 3
        execute the controlled statements with i equal to 4
        execute the controlled statements with i equal to 5
        execute the controlled statements with i equal to 6
In this case, the inner for loop prints "i" number of stars on a line. So when we execute the statements with i equal to 1, we get a line of output with 1 star on it. When we execute the statements with i equal to 2, we get a line of output with 2 stars on it. And so on. In other words, this code produces a triangle as output:

        *
        **
        ***
        ****
        *****
        ******

2.5.2 Scope, Local Variables and Loops

Now that we are nested statements, we need to understand the concept of scope.

Scope of a declaration

The part of a program in which a particular declaration is valid.

We have seen that when it comes to declaring static methods, we can put them in any order whatsoever. The scope of a static method is the entire class in which it appears. Variables work differently. The simple rule is that the scope of a variable declaration extends from the point where it is declared to the right curly brace that encloses it. In other words, find the pair of curly braces that directly enclose the variable declaration. The scope of the variable is from the point where it is declared to the closing curly brace.

There are several implications of this scope rule. Consider first what it means for different methods. Each method has its own set of curly braces to indicate the statements to be executed when the method is called. If variables are declared inside of a method's curly braces, then those variables won't be available outside the method. We refer to such variables as local variables and we refer to this process as localizing variables. We consider this a good programming practice. In general, we want to declare variables in the most local scope possible.

Local Variable

A variable declared inside a method that is accessible only in that method.

Localizing Variables

Declaring variables in the innermost (most local) scope possible.

You might wonder why we would want to localize variables to just one method. Why not just declare everything in one outer scope? That certainly seems simpler. The idea is similar to the use of refrigerators in dormitories. Every dorm room can have its own refrigerator for use in that room. If you are outside of a room, you don't even know that it has a refrigerator in it. The contents of the room are hidden from you.

Localizing variables leads to some duplication and confusion, but provides more security. Our programs use variables to store values just as students use refrigerators to store beer, ice cream, and other valuables. The last time I was in a dorm I noticed that most of the individual rooms had refrigerators in them. This seems terribly redundant, but the reason is obvious. If you want to guarantee the security of something, you put it where nobody else can get it. You will use local variables in much the same way. Each individual method will have its own local variables to use, which means you don't have to consider possible interference from other parts of the program.

Let's look at a simple example involving two methods.

        public static void getStarted() {
            int x = 3;
            int y = 7;
            doIt();
        }

        public static void doIt() {
            int sum = x + y;  // illegal because x and y are not visible
            System.out.println("sum = " + sum);
        }
In this example, the method getStarted declares local variables x and y and gives them initial values. Then it calls method doIt. Inside of method doIt we try to make use of the values of x and y to compute a sum. But this doesn't work. The variables x and y are local to the getStarted method and aren't visible inside of the doIt method. In the next chapter we will see a technique for allowing one method to pass a value to another.

Scope rules are important to understand when we talk about the local variables of one method versus another method, but they also have implications for what happens inside a single method. We have seen that curly braces are used to group together a series of statements. But we can have curly braces inside of curly braces and this leads to some scope issues. For example, consider the following code:

        for (int i = 1; i <= 5; i++) {
            int squared = i * i;
            System.out.println(i + " squared = " + squared);
        }
This is a variation of the code we looked at earlier in the chapter to print out the squares of the first 5 integers. In this version, we use a variable called "squared" to keep track of the square of the for loop control variable. This code works fine, but consider this variation:

        for (int i = 1; i <= 5; i++) {
            int squared = i * i;
            System.out.println(i + " squared = " + squared);
        }
        System.out.println("Last square = " + squared);  // illegal
This code generates a compiler error. The variable called squared is declared inside the for loop. In other words, the curly braces that contain it are the curly braces for the loop. It can't be used outside this scope. So when we attempt to refer to it outside the loop, we get a compiler error.

If for some reason you need to write code like this that accesses the variable after the loop, you'd have to declare the variable in the outer scope before the loop:

        int squared;  // declaration is now in outer scope
        for (int i = 1; i <= 5; i++) {
            squared = i * i;  // change this to an assignment statement
            System.out.println(i + " squared = " + squared);
        }
        System.out.println("Last square = " + squared);  // now legal
There are a few special cases for scope and the for loop is one of them. When a variable is declared in the initialization part of a for loop, then its scope is just the for loop itself (the 3 parts in the for loop header and the statements controlled by the for loop). That means that you can use the same variable name in multiple for loops:
        for (int i = 1; i <= 10; i++) {
            System.out.println(i + " squared = " + (i * i));
        }
        for (int i = 1; i <= 10; i++) {
            System.out.println(i + " cubed = " + (i * i * i));
        }
The variable i is declared twice, but because the scope of each is just the for loop in which it is declared, this isn't a problem. This is like having two dorm rooms, each with its own refrigerator. Of course, we can't do this with nested for loops:

        for (int i = 1; i <= 5; i++) {
            for (int i = 1; i <= 10; i++) {
                System.out.println("hi there.");
            }
        }
This code won't compile. When Java encounters the inner for loop, it will complain that the variable i has already been declared within this scope. You aren't allowed to declare the same variable twice within the same scope. That would be like having two refrigerators in the same dorm room.

In all of the for loop examples we have looked at, the control variable is declared in the initialization part of the loop. This isn't a requirement.

You can separate the declaration of the variable from the initialization of the variable, as in the following.

        int i;
        for (i = 1; i <= 5; i++) {
            System.out.println(i + " squared = " + (i * i));
        }
In effect, we have taken the "int i" part out of the loop itself and put it outside the loop. That means that its scope is greater than it was before. Its scope extends to the end of the enclosing set of curly braces. One advantage of this approach is that we can refer to the final value of the control variable after the loop. Normally we wouldn't be able to do so because its scope would be limited to the loop itself. But this is a dangerous practice that you will generally want to avoid. It also provides a good example of the problems you can encounter when you don't localize variables. Consider the following code, for example:
        int i;
        for (i = 1; i <= 5; i++) {
            for (i = 1; i <= 10; i++) {
                System.out.println("hi there.");
            }
        }
As noted above, you shouldn't use the same control variable when you have nested loops. But unlike the previous example, this one actually compiles. So instead of getting a helpful error message from the Java compiler, you get a program with a bug in it. You'd think from reading these loops that it produces 50 lines of output but it actually produces just 10 lines of output. The inner loop increments the variable i until it becomes 11 and that causes the outer loop to terminate after just one iteration. It can be even worse. If you reverse the order of these loops:

        int i;
        for (i = 1; i <= 10; i++) {
            for (i = 1; i <= 5; i++) {
                System.out.println("hi there.");
            }
        }
You get something known as an infinite loop.
Infinite Loop

A loop that never terminates.

This loop is infinite because no matter what the outer loop does to the variable i, the inner loop always sets it back to 1 and iterates until it becomes 6. Then the outer loop increments the variable to 7 and finds that 7 is less than or equal to 10, so it always goes back to the inner loop which once again sets the variable back to 1 and iterates up to 6. This goes on indefinitely. These are the kinds of interference problems you can get when you fail to localize variables.

2.5.3 Programming with Pseudocode

The programs that you write describe a particular algorithm for solving a problem.

Algorithm

A precise step-by-step description of how to solve a problem.

As you develop more and more complex algorithms, you will want to make use of the technique of writing pseudocode.

Pseudocode

English-like descriptions of algorithms. Programming with pseudocode involves successively refining an informal description until it is easily translated into Java.

For example, you can describe the problem of drawing a box as:

        draw a box with 50 lines and 30 columns of asterisks.
While this describes the figure, it is not specific about how to draw it, what algorithm to use. Do you draw the figure line-by-line or column-by-column? In Java, figures like these must be generated line by line because once a println has been performed on a line of output, that line cannot be changed. There is no command for going back to a previous line in an output file. Therefore, the first line must be output in its entirety first, then the second line in its entirety, and so on. That means your decompositions for these figures will be line-oriented at the top level. Thus, a closer approximation is:

        for (each of 50 lines) {
            draw a line of 30 asterisks.
        }
Even this can be made more specific by introducing the idea of writing a single character on the output line versus moving to a new line of output:

        for (each of 50 lines) {
           for (each of 30 columns) {
               write one asterisk on the output line.
            }
           go to a new output line.
        }
Using pseudocode, you can gradually convert an English description into something easily translated into a Java program. The simple examples you have seen so far are hardly worth the application of pseudocode, so you will now examine the problem of generating a more complex figure:

        *********
         *******
          *****
           ***
            *
This figure must also be generated line by line:

        for (each of 5 lines) {
            draw one line of the triangle.
        }
Unfortunately, each line is different. Therefore, you must come up with a general rule that fits all lines. The first line of this figure has a series of asterisks on it with no leading spaces. The subsequent lines have a series of spaces followed by a series of asterisks. Using your imagination a bit, you can say that the first line has 0 spaces on it followed by a series of asterisks. This allows you to write a general rule for making this figure:

        for (each of 5 lines) {
            write some spaces (possibly 0) on the output line.
            write some asterisks on the output line.
            go to a new output line.
        }
In order to proceed, you must determine a rule for the number of spaces and a rule for the number of asterisks. Assuming that the lines are numbered 1 through 5 and looking at the figure, you can fill in the following chart:

Line Spaces Asterisks
109
217
325
433
541

You want to find a relationship between line number and the other two columns. This is simple algebra, because these columns are related in a linear way. The second column is easy to get from Line, it equals (line - 1). The third column is a little tougher. Because it goes down by 2 every time and the first column goes up by 1 every time, you need a multiplier of -2. Then you need an appropriate constant. The number 11 seems to do the trick, so that the third column equals (11 - 2 * line). You can improve your pseudocode, then, as follows:

        for line going 1 to 5 {
            write (line - 1) spaces on the output line.
            write (11 - 2 * line) asterisks on the output line.
            go to a new output line.
        }
This is simple to turn into a program:

        public class DrawV {
            public static void main(String[] args) {
                for (int line = 1; line <= 5; line++) {
                    for (int column = 1; column <= (line - 1); column++) {
                        System.out.print(" ");
                    }
                    for (int column = 1; column <= (11 - 2 * line); column++) {
                        System.out.print("*");
                    }
                    System.out.println();
                }
            }
        }

2.5.4 For Loop Variations

How would you produce this figure?

            *
           ***
          *****
         *******
        *********
You could follow the same process you did above and find new expressions that produce the appropriate number of spaces and asterisks. However, there is an easier way. This figure is the same as the previous one, except the lines appear in reverse order. We can achieve this result by running the for loop backwards. So instead of starting at 1 and going up to 5 with a "++" update, we can start at 5 and go down to 1 using a "--" update.

For example, the following loop:

        for (int i = 10; i >= 1; i--) {
            System.out.println(i + " squared = " + (i * i));
        }
will produce the squares of the first ten integers, but in reverse order. The simple way to produce the upward-pointing triangle, then, is:

        public class DrawCone {
            public static void main(String[] args) {
                for (int line = 5; line >= 1; line--) {
                    for (int column = 1; column <= (line - 1); column++) {
                        System.out.print(" ");
                    }
                    for (int column = 1; column <= (11 - 2 * line); column++) {
                        System.out.print("*");
                    }
                    System.out.println();
                }
            }
        }
There are many variations on the basic for loop. For example, suppose that we want to write out the squares of the even numbers between 2 and 20. We could say:
        for (int i = 1; i <= 10; i++) {
            System.out.println(2 * i + " squared = " + (2 * i * 2 * i));
        }
Alternatively, we could have our control variable increase in value by 2 each time instead of increasing by 1 each time:
        for (int i = 2; i <= 20; i += 2) {
            System.out.println(i + " squared = " + (i * i));
        }

2.5.5 Magic Numbers and Class Constants

The DrawCone program in the last section draws a cone with 5 lines. How would you modify it to produce a cone with 3 lines? One simple strategy is to change all the 5's to 3's, which will produce this output:

          *****
         *******
        *********
This is obviously wrong. If you work through the geometry of the figure, you will discover that the problem is with the number 11 in one of the expressions. The number 11 comes from this formula:

        2 * (number of lines) + 1
Thus, for 5 lines the appropriate value is 11. But for 3 lines the appropriate value is 7. Programmers call numbers like these magic numbers. They are magic in the sense that they seem to make the program work, but their definition is not always obvious. Glancing at the program, one is apt to ask, "Why 5? Why 11? Why 3? Why 7? Why me?"

To make programs more readable and more adaptable, you should try to avoid magic numbers whenever possible. You do so by storing the magic numbers. You can use variables to store these values, but that is misleading, given that you are trying to represent values that don't change. Java offers an alternative. You can create objects that are guaranteed to have constant values. Not surprisingly, they are called "constants."

The first advantage of a constant is that you can name it. This allows you to choose a descriptive name that explains what the constant represents. You can then use that name instead of referring to the specific value to make your programs more readable and adaptable. For example, in the DrawCone program you might want to introduce a constant called LINES that will replace the magic number 5 (recall from chapter 1 that we use all uppercase letters for constant names). Also, you can use the constant as part of an expression to calculate a value. This allows you to replace the magic number 11 with a formula like (2 * LINES + 1).

Constants are declared with the keyword "final" which indicates the fact that their values cannot be changed once assigned. You can declare them anywhere you can declare a variable, as in:

final int LINES = 5; These values can be declared inside a method just like a variable, but they are often used by several different methods. As a result, we generally declare constants outside of methods. This causes us to have another run-in with our old pal the "static" keyword. If we want to declare a constant that our static methods can access, then the constant itself has to be static. And just as we declare our methods to be public, we usually declare our constants to be public. The general syntax for constant definitions appears below.

public static final <type> <identifier> = <expression>; For example, below are definitions for two constants.

public static final int HEIGHT = 10; public static final int WIDTH = 20; These definitions create constants called HEIGHT and WIDTH that will always have the values 10 and 20. These are known as class constants because we declare them in the outermost scope of the class, along with the methods of the class. That way they are visible in each method of the class.

How would you rewrite the DrawCone program with a constant to eliminate the magic numbers? You would introduce a constant for the number of lines:

public static final int LINES = 5; Next, you replace the 5 in the outer loop with this constant. Then, you replace the 11 in the second inner loop with the expression (2 * LINES + 1). The result is the following program.

        public class DrawCone2 {
            public static final int LINES = 5;
        
            public static void main(String[] args) {
                for (int line = LINES; line >= 1; line--) {
                    for (int column = 1; column <= (line - 1); column++) {
                        System.out.print(" ");
                    }
                    for (int column = 1; column <= (2 * LINES + 1 - 2 * line); column++) {
                        System.out.print("*");
                    }
                    System.out.println();
                }
            }
        }
The advantage of this program is that it is more readable and more adaptable. You can make a simple change to the constant LINES to make it produce a different size figure.

2.5.6 Case Study: A Complex Figure

Now consider an example that is even more complex. To solve it, we will go through three basic steps:

  1. Decompose the task into subtasks, each of which will become a static method.
  2. For each subtask, make a table for the figure and compute formulas for each column of the table in terms of the line number.
  3. Convert the tables into actual for loop code for each method.
The output we want to produce is the following.


        +------+
        |\    /|
        | \  / |
        |  \/  |
        |  /\  |
        | /  \ |
        |/    \|
        +------+
In order to generate this figure, you have to first break it down into subfigures. In doing so, you should look for lines that are similar in one way or another. The first and last lines are exactly the same. The three lines after the first line all fit one pattern, and the three lines after that fit another. Thus, you can break the problem down as:

        draw a solid line.
        draw the top half of the hourglass.
        draw the bottom half of the hourglass.
        draw a solid line.
You should solve each independently. Before you do so, however, you should think a moment about possible magic numbers this figure might generate. It has a specific height and width that might introduce magic numbers. Because of the regularity of the figure, though, the height is determined by the width and vice versa. For example, if you change the height of the two hourglass halves from 3 to 4, you have to increase the overall width from 8 to 10 to make the diagonals line up properly. Therefore, you should define only one constant. Suppose you choose the height of the hourglass halves:

public static final int SUB_HEIGHT = 3; Notice how we use the underscore character to separate the different words in the name of the constant. Given this constant, you can calculate the other magic numbers that are related to size. For example, the overall width of the figure is:

(2 * SUB_HEIGHT + 2) And the number of dashes in a solid line is:

(2 * SUB_HEIGHT) Thus, you can use these expressions to avoid magic numbers. The solid line task can be further specified as:

        write a plus on the output line.
        write (2 * SUB_HEIGHT) dashes on the output line.
        write a plus on the output line.
        go to a new output line.

This translates easily into a static method:


public static void drawLine() { System.out.print("+"); for (int column = 1; column <= (2 * SUB_HEIGHT); column++) { System.out.print("-"); } System.out.println("+"); } The top half of the hourglass is more complex. Here is a typical line:

        | \  / |
This has four printing characters and some spaces that separate them:

         |               \                /             |
        bar  spaces  backslash  spaces  slash  spaces  bar
Thus, a first approximation in pseudocode:

        for (each of SUB_HEIGHT lines) {
            write a bar on the output line.
            write some spaces on the output line.
            write a back slash on the output line.
            write some spaces on the output line.
            write a slash on the output line.
            write some spaces on the output line.
            write a bar on the output line.
            go to a new line of output.
        }
Again, you can make a table to figure out the desired expressions. Writing the single characters will be easy enough to translate into Java, but you need to be more specific about the spaces. This line really has three sets of spaces. Here is a table that shows how many to use in each case:

Line Spaces Spaces Spaces
1040
2121
3202

The first and third sets of spaces fit the rule (line - 1), and the second number of spaces is (6 - 2 * line). But how do you account for possible magic numbers? There are many ways to do so. You could find the expressions for different heights and see how they differ. Or you could analyze the geometry of the figure and try to deduce the expressions. Or you could guess and hope that you are right. Any of those solutions would inevitably lead to the conclusion that the only magic number in the expressions above is 6, which comes from (2 * SUB_HEIGHT). Thus, the correct expressions are: (line - 1) and 2 * (SUB_HEIGHT - line).

Therefore, the pseudocode should read:

        for (line going 1 to SUB_HEIGHT) {
            write a bar on the output line.
            write (line - 1) spaces on the output line.
            write a back slash on the output line.
            write 2 * (SUB_HEIGHT - line) spaces on the output line.
            write a slash on the output line.
            write (line - 1) spaces on the output line.
            write a bar on the output line.
            go to a new line of output.
        }
This is easily translated into a static method. A similar solution exists for the bottom half of the hourglass. Put together, the program looks like this:

        public class DrawFigure {
            public static final int SUB_HEIGHT = 3;
        
            public static void main(String[] args) {
                drawLine();
                drawTop();
                drawBottom();
                drawLine();
            }
        
            // Produces a solid line
            public static void drawLine() {
                System.out.print("+");
                for (int column = 1; column <= (2 * SUB_HEIGHT); column++) {
                    System.out.print("-");
                }
                System.out.println("+");
            }
        
            // This produces the top half of the hourglass figure
            public static void drawTop() {
                for (int line = 1; line <= SUB_HEIGHT; line++) {
                    System.out.print("|");
                    for (int column = 1; column <= (line - 1); column++) {
                        System.out.print(" ");
                    }
                    System.out.print("\\");
                    for (int column = 1; column <= 2 * (SUB_HEIGHT - line); column++) {
                        System.out.print(" ");
                    }
                    System.out.print("/");
                    for (int column = 1; column <= (line - 1); column++) {
                        System.out.print(" ");
                    }
                    System.out.println("|");
                }
            }
        
            // This produces the bottom half of the hourglass figure
            public static void drawBottom() {
                for (int line = 1; line <= SUB_HEIGHT; line++) {
                    System.out.print("|");
                    for (int column = 1; column <= (SUB_HEIGHT - line); column++) {
                        System.out.print(" ");
                    }
                    System.out.print("/");
                    for (int column = 1; column <= 2 * (line - 1); column++) {
                        System.out.print(" ");
                    }
                    System.out.print("\\");
                    for (int column = 1; column <= (SUB_HEIGHT - line); column++) {
                        System.out.print(" ");
                    }
                    System.out.println("|");
                }
            }
        }
This solution may seem cumbersome, but it is easier to adapt to a new task. It would be simple, for example, to modify this program to produce the figure below. All you have to do is reverse the order of the calls on methods drawTop and drawBottom in the main method:

        +------+
        |  /\  |
        | /  \ |
        |/    \|
        |\    /|
        | \  / |
        |  \/  |
        +------+
This program raises an important issue. It declares a constant called SUB_HEIGHT that is used throughout the program. But the constant is not declared locally in the individual methods. That seems to violate our principle of localizing whenever possible. While localizing of variables is a good idea, the same, is not always true for constants. In this case a class-wide definition is more appropriate. We localize variables to avoid potential interference. That argument doesn't hold for constants, since they are guaranteed not to change. Another argument for using local variables is that it makes our static methods more independent. That argument has some merit, but not enough. It is true that class constants introduce dependencies between methods, but often that is really what you want. For example, the three methods of the hourglass program should not be independent of each other when it comes to the size of figures. Each subfigure has to use the same size constant. Imagine the potential disaster if each method had its own SUB_HEIGHT each with a different value. None of the pieces would fit together.

 

2.6 Chapter Summary

2.7 Self-Check Exercises

  1. Which of the following are legal int literals? 22 1.5 -1 2.3 10.0 5. -6875309 '7'
  2. Trace the evaluation of the following expressions, and give their resulting values:
    2 + 3 * 4 - 6
    14 / 7 * 2 + 30 / 5 + 1
    (12 + 3) / 4 * 2
    (238 % 10 + 3) % 7
    (18 - 7) * (43 % 10)
    2 + 19 % 5 - (11 * (5 / 2))
    813 % 100/3 + 2.4
    4.0/2 * 9/2
    2.5 * 2 + 8/5.0 + 10/3
    12/7 * 4.4 * 2/4
    
  3. Trace the evaluation of the following expressions, and give their resulting values:
    (char) ('a' + 5)
    (char) ('z' - 16)
    
  4. Trace the evaluation of the following expressions, and give their resulting values:
    2 + 2 + 3 + 4
    "2 + 2 " + 3 + 4
    2 + " 2 + 3 " + 4
    3 + 4 + " 2 + 2"
    "2 + 2 " + (3 + 4)
    "(2 + 2) " + (3 + 4)
    
  5. Write the code statements to declare int variables named width and height. How many legal ways are there to declare these two variables?
  6. Imagine you are writing a personal fitness program that stores the user's age, gender, height (in feet or meters) and weight (to the nearest pound or kilogram). Declare variables with the appropriate names and types to hold this information.
  7. Imagine you are writing a student program that stores the student's year (Freshman, Sophomore, Junior, or Senior), number of courses, and GPA on a 4.0 scale. Declare variables with the appropriate names and types to hold this information.
  8. Suppose you have an int variable named number. What Java expression produces the last digit of the number (the 1s place)?
  9. Suppose you have an int variable named number. What Java expression produces the second to last digit of the number (the 10s place)? What expression produces the third to last digit of the number (the 100s place)?
  10. Consider the following code: int first = 8; int second = 19; first = first + second; second = first - second; first = first - second; What are the values of first and second at the end of this code?
  11. How would you describe the net effect of the code statements in the previous exercise?
  12. Rewrite the code from the previous exercise to be shorter, by declaring the variables together and by using the special assignment operators such as +=, -=, *= and /= as appropriate.
  13. Rewrite the following code as a series of equivalent println statements (i.e., without any print statements): System.out.print("Twas "); System.out.print("brillig and the"); System.out.println(" "); System.out.print(" slithy toves did"); System.out.print(" "); System.out.println ("gyre and"); System.out.println( "gimble"); System.out.println(); System.out.println( "in the wabe." );
  14. What are the values of a, b, and c after the following code statements? (It may help you to write down their values after each line.) int a = 5; int b = 10; int c = a++ + ++b; a++; b--;
  15. Write for loops to produce the following output:
    ----------------------------------------------------
    aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ
    1122334455667788990011223344556677889900112233445566
    ----------------------------------------------------
    
  16. Write nested for loops to produce the following output:
             |         |         |         |         |         |
    123456789012345678901234567890123456789012345678901234567890
    
  17. Modify your code from the previous exercise so that it could easily be modified to display a different range of numbers (instead of 1234567890), a different number of repetitions of those numbers (instead of 60 total characters) and keep the vertical bars matching up correctly. Use class constants instead of "magic numbers." Example outputs that could be generated by changing your constants would be:
        |    |    |    |    |    |    |    |    |    |
    12340123401234012340123401234012340123401234012340
    
           |       |       |       |       |       |       |
    12345670123456701234567012345670123456701234567012345670
    
  18. Write nested for loops that produce the following output. Indicate the level of each statement in your code using indentation and a comment.
    000111222333444555666777888999
    000111222333444555666777888999
    000111222333444555666777888999
    
  19. Modify the code so that it now produces the following output:
    99999888887777766666555554444433333222221111100000
    99999888887777766666555554444433333222221111100000
    99999888887777766666555554444433333222221111100000
    99999888887777766666555554444433333222221111100000
    99999888887777766666555554444433333222221111100000
    
  20. Modify the code so that it now produces the following output:
    999999999888888887777777666666555554444333221
    999999999888888887777777666666555554444333221
    999999999888888887777777666666555554444333221
    999999999888888887777777666666555554444333221
    
  21. Write a Java method called printDesign that produces the following output. Use for loops to capture the structure of the figure.
    -----1-----
    ----333----
    ---55555---
    --7777777--
    -999999999-
    
  22. Suppose that you are trying to write a program that produces the following output:
    1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21
    1, 3, 5, 7, 9, 11
    
    The following program is an attempt at a solution, but it contains four major errors. Identify them all.
            public class BadNews {
                public static final int MAX_ODD = 21;
            
                public static void writeOdds() {
                    // print each odd number
                    for (int count = 1; count <= (MAX_ODD - 2); count++) {
                        System.out.print(count + ", ");
                        count = count + 2;
                    }
            
                    // print the last odd number
                    System.out.println(count + 2);
                }
            
                public static void main(String[] args) {
                    // write all odds up to 21
                    writeOdds();
            
                    // now, write all odds up to 11
                    MAX_ODD = 11;
                    writeOdds();
                }
            }
  23. What is the output of the following method oddStuff: public static void oddStuff() { int number = 4; for (int count = 1; count <= number; count++) { System.out.println(number); number = number / 2; } }
  24. What is the output of the following method unknown:
            public class Strange {
                public static final int MAX = 5;
            
                public static void unknown() {
                    int number = 0;
            
                    for (int count = MAX; count >= 1; count--) {
                        number += (count * count);
                    }
            
                    System.out.println("The result is: " + number);
                }
            
                public static void main(String[] args) {
                    unknown();
                }
            }
    
  25. What is the output of the following loop? int total = 25; for (int number = 1; number <= (total / 2); number++) { total = total - number; System.out.println(total + " " + number); }
  26. The following loops are indented poorly, making it difficult to know what the code is doing. What is the output of the following sequence of loops? for (int i = 1; i <= 2; i++) { for (int j = 1; j <= 3; j++) { for (int k = 1; k <= 4; k++) { System.out.print("*"); } System.out.print("!"); } System.out.println(); }
  27. What is the output of the following sequence of loops? Notice that it is the same as the previous exercise, except that the placement of the braces has changed. for (int i = 1; i <= 2; i++) { for (int j = 1; j <= 3; j++) { for (int k = 1; k <= 4; k++) { System.out.print("*"); } } System.out.print("!"); System.out.println(); }
  28. Write for loops that produce the following output:
    1 4 9 16 25 36 49 64 81 100
    Use a class constant so that the maximum number can be changed.
  29. Modify your code from the previous exercise so that it does not need to use the * multiplication operator. (It can be done!)
  30. Write a pseudo-code algorithm that will produce the following figure as output.
    +===+===+
    |   |   |
    |   |   |
    |   |   |
    +===+===+
    |   |   |
    |   |   |
    |   |   |
    +===+===+
    
  31. Use your pseudo-code from the previous exercise to write a Java program that produces the preceding figure as output. Use for loops to print the repeated parts of the figure. Once you get it to work, add a class constant so that the size of the figure could be changed simply by changing the constant's value.

2.8 Programming Problems

  1. Write a program that produces the following output:

            ****** //////////// ******
            *****  //////////\\  *****
            ****   ////////\\\\   ****
            ***    //////\\\\\\    ***
            **     ////\\\\\\\\     **
            *      //\\\\\\\\\\      *
                   \\\\\\\\\\\\
    

  2. Write a program that produces the following output:

            +---------+
            |    *    |
            |   /*\   |
            |  //*\\  |
            | ///*\\\ |
            | \\\*/// |
            |  \\*//  |
            |   \*/   |
            |    *    |
            +---------+
            | \\\*/// |
            |  \\*//  |
            |   \*/   |
            |    *    |
            |    *    |
            |   /*\   |
            |  //*\\  |
            | ///*\\\ |
            +---------+
    
  3. Write a program that produces the following output. Use a program constant to make it possible to change the number of stairs in the figure.

                                  O  *******
                                 /|\ *     *
                                 / \ *     *
                             O  ******     *
                            /|\ *          *
                            / \ *          *
                        O  ******          *
                       /|\ *               *
                       / \ *               *
                   O  ******               *
                  /|\ *                    *
                  / \ *                    *
              O  ******                    *
             /|\ *                         *
             / \ *                         *
            ********************************
    

Stuart Reges
Marty Stepp
Last modified: Tue Jan 3 01:53:05 PDT 2006

Chapter 3
Introduction to Parameters and Objects

Copyright © 2005 by Stuart Reges and Marty Stepp

3.1 Introduction

Chapter 2 discussed techniques for solving complex problems and class constants that can add to the flexibility of a program solution. This chapter explores a more powerful technique for obtaining such flexibility. You will learn how to use value parameters to create methods that solve not just single tasks, but whole families of tasks. Creating such methods requires an insight into problems called generalization. It requires looking beyond a specific task to find a more general task for which this is just one instance. The ability to generalize is one of the most important qualities of a software engineer, and the generalization technique you will study in this chapter is one of the most powerful features of Java.

After exploring value parameters, the chapter discusses other issues associated with methods, particularly the ability of a method to return a value. The chapter ends with an introduction to objects and graphics.

3.2 Value Parameters

Humans are very good at learning new tasks. In doing so, we often group a family of related tasks into one generalized solution. For example, someone might ask you to take 10 steps forward or someone might ask you to take 20 steps forward. These are different tasks, but they both involve taking a certain number of steps forward. We think of this as a single task of taking steps forward, but we understand that the number of steps will vary from one task to another. In programming we refer to the number of steps as a parameter that allows you to generalize the task.

Parameter (parameterize)

Any of a set of characteristics that distinguish different members of a family of tasks. To parameterize a task is to identify a set of its parameters.

For a programming example, let's return to the DrawFigure program of chapter 2. It performs its task adequately, but there are several things wrong with it. For example, there are six different places where a for loop writes out spaces. This is redundant and should be consolidated into a single method that performs all space writing tasks.

Each space writing task requires a different number of spaces, so we need some way to tell the method how many spaces to use. The methods we have been writing have a simple calling mechanism where we say:

        writeSpaces();
You could imagine setting a variable to a particular value before the method is called, as in:

        int number = 10;
        writeSpaces();
Then the method could look at the value of the variable number to see how many spaces to write. Unfortunately, this approach won't work. Remember that variables are local to the method in which they are declared. The writeSpaces method wouldn't have access to this variable called number because it is declared outside the method.

We want to be able to somehow include this value in the call, so that if we want to get 10 spaces we say:

        writeSpaces(10);
and if we want to get 20 spaces we say:

        writeSpaces(20);
Value parameters in Java allow us to do exactly this. They allow us to specify one or more parameters to the method. The idea is that instead of writing a method that performs just one version of a task, we write a more flexible version that solves a family of related tasks that all differ by one or more parameters. In the case of the writeSpaces method, the parameter is the number of spaces to write. This number characterizes the different space writing tasks.

Below is the definition of writeSpaces with a parameter for the number of spaces to write.

        public static void writeSpaces(int number) {
            for (int i = 1; i <= number; i++) {
                System.out.print(" ");
            }
        }
The value parameter appears in the method header after the name and inside the parentheses we have been leaving empty. This method uses a parameter called number of type int. As indicated above, you can no longer call the parameterized method by using just its name:

        writeSpaces();
You must now say something like:

        writeSpaces(10);
When a call like this is made, the value 10 is used to initialize the parameter called number. You can think of this as information flowing into the method from the call:
            writeSpaces(10);
                        |
                        V
                        +--->--->--->--->--->---+
                                                |
                                                V
        +----------------------------------------------+
        | public static void writeSpaces(int number) { |
        |     ...                                      |
        | }                                            |
        +----------------------------------------------+
The parameter number is like a local variable, but it gets its initial value from the call. Given that we are calling this method with the value 10, it's as if we had included the following declaration at the beginning of method writeSpaces:
        int number = 10;
Of course, this mechanism is more flexible than a specific variable declaration, because we can instead say:

        writeSpaces(20);
and it will be as if we had said:

        int number = 20;
at the beginning of the method. We can even use an integer expression for the call:

        writeSpaces(3 * 4 - 5);
In this case Java evaluates the expression to get the value 7 and then calls the method initializing number to 7.

Computer scientists use the word parameter liberally to mean both what appears in the method declaration (the formal parameter) and what appears in the method call (the actual parameter).

Formal Parameter

The variable that appears in the header of a method declaration and which is used to generalize the method's behavior.

Actual Parameter

A specific value or expression that appears in a method call and which is used to perform a specific task from the family of tasks.

The term formal parameter is not very descriptive of its purpose. A better name would be "generalized parameter." In the method above, number is the generalized parameter that appears in the method declaration. It is a placeholder for some unspecified value. The values appearing in the method calls are the actual parameters, because each call indicates a specific task to perform. In other words, each call provides an actual value to fill the placeholder.

The word "argument" is often used as a synonym for "parameter," as in, "These are the arguments I'm passing to this method." Some people prefer to reserve the word "argument" for actual parameters and the word "parameter" for formal parameters.

Let's look at an example of how you might use this writeSpaces method. Remember that the DrawFigure program had the following method called drawTop:

        public static void drawTop() {
            for (int line = 1; line <= SUB_HEIGHT; line++) {
                System.out.print("|");
                for (int column = 1; column <= (line - 1); column++) {
                    System.out.print(" ");
                }
                System.out.print("\\");
                for (int column = 1; column <= 2 * (SUB_HEIGHT - line); column++) {
                    System.out.print(" ");
                }
                System.out.print("/");
                for (int column = 1; column <= (line - 1); column++) {
                    System.out.print(" ");
                }
                System.out.println("|");
            }
        }
Using the writeSpaces method we can rewrite this as follows:

        public static void drawTop() {
            for (int line = 1; line <= SUB_HEIGHT; line++) {
                System.out.print("|");
                writeSpaces(line - 1);
                System.out.print("\\");
                writeSpaces(2 * (SUB_HEIGHT - line));
                System.out.print("/");
                writeSpaces(line - 1);
                System.out.println("|");
            }
        }
Notice that we call writeSpaces three different times, specifying how many spaces we want in each case. Similarly, we could also modify the drawBottom method from the DrawFigure program to simplify it.

3.2.1 The Mechanics of Value Parameters

When the computer calls a method, it initializes its value parameters. For each value parameter it first evaluates the expression passed as the actual parameter and then uses the result to initialize a local variable whose name is given by the formal parameter. This is best explained by example.


        public class ValueParameterExample {
            public static void main(String[] args) {
                int first = 8;
                int second = 10;
                System.out.print("*");
                writeSpaces(first);
                System.out.println("*");
                System.out.print("!");
                writeSpaces(second);
                System.out.println("!");
                System.out.print("'");
                writeSpaces(30);
                System.out.println("'");
                System.out.print("<");
                writeSpaces(first * second - 30);
                System.out.println(">");
            }
        
            // Writes "number" spaces on the current output line to System.out
            public static void writeSpaces(int number) {
                for(int i = 1;  i <= number; i++) {
                    System.out.print(" ");
                }
            }
        }
In the first two lines of method main the computer finds instructions to allocate and initialize two variables:

              +---+            +----+
        first | 8 |     second | 10 |
              +---+            +----+
The next three lines of code:

        System.out.print("*");
        writeSpaces(first);
        System.out.println("*");
produce an output line with 8 spaces bounded by asterisks on either side. You can see where the asterisks come from, but look at the method call that produces the spaces. When the computer activates the block for method writeSpaces, it must set up its value parameter. To set up the value parameter, it first evaluates the expression being passed as the actual parameter. The expression is simply the variable first, which has the value 8. Therefore, the expression evaluates to 8. The computer uses this result to initialize a local variable called number.

The diagram below indicates how memory would look as we enter method writeSpaces the first time. Because there are two methods involved (main which calls writeSpaces), the diagram indicates which variables are local to main (first and second) and which are local to writeSpaces (the parameter number).


        +-------------------------------+   +--------------------+
        | method main                   |   | method writeSpaces |
        |       +---+            +----+ |   |        +---+       |
        | first | 8 |     second | 10 | |   | number | 8 |       |
        |       +---+            +----+ |   |        +---+       |
        +-------------------------------+   +--------------------+
The net effect of this process is that you have a local copy of the variable first. On exiting the method, the computer deallocates the value parameter:

        +-------------------------------+
        | method main                   |
        |       +---+            +----+ |
        | first | 8 |     second | 10 | |
        |       +---+            +----+ |
        +-------------------------------+
You finish this line of output with the println that appears after the method call. The next line starts by writing an exclamation mark at the left margin and then writeSpaces is called again, this time with the variable second as its actual parameter. The computer evaluates this expression obtaining the result 10. This value is used to initialize number. Thus, this time it creates a copy of variable second:

        +-------------------------------+   +--------------------+
        | method main                   |   | method writeSpaces |
        |       +---+            +----+ |   |        +----+      |
        | first | 8 |     second | 10 | |   | number | 10 |      |
        |       +---+            +----+ |   |        +----+      |
        +-------------------------------+   +--------------------+
Because number has a different value this time (10 instead of 8), the method produces a different number of spaces. After this execution the computer again deallocates the local objects:

        +-------------------------------+
        | method main                   |
        |       +---+            +----+ |
        | first | 8 |     second | 10 | |
        |       +---+            +----+ |
        +-------------------------------+
The output line is finished with the println after the method call and the computer starts the third line of output. It writes a single quote mark at the left margin and then activates method writeSpaces again. This time it uses the integer literal 30 as the expression, which means it makes the value parameter number into a copy of it:

        +-------------------------------+   +--------------------+
        | method main                   |   | method writeSpaces |
        |       +---+            +----+ |   |        +----+      |
        | first | 8 |     second | 10 | |   | number | 30 |      |
        |       +---+            +----+ |   |        +----+      |
        +-------------------------------+   +--------------------+
Again, the method will behave differently because of the different value of number. Finally, for the fourth call to method writeSpaces, the computer must evaluate a complex expression with arithmetic operators:

        first * second - 30 = (8 * 10) - 30 = 80 - 30 = 50
The computer uses this result to initialize number:

        +-------------------------------+   +--------------------+
        | method main                   |   | method writeSpaces |
        |       +---+            +----+ |   |        +----+      |
        | first | 8 |     second | 10 | |   | number | 50 |      |
        |       +---+            +----+ |   |        +----+      |
        +-------------------------------+   +--------------------+
Thus, now number is a copy of the value described by this complex expression. Therefore, the total output of this program is:

        *        *
        !          !
        '                              '
        <                                                  >

3.2.2 Limitations of Value Parameters

We've seen that a value parameter can be used to provide input to a method. You can use a parameter to send a value into a method. Unfortunately, you can't use a parameter to get a value out of a method. Let's see why.

When a value parameter is set up, a local variable is created and is initialized to the value being passed as the actual parameter. The net effect is that the local variable is a copy of the value coming from the outside. Since it is a local variable, it can't influence any variables outside the method. Consider the following sample program.

        public class ValueParameterExample2 {
            public static void main(String[] args) {
                int x = 17;
                doubleNumber(x);
                System.out.println("x = " + x);
        
                int number = 42;
                doubleNumber(number);
                System.out.println("number = " + number);
            }
        
            public static void doubleNumber(int number) {
                System.out.println("Initial value of number = " + number);
                number *= 2;
                System.out.println("Final value of number = " + number);
            }
        }
This program begins by declaring and initializing an integer variable called x with value 17.

        +-------------+
        | method main |
        |   +----+    |
        | x | 17 |    |
        |   +----+    |
        +-------------+
It then calls the method doubleNumber, passing x as a parameter. The value of x is used to initialize the parameter number as a local variable of the method called doubleNumber:

        +-------------+   +---------------------+
        | method main |   | method doubleNumber |
        |   +----+    |   |        +----+       |
        | x | 17 |    |   | number | 17 |       |
        |   +----+    |   |        +----+       |
        +-------------+   +---------------------+
We then execute the statements inside of doubleNumber. It begins by printing the initial value of number (17). Then it doubles number:

        +-------------+   +---------------------+
        | method main |   | method doubleNumber |
        |   +----+    |   |        +----+       |
        | x | 17 |    |   | number | 34 |       |
        |   +----+    |   |        +----+       |
        +-------------+   +---------------------+
Notice that this has no effect on the variable x. The parameter called number is a copy of x, so even though they started out the same, the change to number has no effect on x. Then we report the new value of number (34).

At this point, method doubleNumber finishes executing and we return to main.

        +-------------+
        | method main |
        |   +----+    |
        | x | 17 |    |
        |   +----+    |
        +-------------+
We report the value of x, which is 17. Then we declare and initialize a variable called number with value 42:

        +----------------------------+
        | method main                |
        |   +----+            +----+ |
        | x | 17 |     number | 42 | |
        |   +----+            +----+ |
        +----------------------------+
And we once again call doubleNumber, this time passing it the value of number. This is an odd situation because the parameter has the same name as the variable in main. But Java doesn't care that they have the same name. It always creates a new local variable for method doubleNumber:

        +----------------------------+   +---------------------+
        | method main                |   | method doubleNumber |
        |   +----+            +----+ |   |        +----+       |
        | x | 17 |     number | 42 | |   | number | 42 |       |
        |   +----+            +----+ |   |        +----+       |
        +----------------------------+   +---------------------+
So at this point in time, there are two different variables called number, one in each method. We then execute the statements of doubleNumber. We first report the value of number (42). Then we double it:

        +----------------------------+   +---------------------+
        | method main                |   | method doubleNumber |
        |   +----+            +----+ |   |        +----+       |
        | x | 17 |     number | 42 | |   | number | 84 |       |
        |   +----+            +----+ |   |        +----+       |
        +----------------------------+   +---------------------+
Again, notice that doubling number inside of doubleNumber has no effect on the original variable number that is in method main. These are separate variables. The method then reports the new value of number (84) and returns to main.

        +----------------------------+
        | method main                |
        |   +----+            +----+ |
        | x | 17 |     number | 42 | |
        |   +----+            +----+ |
        +----------------------------+
The program then reports the value of number and terminates. So the overall output for the program is as follows.

        Initial value of number = 17
        Final value of number = 34
        x = 17
        Initial value of number = 42
        Final value of number = 84
        number = 42
Thus, the local manipulations of the value parameter do not change these variables on the outside. The use of copies is an important property of value parameters. A variable passed as the actual parameter to a value parameter is guaranteed not to change as a result of manipulations to the value parameter. But it does mean that while value parameters will allow us to send information into a method, they won't allow us to get information back out of a method.

3.2.3 More Value Parameter Details

So far the discussion of value parameter syntax has been informal. It's about time that we wrote down more precisely what syntax we are using to declare static methods.

public static void <name>(<type> <name>, ..., <type name>) { <statement or variable declaration>; <statement or variable declaration>; ... <statement or variable declaration>; } This template indicates that we can declare as many value parameters as we want inside the parentheses that appear after the name of a method in its header. We use commas to separate different parameters.

As an example of a method with multiple parameters, let's consider a variation of writeSpaces. It is convenient that we can tell it a different number of spaces to write, but it always writes spaces. What if we want 18 asterisks or 23 periods or 17 question marks? We can generalize the task even further by having the method take two parameters: both a character and a number of times to write that character.

        public static void writeChars(char ch, int number) {
            for (int i = 1;  i <= number; i++) {
                System.out.print(ch);
            }
        }
Recall that character literals are enclosed in single quotation marks.

Using this method we can write code like the following:

        writeChars('=', 20);
        System.out.println();
        for(int i = 1; i <= 10; i++) {
            writeChars('>', i);
            writeChars(' ', 20 - 2 * i);
            writeChars('<', i);
            System.out.println();
        }
        writeChars('=', 20);
        System.out.println();
which produces the following output:

        ====================
        >                  <
        >>                <<
        >>>              <<<
        >>>>            <<<<
        >>>>>          <<<<<
        >>>>>>        <<<<<<
        >>>>>>>      <<<<<<<
        >>>>>>>>    <<<<<<<<
        >>>>>>>>>  <<<<<<<<<
        >>>>>>>>>><<<<<<<<<<
        ====================
You can include as many parameters as you want when you define a method. Each method call must provide exactly that number of parameters. They are lined up sequentially. For example, consider the first call on writeChars in the code fragment above versus the header for writeChars. Java lines these two up in sequential order (the first actual parameter going into the first formal parameter, the second actual parameter going into the second formal parameter):


           writeChars('=', 20);
                       |    |
                       |    V
                       |    +--->--->--->--->--->--->---+
                       V                                |
                       +--->--->--->--->--->-+          |
                                             |          |
                                             V          V
        +------------------------------------------------------+
        | public static void writeChars(char ch, int number) { |
        |     ...                                              |
        | }                                                    |
        +------------------------------------------------------+

3.2.4 Value Parameters versus Class Constants

How does this new technique relate to what you already know? The most important technique you learned in chapter 2 for creating program flexibility is the use of class constants. By using such constants, you make it easy to modify a program. The value parameter provides much of the same flexibility, and more. Consider the writeSpaces procedure. Suppose you wrote it using a class constant:

public static final int NUMBER_OF_SPACES = 10; This gives you the flexibility to produce a different number of spaces, but has one major limitation. The constant can change only from execution to execution. It cannot change within a single execution. In other words, you can execute the program once with one value, and then execute it again with a different value. But you can't use different values in a single execution of the program using a class constant.

Value parameters are more flexible. Because you specify the value to be used each time you call the method, you can use several different values in a single program execution. As you have seen, you can call it many different times within a single program execution and have it behave differently every time. At the same time, however, the value parameter is more work for the programmer than the class constant. It makes your method headers and method calls more tedious, not to mention making the execution (and, thus, the debugging) more complex.

Therefore, you will probably find occasion to use each technique. The basic rule is to use a class constant when you only want to change the value from execution to execution. If you want to use different values within a single execution, use the value parameter.

3.3 Methods that Return Values

Value parameters allow you to send a value into a method, but how do you get a value back out? Java provides a mechanism for returning a value from a method using a special "return" statement. For example, here is a method that takes a distance specified as a number of feet and that returns the corresponding number of miles.

public static double miles(double feet) { return feet / 5280.0; } First, notice that in the header for the method the familiar word "void" has been replaced with the word "double." When you declare a method that returns a value, you have to tell Java what kind of value it will return. In fact, the keyword "void" simply means "no return value". We can update our syntax template for static methods once more to include the fact that the header includes a return type (void for none):

public static <type> <name>(<type> <name>, ..., <type name>) { <statement or variable declaration>; <statement or variable declaration>; ... <statement or variable declaration>; } The syntax of the return statement is:

return <expression>; When Java encounters a return statement, it evaluates the given expression and immediately terminates the method, returning the value it obtained from the expression. It is possible to have more than one return statement within a method, although this won't make much sense until chapter 4 when we can do something called conditional execution using if and if/else statements. It is an error for a Java method with a non-void return type to terminate without executing a return.

It is possible to use a return statement even in a void method. In that case, there is no value to return, so the syntax is simply:

return;

3.3.1 The Math Class

Many Java classes have static methods that return values. In particular, the Math class has a number of methods that perform standard arithmetic operations. For example, it has a method called sqrt that computes the square root of a number. The method has the following header:

public static double sqrt(double a) Keep in mind that this method is defined inside a class called Math. If we want to make use of that method in one of our own classes, we have to tell Java where to find it. We do so with the dot notation, referring to this as Math.sqrt. So we might write a program like the following:
        public class WriteRoots {
            public static void main(String[] args) {
                for (int i = 1; i <= 20; i++)
                    System.out.println("sqrt(" + i + ") = " + Math.sqrt(i));
            }
        }
which produces the following output:

        sqrt(1) = 1.0
        sqrt(2) = 1.4142135623730951
        sqrt(3) = 1.7320508075688772
        sqrt(4) = 2.0
        sqrt(5) = 2.23606797749979
        sqrt(6) = 2.449489742783178
        sqrt(7) = 2.6457513110645907
        sqrt(8) = 2.8284271247461903
        sqrt(9) = 3.0
        sqrt(10) = 3.1622776601683795
        sqrt(11) = 3.3166247903554
        sqrt(12) = 3.4641016151377544
        sqrt(13) = 3.605551275463989
        sqrt(14) = 3.7416573867739413
        sqrt(15) = 3.872983346207417
        sqrt(16) = 4.0
        sqrt(17) = 4.123105625617661
        sqrt(18) = 4.242640687119285
        sqrt(19) = 4.358898943540674
        sqrt(20) = 4.47213595499958
Notice that we are passing a value of type int to Math.sqrt, but the header says that it expects a value of type double. Remember that if Java is expecting a double and gets an int, it converts the int into a corresponding double.

The Math class also defines two constants that are frequently used, e and pi. Following the Java convention, we use all uppercase letters for their names and refer to them as Math.E and Math.PI.

Below is a short list of some of the most useful static methods from the Math class.

Useful Static Methods in the Math Class
Method Description Example
abs absolute value Math.abs(-308) returns 308
cos cosine (radians) Math.cos(Math.PI) returns -1.0
exp exponent base e Math.exp(1) returns 2.7182818284590455
log logarithm base e Math.log(Math.E) returns 1.0
max maximum of two values Math.max(45, 207) returns 207
min minimum of two values Math.min(3.8, 2.75) returns 2.75
pow power (general exponentiation) Math.pow(3, 4) returns 81.0
random random value Math.random() returns a random between 0.0 and 1.0
sin sine Math.sin(0) returns 0.0
sqrt square root Math.sqrt(2) returns 1.4142135623730951

You can see a complete list of methods defined in the Math class by checking out the api documentation for your version of Java. The acronym "api" stands for "Application Program Interface" or "Application Programming Interface." The api describes how to make use of the standard libraries that are available to Java programmers. It can be a bit overwhelming to read through the api documentation because the Java libraries are vast. So wander around a bit if you are so inclined, but don't be dismayed that there are so many libraries to choose from in Java.

3.4 Overloading of Methods

It is often the case that we want to create slight variations of the same method with different parameter passing. For example, we might have a method drawBox that allows you to specify a particular height and width but you might want to also have a version that draws a box of default size. In other words, sometimes you want to specify these values:

drawBox(8, 10); and other times you want to just tell it to draw a box with the standard height and width:

drawBox(); Older programming languages required you to come up with different names for these. One might be called drawBox and the other might be called drawDefaultBox. Coming up with new names for each variation becomes tedious. Fortunately Java allows us to have more than one method with the same name as long as they have different parameters. This is a process called overloading and the primary requirement for overloading is that the different methods that you define have different method signatures.
Method Signature

The name of a method along with its number and type of parameters.

Method Overloading

Defining two or more different methods that have the same name but different method signatures.

For the drawBox example the two versions would clearly have different method signatures because one has 2 parameters and the other has 0 parameters. It would be obvious from any call on the method which one to use. If you see 2 parameters, you execute the version with 2 parameters. If you see 0 parameters, you execute the version with 0 parameters.

It gets more complicated when overloading involves the same number of parameters, but this turns out to be one of the most useful applications of overloading. For example, the println method is actually a series of overloaded methods. We can call println passing it a String, or passing it an int, or passing it a double and so on. This is implemented as series of different methods all of which take one parameter but one version takes a String, another version takes an int, another version takes a double and so on. Obviously you do slightly different things to print one of these kinds of data versus another, which is why it's useful to have these different versions of the method.

The Math class also has several overloaded methods. For example, there is a version of the absolute value method (Math.abs) for ints and another for doubles. The rules that govern which method is called are complex, so we won't cover them here. The basic idea, though, is that Java tries to find the method that is the best fit. For the most part, you don't have to think much about this. You can just let Java choose for you and it will generally make the right choice.

3.5 Introduction to Objects

We've spent a considerable amount of time discussing the primitive types in Java and how they work, so it's about time that we started talking about objects and how they work. It would be nice if Java had a consistent object model, but it doesn't. That means that we, unfortunately, have to learn two sets of rules if we want to understand how our programs operate.

The idea for objects came from the observation that as we start working with a new kind of data (integers, reals, characters, text, etc), we find ourselves writing a lot of methods that operate on that data. It seemed odd to have these two things separated. It makes more sense to include some of the basic operations with the data itself. This packaging of data and operations into one entity is the central idea behind objects.

As an analogy, imagine that you want to build a car and someone tells you that you can't include any controls in the car for things like turning the steering wheel or turning the windshield wipers on and off. They say you have to build something called a car and you have to build separate devices that control the car. That doesn't make much sense to us because we are so used to having an integrated device that has controls built into the car itself. Objects are like a car, with certain controls built into the object itself.

We have seen that all code in a Java program must be inside of a class. Thus, as we said in chapter 1, classes are the basic building blocks of Java programs. But classes are also associated with objects. Java allows us to describe a new category of objects with a class definition, which leads us to a second definition for class.

Class

A category or type of object.

In this case, a class is like a blueprint of what the object looks like. Once we've given Java the blueprint, we can ask it to create actual objects that match the blueprint. We refer to each of the individual objects as instances of the class.

Instance

An object that belongs to a particular class (an instance of a class).

We tend to use the word "instance" and "object" interchangeably.

This is difficult to understand in the abstract, so let's look at several different classes to better understand what this means and how it works. In keeping with our idea of focusing on fundamental concepts first, we're going to study how to use objects, but we aren't going to study how to define our own classes just yet. We'll get to that in chapter 8 after we've had some time to practice using objects.

3.5.1 String Objects

Strings are one of the most useful and the most commonly used types of objects in Java, so we definitely want to see how they work. They don't make the best example of objects, though, because there are a lot of special rules that apply only to strings, so in the next section we'll look at a second kind of object to see something that is a little more typical.

Strings have the special property that there are literals that represent String objects. We've been using them in println statements since chapter 1. What we haven't discussed is that these literal values represent objects of type String (in other words, instances of the String class). For example, in the same way that you can say:

int x = 8; you can say:

String s = "hello there"; In other words, we can declare variables of type String and can use the assignment statement to give a value to these variables. We can also write code that involves String expressions:

        String s1 = "hello";
        String s2 = "there";
        String combined = s1 + " " + s2;
This code defines two Strings that each represent a single word and a third String that represents the concatenation of the two words with a space in between.

So far we haven't seen anything special about String variables. Remember that the idea behind objects was to include basic operations with the data itself, the way we build a car that has controls built in. The data stored in a String is a sequence of characters. There are all sorts of operations we might want to perform on this sequence of characters. For example, we might want to know how many characters there are in the String. String objects have a length method that let's you figure this out.

If the length method were static, you would call it by saying something like:

        length(s)
Instead, you put the name of the variable first and then the method with a dot in between:

        s.length()
Think of it as talking to the String object. When you ask for "s.length()", you're saying, "Hey, s. I'm talking to you. What's your length?". Of course, different String objects have different lengths, so you will get different answers when you talk to different String objects.

For example, suppose that we have initialized two string variables as follows:

        String s1 = "hello";
        String s2 = "how are you?";
We can use a println to examine the length of each string:

        System.out.println("Length of s1 = " + s1.length());
        System.out.println("Length of s2 = " + s2.length());
which produces the following output:

        Length of s1 = 5
        Length of s2 = 12
What else might we want to do with a String object? With the length method we can figure out how many characters there are, but what about getting the individual characters themselves? There are several ways to do this, but one of the most common is to use a method called charAt that returns the character at a specific location in the String.

This leads us to the problem of how to specify locations in a sequence. Obviously there is a first character, second character, and so on, so it makes sense to use an integer to refer to a specific location. We call this the index of the location and, as we'll see, Java usually uses 0 as the first index.

Index

An integer used to specify a location in a sequence of values. Java generally uses 0-based indexing (0 as the first index followed by 1, 2, 3 and so on).

So each character of a String object is assigned an index starting with index 0. For example, for our variable s1 that refers to the String "hello" the indexes would be:

        h    e    l    l    o
        |    |    |    |    |
        0    1    2    3    4
It may seem intuitive to consider the letter "h" to be at position 1, but there are advantages to starting with an index of 0 and it's a convention that was adopted by the designers of the C language that has been followed also by the designers of C++ and Java, so it's a convention you'll have to learn to live with. For our longer string s2 the positions would be:

        h    o    w         a    r    e         y    o    u    ?
        |    |    |    |    |    |    |    |    |    |    |    |
        0    1    2    3    4    5    6    7    8    9   10   11
Notice that the spaces in the string have positions as well, as in positions 3 and 7 above. Also notice that the indexes for a given String always range from 0 to one less than the length of the String.

Using the charAt method, we can request specific characters of a String. The return type is char. For example, if we ask for s1.charAt(1) we'll get 'e' (the 'e' in "hello"). If we ask for s2.charAt(5) we'll get 'r' (the 'r' in "how are you?"). For any String, if we ask for charAt(0), we'll get the first character of the String.

When working with String variables, we often find it useful to write a for loop to handle the different characters of the String. Because Strings are indexed starting at 0, this is a place where it is easier to write for loops that start with 0 rather than 1. Consider, for example, the following code that prints out the individual characters of s1:

    String s1 = "hello";
    for (int i = 0; i < s1.length(); i++) {
        System.out.println(i + ": " + s1.charAt(i));
    }
which produces the following output:

        0: h
        1: e
        2: l
        3: l
        4: o
Remember that when we start loops at 0, we usually test with less-than ("<") rather than less-than-or-equal ("<="). Our String s1 has 5 characters in it, so the call on s1.length() will return 5. But because the first index is 0, the last index will be one less than 5 (index 4). This takes a while to get used to, but 0-based indexing is used throughout Java, so you'll eventually get the hang of it and you'll find that it pays off.

Another useful String method is the substring method. It takes two integer arguments representing a starting and ending index. When you call the substring method, you provide two of these indexes, the index of the first character you want and the index just past the last index that you want.

Recall that our String s2 that we set to "how are you?" has the following positions:

        h    o    w         a    r    e         y    o    u    ?
        |    |    |    |    |    |    |    |    |    |    |    |
        0    1    2    3    4    5    6    7    8    9   10   11
If you want to pull out the individual word "how" from this String, you'd ask for:

s2.substring(0, 3) Remember that the second value that you pass to the substring method is supposed to be one beyond the end of the substring you are forming. So even though there is a space at position 3 in the original string, we don't get it from this call on substring. Instead we get all characters just before position 3.

This means that sometimes you will give a position to substring at which there isn't a character. The last character in the string that s2 refers to is at index 11 (the question mark). If you want to get the substring "you?" including the question mark, you'd ask for:

s2.substring(8, 12) There is no character at position 12 in s2, but this call asks for characters starting at position 8 that come before position 12, so this actually makes sense.

You have to be careful about what indexes you use. With the substring method we can ask for the position just beyond the end of the String, but you can't ask for anything beyond that. For example, if you ask for:

        s2.substring(8, 13)
Your program will generate an execution error. Similarly, if you ask for the charAt a nonexistent position, your program will generate an execution error.

You can use Strings as parameters to methods. For example, the program below uses String parameters to eliminate some of the redundancy in a popular children's song.

        public class BusSong {
            public static void main(String[] args) {
                verse("wheels", "go", "round and round");
                verse("wipers", "go", "swish, swish, swish");
                verse("horn", "goes", "beep, beep, beep");
            }
        
            public static void verse(String item, String verb, String sound) {
                System.out.print("The " + item + " on the bus " + verb + " ");
                System.out.println(sound + ",");
                System.out.println(sound + ",");
                System.out.println(sound + ".");
                System.out.print("The " + item + " on the bus " + verb + " ");
                System.out.println(sound + ",");
                System.out.println("All through the town.");
                System.out.println();
            }
        }
It produces the following output:

        The wheels on the bus go round and round,
        round and round,
        round and round.
        The wheels on the bus go round and round,
        All through the town.
        
        The wipers on the bus go swish, swish, swish,
        swish, swish, swish,
        swish, swish, swish.
        The wipers on the bus go swish, swish, swish,
        All through the town.
        
        The horn on the bus goes beep, beep, beep,
        beep, beep, beep,
        beep, beep, beep.
        The horn on the bus goes beep, beep, beep,
        All through the town.
Below is a table of some of the most useful methods that you can call on String objects.

Useful Methods of String Objects
Method Description Example
charAt(index) character at a specific index s1.charAt(1) returns 'e'
endsWith(text) whether or not string ends with some text s1.endsWith("llo") returns true
indexOf(character) index of a particular character (-1 if not present) s1.indexOf('o') returns 4
length() number of characters s1.length() returns 5
startsWith(text) whether or not string starts with some text s1.startsWith("hi") returns false
substring(start, stop) characters from start index to just before stop index s1.substring(1,3) returns "el"
toLowerCase() a new string with all lowercase letters s1.toLowerCase() returns "hello"
toUpperCase() a new string with all uppercase letters s1.toUpperCase() returns "HELLO"

Strings in Java are immutable, which means that once they are constructed, they can never be changed in value.

Immutable Object

An object whose value cannot be changed.

It may seem odd that strings are immutable and yet they have methods like toUpperCase and toLowerCase. You have to read the table description carefully. These methods don't actually change a given string object, they return a new string. Consider the following code.

String s = "Hello Maria"; s.toUpperCase(); System.out.println(s); You might think that this will turn the string to its uppercase equivalent, but it doesn't. The second line of code constructs a new string that has the uppercase equivalent of the string, but we don't do anything with this new value. The key is to either store this new string in a different variable or to reassign the variable s to point to the new string: String s = "Hello Maria"; s = s.toUpperCase(); System.out.println(s); This version of the code produces the following output:
        HELLO MARIA
The toUpperCase and toLowerCase methods are particularly helpful when you want to perform string comparisons in which you ignore the case of the letters involved.

3.5.2 Point Objects

Java includes a class called Point for storing the (x, y) coordinates of a point. These coordinates are expressed as integers, although there are also variations for storing points expressed using floating point numbers. Each Point object, then, stores a particular x-coordinate and y-coordinate.

Strings are a special case that have literal values that can be referred to directly. Most objects have to be explicitly constructed by calling a special method known as a constructor.

Construct (Constructor)

Objects in Java programs are constructed before they can be used. A method that constructs an object is known as a constructor.

Remember that a class is like a blueprint for a family of objects. Calling a constructor is like sending an order to the factory asking it to follow the blueprint to get you an actual object that you can manipulate. When you send in your order to the factory, you sometimes specify certain parameters (e.g., what color you want the object to be).

In Java, constructors are called using the special keyword "new". For example, to construct a specific Point object, you have to pass the values you want for x and y:

Point p = new Point(3, 8); This is actually a call on a constructor method of the Point class. Constructors always have the same name as the class. In this case the constructor is passed two integer values as parameters to specify the x-coordinate and y-coordinate of the point.

After executing the line of code above, we would have the following situation:

                   +--------------------+
          +---+    |   +----+    +----+ |
        p | +-+--> | x |  3 |  y |  8 | |
          +---+    |   +----+    +----+ |
                   +--------------------+
Once you have constructed a Point object, what can you do with it? One of the most common things you do with an object like this is to ask for a String version of the data calling a method known as toString. Many Java objects have a toString method, including Point objects.

Point objects also have a method called "translate" that can be used to shift the coordinates by a specific delta-x and delta-y, which are passed as parameters. When you translate a point, you shift its location by the specified amount. For example, you might say:

p.translate(-1, -2); // subtract 1 from x, subtract 2 from y Given that the point started out with coordinates (3, 8), this translation would leave the point with coordinates (2, 6). Thus, after these this line of code is executed, we end up with the following situation:

                   +--------------------+
          +---+    |   +----+    +----+ |
        p | +-+--> | x |  2 |  y |  6 | |
          +---+    |   +----+    +----+ |
                   +--------------------+

Here is a complete program that constructs a Point object and translates its coordinates, using println statements with a call on toString to examine the coordinates before and after the call.

        import java.awt.*;
        
        public class PointExample1 {
            public static void main(String[] args) {
                Point p = new Point(3, 8);
                System.out.println("initially p = " + p.toString());
                p.translate(-1, -2);
                System.out.println("after translating p = " + p.toString());
            }
        }
There is something new at the beginning of this class file called an import declaration. Java has a vast array of standard classes that are collectively known as the Java class libraries. To help manage this complexity, Java provides an organizational unit known as a package. Related classes are combined together into a single package.

Package

A collection of related Java classes.

For example, the Point class is stored in a package known as java.awt, which is an abbreviation for the "Java Abstract Windowing Toolkit". Java programs don't normally have access to a package unless it includes an import declaration.

Import Declaration

A request to access a specific Java package.

We haven't needed an import statement yet because Java automatically imports every class stored in a package called "java.lang". The java.lang package includes basic classes that most Java programs would be likely to use (e.g., System, String, Math). Because Java does not automatically import java.awt, we have to do it ourselves.

The import declaration allows you to import just a single item from a package, as in:

import java.awt.Point; Some people prefer to specifically mention each class that you are importing. The problem is that once you start importing one class from a package, you're likely to want to import others as well. Java allows you to use an asterisk to import all classes from a package:

import java.awt.*; We will use the asterisk version of the import in this book to keep things simple.

Below is the output produced by the program:

        initially p = java.awt.Point[x=3,y=8]
        after translating p = java.awt.Point[x=2,y=6]
Notice that the toString method generates a String with the full name of the class (java.awt.Point) and the values of the x and y coordinates inside of square brackets.

The toString method is so useful and so common that Java provides a shorthand for calling it. Consider the following two statements:

System.out.println(p.toString()); System.out.println("p = " + p.toString()); In each case we call the toString method to convert the object to a String equivalent. In the first case we write out just the String. In the second case we combine the result with some other text using string concatenation. The call on toString can be omitted in both cases:

System.out.println(p); System.out.println("p = " + p); In each case Java will call the toString method for you, which means that these two lines of code perform exactly the same as the previous two lines of code.

The data values inside an object are stored in what are known as data fields. In the case of a Point object, the fields are called "x" and "y" for the two coordinates. You can access these fields using the dot notation:

int sum = p.x + p.y; You can even change these internal values directly, as in:

p.x = 12; p.y = 15; Point objects are unusual in that they allow you to access the fields directly in this way. It is considered bad programming style to directly manipulate the fields inside of an object and the class designers at Sun have stated publicly that they regret having made these fields accessible. Point objects have methods like getX, getY and setLocation that allow you to manipulate the fields through methods rather than through direct access. Thus, the Point class is a good example for understanding the fact that objects have fields that can be referred to using the dot notation, but this should not be considered an example to emulate.

3.5.3 Reference Semantics

Objects are stored internally in a different way than primitive data. For example, when we declare an integer variable:

        int x = 8;
The variable stores the actual data. So we've drawn pictures like the following:

          +---+
        x | 8 |
          +---+
The situation is different for objects. With objects, the variable doesn't store the actual data. Instead, the data is stored in an object and the variable stores a reference to where the object is stored. So we have two different elements in memory: the variable and the object. So when we construct a Point object:

        Point p = new Point(3, 8);
we end up with the following:

                   +--------------------+
          +---+    |   +----+    +----+ |
        p | +-+--> | x |  3 |  y |  8 | |
          +---+    |   +----+    +----+ |
                   +--------------------+
As the diagram indicates, we have two different values stored in memory. We have the Point object itself, which appears on the right side of this picture. We also have a variable called p which stores a reference to the Point object (represented in this picture as an arrow). We sometimes say that p "points" to the object.

This will take some time to get used to because these are really two different approaches to storing data. These approaches are so common that computer scientists have technical terms to describe them. The system for the primitive types like int is known as value semantics and those types are often referred to as value types. The system for Points and other objects is known as reference semantics and those types are often referred to as reference types.

Value Semantics

A system in which values are stored directly and copying involves the creation of independent copies of values. Types that use value semantics are called value types.

Reference Semantics

A system in which references to values are stored and copying involves copying references. Types that use reference semantics are called reference types.

It will take us a while to explore all of the implications of this difference. The key thing to remember is that when you are working with objects, you are always working with references to data rather than the data itself.

3.5.4 Multiple Objects

In the last section we saw how to manipulate a single object. Consider the following lines of code that construct two different objects.

Point p1 = new Point(3, 8); Point p2 = new Point(); Each object must be constructed separately by a call on new. The first call passes the x-coordinate and y-coordinate of the point. The second call uses a different constructor with a different method signature (zero parameters instead of two parameters). This constructor sets the x-coordinate and y-coordinate to 0. The 0-argument constructor is often referred to as the "default" or "paren paren" constructor. After executing these statements, our memory would look as follows.

                    +--------------------+
           +---+    |   +----+    +----+ |
        p1 | +-+--> | x |  3 |  y |  8 | |
           +---+    |   +----+    +----+ |
                    +--------------------+

                    +--------------------+
           +---+    |   +----+    +----+ |
        p2 | +-+--> | x |  0 |  y |  0 | |
           +---+    |   +----+    +----+ |
                    +--------------------+
To underscore the fact that variables and objects are stored separately, consider what would happen if you now execute the following code:

Point p3 = p2; This declares a third Point variable but doesn't include a third call on new.

That means that we still have just the two Point objects even though we now have three Point variables:

                    +--------------------+
           +---+    |   +----+    +----+ |
        p1 | +-+--> | x |  3 |  y |  8 | |
           +---+    |   +----+    +----+ |
                    +--------------------+

                    +--------------------+
           +---+    |   +----+    +----+ |
        p2 | +-+--> | x |  0 |  y |  0 | |
           +---+    |   +----+    +----+ |
                    +--------------------+
                              ^
           +---+              |
        p3 | --+--------------+
           +---+
The variables p2 and p3 both refer to the same Point object. This situation doesn't arise when you use value types like int. This happens only with objects.

Let's look at a complete program to explore how this works.

        import java.awt.*;
        
        public class PointExample2 {
            public static void main(String[] args) {
                // declare variables and construct objects
                Point p1 = new Point(3, 8);
                Point p2 = new Point();
                Point p3 = p2;
        
                // see what is in each point initially
                System.out.println("p1 = " + p1);
                System.out.println("p2 = " + p2);
                System.out.println("p3 = " + p3);
                System.out.println();
        
                // three translations
                p1.translate(-1, -2);
                p2.translate(4, 8);
                p3.translate(2, 3);
        
                // see what is in each point now
                System.out.println("p1 = " + p1);
                System.out.println("p2 = " + p2);
                System.out.println("p3 = " + p3);
            }
        }
This program produces output that might be surprising:

        p1 = java.awt.Point[x=3,y=8]
        p2 = java.awt.Point[x=0,y=0]
        p3 = java.awt.Point[x=0,y=0]
        
        p1 = java.awt.Point[x=2,y=6]
        p2 = java.awt.Point[x=6,y=11]
        p3 = java.awt.Point[x=6,y=11]
We end up with coordinates (6, 11), but there aren't any 6's or 11's in the original program. Let's see how this happens. The manipulations using variable p1 are fairly straightforward. Variables p2 and p3 are more complex. Remember that we have a single Point object that both of these variables refer to. This object initially has coordinates (0, 0). So after executing the first three lines of code, memory looks like this:

                    +--------------------+
           +---+    |   +----+    +----+ |
        p1 | +-+--> | x |  3 |  y |  8 | |
           +---+    |   +----+    +----+ |
                    +--------------------+

                    +--------------------+
           +---+    |   +----+    +----+ |
        p2 | +-+--> | x |  0 |  y |  0 | |
           +---+    |   +----+    +----+ |
                    +--------------------+
                              ^
           +---+              |
        p3 | --+--------------+
           +---+
When we execute the first set of println statements we see that p1 has coordinates (3, 8). When we execute the println statement on p2 we get the coordinates (0, 0) and when we execute the println statement on p3 we get these coordinates again, because each variable is referring to this object. Then we translate the coordinates for p1 by -1 in the x direction and -2 in the y direction:

                    +--------------------+
           +---+    |   +----+    +----+ |
        p1 | +-+--> | x |  2 |  y |  6 | |
           +---+    |   +----+    +----+ |
                    +--------------------+

                    +--------------------+
           +---+    |   +----+    +----+ |
        p2 | +-+--> | x |  0 |  y |  0 | |
           +---+    |   +----+    +----+ |
                    +--------------------+
                              ^
           +---+              |
        p3 | --+--------------+
           +---+
Then we translate the coordinates for p2 by 4 in the x direction and by 8 in the y direction:

                    +--------------------+
           +---+    |   +----+    +----+ |
        p1 | +-+--> | x |  2 |  y |  6 | |
           +---+    |   +----+    +----+ |
                    +--------------------+

                    +--------------------+
           +---+    |   +----+    +----+ |
        p2 | +-+--> | x |  4 |  y |  8 | |
           +---+    |   +----+    +----+ |
                    +--------------------+
                              ^
           +---+              |
        p3 | --+--------------+
           +---+
Then we translate the coordinates for p3 by 2 in the x direction and 3 in the y direction.

But because p3 refers to the same object as p2, this translation is, in effect, added to the previous one:

                    +--------------------+
           +---+    |   +----+    +----+ |
        p1 | +-+--> | x |  2 |  y |  6 | |
           +---+    |   +----+    +----+ |
                    +--------------------+

                    +--------------------+
           +---+    |   +----+    +----+ |
        p2 | +-+--> | x |  6 |  y | 11 | |
           +---+    |   +----+    +----+ |
                    +--------------------+
                              ^
           +---+              |
        p3 | --+--------------+
           +---+
We started with the coordinates (0, 0) and we translated p2 by (4, 8) and p3 by (2, 3), but p2 and p3 refer to the same object, which means we ended up translating that single point twice, leaving it at (6, 11). The final println statements report the new coordinates of p1 and report the new coordinates of the other point twice (once through the println on p2 and once through the println on p3). That's because we also translate the coordinates for p3 by 2 in x and 3 in y. But p3 and p2 are both referring to the same point. That means that we shift the coordinates from (4, 8) to (6, 11). And those are exactly the coordinates we see when we execute a println on p2 and p3.

3.5.5 Objects as Parameters to Methods

Consider what happens when you define a static method that uses a reference type for its parameter:

public static void manipulate(Point p) { p.translate(2, 3); } Remember that the value parameter creates copies of whatever is passed to it. With the value types like int, any changes that we made to the parameter had no effect on any variable passed to it. The situation is more complicated for objects because of their reference semantics. Suppose that we have defined a Point variable and we pass it to this method:

Point test = new Point(6, 15); manipulate(test); Does the method end up translating the coordinates of the object? The answer is yes, even though we are using a value parameter that creates a copy. Think of what is happening in the computer's memory. When we declare our variable test, we end up with this situation:

                      +--------------------+
             +---+    |   +----+    +----+ |
        test | +-+--> | x |  6 |  y | 15 | |
             +---+    |   +----+    +----+ |
                      +--------------------+
When we call the manipulate method, Java makes a copy of the variable test and stores this in the parameter called p. But the variable test isn't itself an object. It stores a reference to an object. So when we make a copy of it, we end up with this situation in memory:

                      +--------------------+
             +---+    |   +----+    +----+ |
        test | +-+--> | x |  6 |  y | 15 | |
             +---+    |   +----+    +----+ |
                      +--------------------+
                                ^
          +---+                 |
        p | --+-----------------+
          +---+
We now have two variables that refer to the same Point object. So it doesn't matter that p is a copy, because it's referring to the same object as the original. So any calls on translate using p will change the object that test refers to.

There is one other case worth considering. What if the manipulate method had been written this way instead:

public static void manipulate(Point p) { p.translate(2, 3); p = new Point(17, 45); } Let's look at this in detail. When the method is called, we set up the variable p as a copy of the variable test, which leads to two variables referring to the same Point object.

                      +--------------------+
             +---+    |   +----+    +----+ |
        test | +-+--> | x |  6 |  y | 15 | |
             +---+    |   +----+    +----+ |
                      +--------------------+
                                ^
          +---+                 |
        p | --+-----------------+
          +---+
We translate the coordinates for p by 2 in the x direction and 3 in the y direction as in the old version of the method:

                      +--------------------+
             +---+    |   +----+    +----+ |
        test | +-+--> | x |  8 |  y | 18 | |
             +---+    |   +----+    +----+ |
                      +--------------------+
                                ^
          +---+                 |
        p | --+-----------------+
          +---+
Then we turn around and reset the variable p to a new Point object with a different set of coordinates. Does this affect our variable test? The answer is no.


Because p is a local copy, changing its value has no effect on our variable test:

                      +--------------------+
             +---+    |   +----+    +----+ |
        test | +-+--> | x |  8 |  y | 18 | |
             +---+    |   +----+    +----+ |
                      +--------------------+

                   +--------------------+
          +---+    |   +----+    +----+ |
        p | +-+--> | x | 17 |  y | 45 | |
          +---+    |   +----+    +----+ |
                   +--------------------+
The bottom line is that when you pass an object as a parameter, you can change the object itself but you can't change the variable that points to the object.

It's as though you have a piece of paper on which you've written down the address to your home. You might write down the address to your home on a second piece of paper and give that to someone else. Because you have your own piece of paper telling you where your home is, nobody will be able to take that away from you even if they change what is written on their piece of paper. So you'll always know where you live. But if they know where your home is, they can go there and do whatever they want to it. In Hollywood terms, imagine a villain saying, "I know where you live," meaning, "I can go to your house and cause trouble."

3.6 Introduction to Graphics

Java has extensive libraries for performing graphics operations. Graphics are used for rendering complex images, for computer animation and for modern user interfaces called GUIs (Graphical User Interfaces). The original tools were collectively known as AWT which is short for "Abstract Windowing Toolkit". As we saw earlier in the chapter, the Point class is defined in the java.awt package. Other libraries have been added since then including the Swing libraries for designing more sophisticated and more reliable user interfaces, the Java 2D framework and the Java 3D framework.

In this section we will examine a few of the basic classes from the original awt framework. To keep things simple, we will be using a custom class called DrawingPanel that was written by the authors of this textbook to simplify some of the more esoteric details of Java graphics. It is less than a page in length, so we aren't hiding much.

The actual drawing will be done with an object of type Graphics. The Graphics class is part of the java.awt library. Think of the drawing panel as being like a piece of paper and the Graphics object as being like a pencil or paintbrush. The panel keeps track of the overall image, but the Graphics object does the actual drawing of individual shapes within the panel.

We will also explore the option of using different colors. You will be able to set the background color of the panel itself and you can also change the current color being used by the Graphics object (like dipping your paintbrush in a different container of paint). We will use the standard Color class that is also part of the standard java.awt package. There are a number of predefined colors that we can refer to directly. They are defined as class constants (just like the constants we were using in chapter 2). The names of these constants are in all uppercase and are self-explanatory. To refer to one of these colors, you have to precede it with the class name and a dot, as in Color.GREEN or Color.BLUE. Below is a table of all of the predefined Color constants.

Color Constants
Color.BLACK Color.GREEN Color.RED
Color.BLUE Color.LIGHT_GRAY Color.WHITE
Color.CYAN Color.MAGENTA Color.YELLOW
Color.DARK_GRAY Color.ORANGE  
Color.GRAY Color.PINK  

DrawingPanel objects have the following public methods.

Public Methods of the DrawingPanel Class
Method Description
DrawingPanel(int width, int height) constructs a DrawingPanel object with a drawing area of the given width and height and displays it on the screen
getGraphics() returns a reference to the Graphics object that can be used to draw onto the panel
setBackground(Color c) sets the background color of the panel to the given color (default is white)

The typical usage of the DrawingPanel will be to construct one with a particular height and width, to set its background color if you don't want the default white background and then to draw something using its Graphics object. The DrawingPanel appears on the screen at the time that you construct it.

Notice that all coordinates are specified as integers. Each (x, y) position corresponds to a different pixel on the computer screen. The word "pixel" is a shorthand for "picture element" and represents a single dot on the computer screen.

The coordinate system assigns the upper-left corner of a panel to (0, 0). As we move to the right of this position, the x coordinate goes up. As we go down from this position, the y coordinates goes up. For example, suppose that you construct a DrawingPanel with a width of 200 pixels and a height of 100 pixels. Then the upper-left corner has coordinates (0, 0) and the lower-right corner has coordinates (200, 100).

        (0, 0) +-----------+
               |           |
               |           |
               |           |
               +-----------+ (200, 100)
This is likely to be confusing at first because in math classes you probably used coordinate systems where y values went down as you moved down.

So how do you actually draw something? The DrawingPanel class has a method called getGraphics that returns a reference to its Graphics object (its paintbrush). To do any actual drawing, we have to know how to manipulate a Graphics object. One of the simplest drawing commands is drawLine which takes four integer arguments:

        drawLine(int x1, int y1, int x2, int y2)

        draws a line from the point (x1, y1) to the point (x2, y2)
Here is a sample program that puts these pieces together:

        import java.awt.*;
        
        public class DrawSample1 {
            public static void main(String[] args) {
                DrawingPanel p = new DrawingPanel(200, 100);
                p.setBackground(Color.CYAN);
                p.getGraphics().drawLine(25, 75, 175, 25);
            }
        }
which produces the following output:

The first statement in main constructs a DrawingPanel with a width of 200 and a height of 100. Once constructed, the window will pop up on the screen. The second statement sets the background color to cyan. The third statement draws a line from (25, 75) to (175, 25). The first point is in the lower-left part of the window (25 over from the left, 75 down from the top). The second point is in the upper-right corner (175 over from the left, only 25 down from the top).

Notice this particular line of code:

        p.getGraphics().drawLine(25, 75, 175, 25);
You might wonder why you can't just say:

        p.drawLine(25, 75, 175, 25);
The point is that there are two different objects involved in this program: the drawing panel itself (which is like the canvas) and the Graphics object associated with the panel (which is like the paintbrush). The panel doesn't know how to draw a line. Only the Graphics object knows how to draw a line. So you have to be careful to make sure that you are talking to the right object when you give a command.

This can be confusing, but it is a common occurrence in Java programs. In fact, a typical Java program has hundreds if not thousands of objects that are interacting with each other. This isn't so unlike what we do as people. If you want to schedule a meeting with a busy corporate executive, she might tell you to, "Talk to my secretary about that." Or if you're asking difficult legal questions, a person might tell you to, "Talk to my lawyer about that." In this case, the DrawingPanel doesn't know how to draw. If it could talk, it would say, "Talk to my Graphics object about that."

Let's look at a more complicated example:

        import java.awt.*;
        
        public class DrawSample2 {
            public static void main(String[] args) {
                DrawingPanel p = new DrawingPanel(200, 100);
                p.setBackground(Color.CYAN);
                p.getGraphics().drawLine(25, 75, 100, 25);
                p.getGraphics().drawLine(100, 25, 175, 75);
                p.getGraphics().drawLine(25, 75, 175, 75);
            }
        }
This program draws three different lines to form a triangle:

The lines are drawn between three different points. In the lower-left corner we have the point (25, 75). In the middle at the top we have the point (100, 25). And in the lower-right corner we have the point (175, 75). The various calls on drawLine simply draw the lines that connect these three points.

You can see in this program that it is rather tedious to have to keep saying "p.getGraphics()" over and over. This is a case where it is helpful to introduce an extra variable to keep track of this. Below is a program that shows the more typical usage where we introduce a variable "g" that keeps track of the Graphics object.

        import java.awt.*;
        
        public class DrawSample3 {
            public static void main(String[] args) {
                DrawingPanel p = new DrawingPanel(200, 100);
                p.setBackground(Color.CYAN);
                Graphics g = p.getGraphics();
        
                g.drawLine(25, 75, 100, 25);
                g.drawLine(100, 25, 175, 75);
                g.drawLine(25, 75, 175, 75);
            }
        }
The Graphics object also has methods for drawing particular shapes. For example, we often want to draw rectangles and we can do so with the drawRect method.

        drawRect(int x, int y, int width, int height)

        draws a rectangle with upper-left coordinates (x, y) and given
        height and width
Notice that the first two values passed to drawRect are coordinates. The next two values are a width and a height, not actual coordinates. For example, here is a short program that draws two rectangles:

        import java.awt.*;
        
        public class DrawSample4 {
            public static void main(String[] args) {
                DrawingPanel p = new DrawingPanel(200, 100);
                p.setBackground(Color.CYAN);
                Graphics g = p.getGraphics();
        
                g.drawRect(25, 50, 20, 20);
                g.drawRect(150, 10, 40, 20);
            }
        }
The first rectangle has upper-left corner (25, 50). It's width and height are each 20, so this is a square. Notice that the second set of values aren't actual coordinates. The coordinates of the lower-right corner would be (45, 70) (20 more than the (x, y) coordinates of the upper-left corner). This program also draws a rectangle with upper-left corner (150, 10) that has a width of 40 and a height of 20 (wider than it is tall).

Below is the output of the program.

Another figure we often want to draw is a circle or, more generally, an oval. We are immediately faced with the problem of how to specify where it appears and how big it is. We do so by specifying what is known as the "bounding rectangle" of the circle or oval. In other words, we specify it the same way we specify a rectangle with the understanding that Java will draw the largest oval possible that fits inside that rectangle.

        drawOval(int x, int y, int width, int height)

        draws the largest oval that fits within the rectangle with upper-left
        coordinates (x, y) and given height and width
For example, below is an extension of the previous program that includes two ovals.

        import java.awt.*;
        
        public class DrawSample5 {
            public static void main(String[] args) {
                DrawingPanel p = new DrawingPanel(200, 100);
                p.setBackground(Color.CYAN);
                Graphics g = p.getGraphics();
        
                g.drawRect(25, 50, 20, 20);
                g.drawRect(150, 10, 40, 20);
                g.drawOval(50, 25, 20, 20);
                g.drawOval(150, 50, 40, 20);
            }
        }
The bounding rectangle of the first oval has upper-left coordinates (50, 25) and a width and height of 20. In other words, this draws a circle. The bounding rectangle of the second oval has upper-left coordinates (150, 50) and a width of 40 and height of 20 (an oval that is wider than it is tall). Below is the output produced by this program.

Sometimes we don't want to draw a figure, we want to paint the entire area with a particular color. There are variations of the drawRect and drawOval methods known as fillRect and fillOval that do exactly this. They fill in the given rectangle or oval with the current color of paint. For example, if we change two of the calls in the previous program to be "fill" operations instead of "draw" operations:

        import java.awt.*;
        
        public class DrawSample6 {
            public static void main(String[] args) {
                DrawingPanel p = new DrawingPanel(200, 100);
                p.setBackground(Color.CYAN);
                Graphics g = p.getGraphics();
        
                g.fillRect(25, 50, 20, 20);
                g.drawRect(150, 10, 40, 20);
                g.drawOval(50, 25, 20, 20);
                g.fillOval(150, 50, 40, 20);
            }
        }
we get this output instead:

Notice that the rectangle and oval that are filled in are black. That's because the default drawing color is black. The Graphics object has another method that can be used to change the current drawing color:

        setColor(Color c)

        sets the current drawing/filling color to c
Calling setColor is like dipping your paintbrush in a different color paint. It means that from that point on, all drawing and filling will be done in that color. For example, here is another version of the previous program that fills in the oval and rectangle with white instead of black.

        import java.awt.*;
        
        public class DrawSample7 {
            public static void main(String[] args) {
                DrawingPanel p = new DrawingPanel(200, 100);
                p.setBackground(Color.CYAN);
                Graphics g = p.getGraphics();
        
                g.drawRect(150, 10, 40, 20);
                g.drawOval(50, 25, 20, 20);
                g.setColor(Color.WHITE);
                g.fillOval(150, 50, 40, 20);
                g.fillRect(25, 50, 20, 20);
            }
        }
Notice that we have rearranged the order of the calls. The two drawing commands appear first, then the call on setColor that changes the color to white, then the two filling commands. We do things in this order so that the drawing is done in black and the filling is done in white. The order of operations is very important in these drawing programs, so you'll have to keep track of what your current color is each time you give a new command to draw or fill something.

In each of the preceding examples, we used simple constants for the drawing and filling commands. It is possible to use expressions. For example, suppose that we stick with our DrawingPanel size of 200 wide and 100 tall and we want to have a diagonal series of four rectangles that extend from the upper-left corner to the lower-right corner, each with a white oval inside. In other words, we want to produce this output:

The overall width of 200 and overall height of 100 are divided evenly into four rectangles, which means that they all have to be 50 wide and 25 high. So their width and height don't change, but the position of their upper-left corners are different. The first rectangle has upper-left corner (0, 0). The second one has upper-left corner (50, 25). The third has upper-left corner (100, 50). And the fourth has upper-left corner (150, 75). We need to write code to generate these different coordinates.

This is a great place to use a for loop. Using the techniques of chapter 2, we can make a table and develop a formula for the coordinates. In this case it is easier to have the loop start with 0 rather than 1, which will often be the case with drawing programs. Below is a program that is a good first stab at generating this output.

        import java.awt.*;
        
        public class DrawSample8 {
            public static void main(String[] args) {
                DrawingPanel p = new DrawingPanel(200, 100);
                p.setBackground(Color.CYAN);
                Graphics g = p.getGraphics();
        
                for (int i = 0; i < 4; i++) {
                    g.drawRect(i * 50, i * 25, 50, 25);
                    g.setColor(Color.WHITE);
                    g.fillOval(i * 50, i * 25, 50, 25);
                }
            }
        }
It produces the following output:

We've got our coordinates and sizes right, but the colors aren't quite right. Instead of getting black rectangles with white ovals inside, we're getting one black rectangle and three white rectangles. That's because we only have one call on setColor inside the loop. Initially the color will be set to black, which is why the first rectangle comes out black. But once we make a call on setColor changing the color to white, then every subsequent drawing and filling command is done in white, including the second, third and fourth rectangles.

So we need to include a call to set the color to black for the rectangle drawing and to set the color to white for the oval. While we're at it, it's a good idea to switch the order of these things. The rectangle and oval overlap slightly and we would rather have the rectangle drawn over the oval than the other way around. Below is a program that produces the correct output.

        import java.awt.*;
        
        public class DrawSample9 {
            public static void main(String[] args) {
                DrawingPanel p = new DrawingPanel(200, 100);
                p.setBackground(Color.CYAN);
                Graphics g = p.getGraphics();
        
                for (int i = 0; i < 4; i++) {
                    g.setColor(Color.WHITE);
                    g.fillOval(i * 50, i * 25, 50, 25);
                    g.setColor(Color.BLACK);
                    g.drawRect(i * 50, i * 25, 50, 25);
                }
            }
        }
There is one last drawing command worth mentioning. It can be used to include text in your drawing.

        drawString(String s, int x, int y)

        draws the given String with lower-left corner (x, y)
This is a slightly different convention than what we used for drawRect. With drawRect we specified the coordinates of the upper-left corner. Here we specify the coordinates of the lower-left corner. By default, text is drawn approximately 10 pixels high. Below is a sample program that uses a loop to draw a particular String ten different times, indenting each time 5 more to the right and moving down 10 from the top.

        import java.awt.*;
        
        public class DrawSample10 {
            public static void main(String[] args) {
                DrawingPanel p = new DrawingPanel(200, 100);
                p.setBackground(Color.CYAN);
                Graphics g = p.getGraphics();
        
                for (int i = 0; i < 10; i++) {
                    g.drawString("There is no place like home", i * 5, 10 + i * 10);
                }
            }
        }

It produces the following output.

One final point is worth mentioning. If you write complex drawing programs, yo will want to break the program down into several static methods. In doing so, you'll have to pass a reference to the Graphics object to each static method that you introduce. For example, the previous program could be split into a main method and a drawing method as follows.

        import java.awt.*;
        
        public class DrawSample11 {
            public static void main(String[] args) {
                DrawingPanel p = new DrawingPanel(200, 100);
                p.setBackground(Color.CYAN);
                Graphics g = p.getGraphics();
        
                drawText(g);
            }
        
            public static void drawText(Graphics g) {
                for (int i = 0; i < 10; i++) {
                    g.drawString("There is no place like home", i * 5, 10 + i * 10);
                }
            }
        }
The Graphics object has many more methods than these. If you are interested, you can read about them in the Java api documentation. Below is a list of some of the most basic operations.

A Few Public Methods of the Graphics Class
Method Description
drawLine(int x1, int y1, int x2, int y2) Draws a line between the points (x1, y1) and (x2, y2).
drawOval(int x, int y, int width, int height) Draws the outline of the largest oval that fits within the specified rectangle.
drawRect(int x, int y, int width, int height) Draws the outline of the specified rectangle.
drawString(String s, int x, int y) Draws the given text with lower-left corner (x, y).
fillOval(int x, int y, int width, int height) Fills the largest oval that fits within the specified rectangle using the current color.
fillRect(int x, int y, int width, int height) Fills the specified rectangle using the current color.
setColor(Color c) Sets this graphics context's current color to the specified color. All subsequent graphics operations using this graphics context use this specified color.

 

3.7 Chapter Summary

3.8 Self-Check Exercises

  1. What is the output of the following program?
            public class Weird {
                public static void halfTheFun(int number) {
                    number = number / 2;
                    for (int count = 1; count <= number; count++)
                        System.out.print(count + " ");
                    System.out.println();
                }
            
                public static void main(String[] args) {
                    int number = 8;
                    halfTheFun(11);
                    halfTheFun(2 - 3 + 2 * 8);
                    halfTheFun(number);
                    System.out.println("number = " + number);
                }
            }
    
  2. What output is produced by the following program?
            public class Mystery1 {
                public static void main(String[] args) {
                    String whom = "her";
                    String who = "him";
                    String it = "who";
                    String he = "it";
                    String she = "whom";
            
                    sentence(he, she, it);
                    sentence(she, he, who);
                    sentence(who, she, who);
                    sentence(it, "stu", "boo");
                    sentence(it, whom, who);
                }
            
                public static void sentence(String she, String who, String whom) {
                    System.out.println(who + " and " + whom + " like " + she);
                }
            }
    
  3. What output is produced by the following program?
            public class Mystery2 {
                public static void main(String[] args) {
                    String one = "two";
                    String two = "three";
                    String three = "1";
                    int number = 20;
            
                    sentence(one, two, 3);
                    sentence(two, three, 14);
                    sentence(three, three, number + 1);
                    sentence(three, two, 1);
                    sentence("eight", three, number/2);
                }
            
                public static void sentence(String three, String one, int number) {
                    System.out.println(one + " times " + three + " = " + (number * 2));
                }
            }
    
  4. What output is produced by the following program?
            public class AnotherMystery {
                public static void printRange(char startLetter, char endLetter) {
                    for (char letter = startLetter; letter <= endLetter; letter++)
                        System.out.print(letter);
                }
                
                public static void main(String[] args) {
                    printRange('e', 'g');
                    printRange('n', 's');
                    System.out.println();
            
                    printRange('z', 'a');
                    System.out.println();
            
                    printRange('q', 'r');
                    System.out.println();
                }
            }
    
  5. Write a static method named smallestOf3 that takes three ints as parameters and returns the value out of the three that is the smallest. A call of smallestOf3(3, -2, 7) would return 7, and a call of smallestOf3(19, 27, 6) would return 6. (Hint: the Math class may help you.)
  6. Write a static method named largerAbsVal that takes two ints as parameters and returns the larger of the two absolute values. A call of largerAbsVal(11, 2) would return 11, and a call of largerAbsVal(4, -5) would return 5.
  7. Write a variation of the method from the last exercise called largestAbsVal that takes three ints returns the largest of their three absolute values. For example, a call of largestAbsVal(7, -2, -11) would return 11, and a call of largestAbsVal(-4, 5, 2) would return 5.
  8. Write a static method named printNumbers that accepts a maximum number as an argument and prints each number from 1 up to that maximum, inclusive, boxed by square brackets. For example, consider the following calls:
        printNumbers(15);
        printNumbers(5);
    
    The preceding calls should produce the following output:
        [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15]
        [1] [2] [3] [4] [5]
    
    You may assume that the value passed to printNumbers has a value of 1 or greater.
  9. Write a static method named printPowersOf2 that accepts a maximum number as an argument and prints each power of 2 from 2^0 (1) up to that maximum power, inclusive. For example, consider the following calls: printPowersOf2(3); printPowersOf2(10); The preceding calls should produce the following output:
            1 2 4 8
            1 2 4 8 16 32 64 128 256 512 1024
    
    You may assume that the value passed to printPowersOf2 has a value of 0 or greater. (The Math class may help you with this problem. If you use it, you may need to cast its results from double to int so that you don't see a .0 after each number in your output. Also, can you write this program without using the Math class?)
  10. Write a static method named printPowersOfN that accepts a base and an exponent as arguments and prints each power of the base from base^0 (1) up to that maximum power, inclusive. For example, consider the following calls: printPowersOfN(4, 3); printPowersOfN(5, 6); printPowersOfN(-2, 8); The preceding calls should produce the following output:
            1 4 16 64
            1 5 25 125 625 3125 15625
            1 -2 4 -8 16 -32 64 -128 256
    
    You may assume that the exponent passed to printPowersOfN has a value of 0 or greater. (The Math class may help you with this problem. If you use it, you may need to cast its results from double to int so that you don't see a .0 after each number in your output. Also, can you write this program without using the Math class?)
  11. Write a static method named quadratic that solves quadratic equations and prints their roots. Recall that a quadratic equation is a polynomial equation in terms of a variable x of the form ax2 + bx + c = 0. The formula for solving a quadratic equation is:

    Here are some example equations and their roots:

    x2 + 3x + 2: x = 2, x = 1
    x2 - 7x + 12: x = -4, x = -3

    Your method should accept the coefficients a, b, and c as parameters and should print the roots of the equation. You may assume that the equation has two real roots, though mathematically this is not always the case.

  12. Write a static method named hypotenuseLength that accepts two double values as its arguments, treats them as the lengths of two perpendicular sides a and b of a right triangle, and computes and returns the length of the triangle's hypotenuse c. The length of a hypotenuse is computed using the Pythagorean theorem:


    Your program should work with the following test code, outputting hypotenuse lengths of 5.0 and 13.0 respectively:

    double c = hypotenuseLength(3.0, 4.0); System.out.println("Hypotenuse length is: " + c); c = hypotenuseLength(5.0, 12.0); System.out.println("Hypotenuse length is: " + c);
  13. What is the value of the string referred to by variable str after the following code? String str = "Mostly harmless"; str = str.substring(8, 11);
  14. What is the value of the string referred to by variable str1 after the following code? String str1 = "Arcturan Megadonkey"; String str2 = "Sirius Cybernetics Corporation"; str1 = str1.toUpperCase(); str1 = str1.substring(10, 14); str1 = str1 + str2.charAt(17);
  15. What is the value of the string referred to by variable str2 after the following code? String str1 = "Arcturan Megadonkey"; String str2 = "Sirius Cybernetics Corporation"; int index = str2.indexOf('c'); str2 = str2.substring(9, index);
  16. What are the values of the string referred to by variables str1 and str2 after the following code? String str1 = "Project Mayhem"; String str2 = "Phantasmagorical"; str1.toUpperCase(); str2.substring(7, 12);
  17. Write a static method named vertical that accepts a String as its parameter and prints each letter of the string on separate lines. For example, a call of vertical("hey now") should produce the following output:
            h
            e
            y
    
            n
            o
            w
    
  18. Write a static method named reverse that accepts a String as its parameter and returns a String that is the parameter reversed, with the characters in opposite order. For example, a call of reverse("hello there!") should return "!ereht olleh". If the empty string is passed to reverse, the empty string should be returned.
  19. What is the output of the following program?
            import java.awt.Point;
            
            public class PointMystery {
                public static void method1(Point p) {
                    int temp = p.x;
                    p.x = p.y;
                    p.y = temp;
                }
                
                public static Point method2(Point p) {
                    p = new Point(-p.y, -p.x);
                    return p;
                }
                
                public static void main(String[] args) {
                    Point p1 = new Point(5, 2);
                    Point p2 = new Point(-3, 6);
                    
                    method1(p1);
                    method2(p2);
                    Point p3 = method2(p1);
            
                    System.out.println("(" + p1.x + ", " + p1.y + ")");
                    System.out.println("(" + p2.x + ", " + p2.y + ")");
                    System.out.println("(" + p3.x + ", " + p3.y + ")");
                }
            }
  20. Write a static method named swapPoints that takes two Points as arguments and swaps their values with each other. Consider the following example code that calls swapPoints: Point p1 = new Point(5, 2); Point p2 = new Point(-3, 6); swapPoints(p1, p2); System.out.println("(" + p1.x + ", " + p1.y + ")"); System.out.println("(" + p2.x + ", " + p2.y + ")"); The output produced from the above code should be:
            (-3, 6)
            (5, 2)
    
  21. What sort of figure will be drawn by the following program? Can you draw an approximate picture that will match its appearance without running it first?
            import java.awt.*;
            
            public class Draw7 {
                public static void main(String[] args) {
                    int size = 200;
                    DrawingPanel panel = new DrawingPanel(size, size);
                    panel.setBackground(Color.WHITE);
                    Graphics g = panel.getGraphics();
                    g.setColor(Color.BLACK);
                    
                    int xy = 0;
                    
                    for (int i = 0; i < 20; i++) {
                        g.drawOval(xy, xy, size - xy, size - xy);
                        xy += 10;
                    }
                }
            }
    
  22. Write a program that uses the DrawingPanel to draw the following figure:

    The window is 220 pixels wide and 150 pixels tall. The background is yellow. There are two blue ovals of size 40 x 40 pixels. They are 80 pixels apart, and the left oval is located at position (50, 25). There is a red square whose top two corners exactly intersect the centers of the two ovals. Lastly, there is a black horizontal line through the center of the square.

  23. Modify your program from the previous exercise so that the figure is drawn by a method called drawFigure. The method should accept two arguments: The Graphics g of the DrawingPanel on which to draw, and a Point specifying the location of the top-left corner of the figure. Use the following heading for your method: public static void drawFigure(Graphics g, Point location) Set your DrawingPanel's size to 450 x 150 pixels, and use your drawFigure method to place two figures on it. One figure should be at position (50, 25) and the other should be at position (250, 45).

  24. Write a program with a static method called showDesign that uses the DrawingPanel to draw the following figure:

    The window is 200 pixels wide and 200 pixels tall. The background is white and the foreground is black. There are 20 pixels between each of the 4 rectangles, and the rectangles are concentric (their centers are at the same point). Use a loop to draw the repeated rectangles.

  25. Modify your showDesign method from the previous exercise so that it accepts parameters for the window width and height and displays the rectangles at the appropriate sizes. For example, if your showDesign method was called with values of 300 and 100, the window would have the following appearance:


  26. Write a program that uses the DrawingPanel to draw the following spiral figure:

    The window is 170 pixels wide and 170 pixels tall. The background is white and the foreground is black. The spiral line begins at point (0, 10) and spirals inward. There are 10 pixels between each arm of the spiral. 8 spirals are made in total. The initial spiral touches points (0, 10), (160, 10), (160, 160), (10, 160), and (10, 20).

    For additional challenge, parameterize your program with parameters such as the window size and the number of spiral loops desired.

3.9 Programming Problems

  1. Write a program that produces Christmas trees as output. It should have a method with two parameters: one for the number of segments in the tree and one for the height of each segment. For example, the tree on the left below has 3 segments of height 4 and the one on the right has 2 segments of height 5.

                 *                        *
                ***                      ***
               *****                    *****
              *******                  *******
                ***                   *********
               *****                     ***
              *******                   *****
             *********                 *******
               *****                  *********
              *******                ***********
             *********                    *
            ***********                   *
                 *                     *******
                 *
              *******
    
  2. A certain bank offers 12.7% interest on loans compounded annually. Create a table that shows how much money a person will accumulate over a period of 25 years assuming an initial investment of $1000 and assuming that $100 is deposited each year after the first. Your table should indicate for each year the current balance, the interest, the new deposit, and the new balance.

  3. Write a program that shows the total number of presents received on each day according to the song "The Twelve Days of Christmas," as indicated below.

    Day Presents Received Total Presents
    111
    223
    336
    4410
    5515
    .........


Stuart Reges
Marty Stepp
Last modified: Tue Jan 3 01:21:51 PDT 2006

Chapter 4
Interactive Programs and Conditional Execution

Copyright © 2005 by Stuart Reges and Marty Stepp

4.1 Introduction

The last three chapters examined programs that produce output. This chapter examines the more interesting side of communication: programs that interact with a user. Because humans and computers don't communicate very well, these programs are the most challenging to write.

The chapter begins with an examination of how values are obtained from the user and the basic structure of an interactive program. After covering this basic background, you will learn an important new control structure: the if/else. Called a conditional or selection construct, it allows you to select from among alternatives depending upon certain conditions. This new structure, like the for loop, is so powerful that you will wonder how you managed to write programs without it.

The chapter ends by discussing how to indicate that a serious error has occurred during program execution.

4.2 The Scanner Class

We have seen that by calling System.out.println and System.out.print we can produce output in the console window rather easily. We can also write programs that pause and wait for the user to type a response. Such programs are known as interactive programs and the responses typed by the user are known as console input.

Console Input

Responses typed by the user in the console window when an interactive program pauses for input.

When you refer to System.out, you are accessing an object in the System class known as the standard output stream, or "standard out" for short. There is a corresponding object for standard input known as System.in. But Java wasn't designed for console input and System.in has never been particularly easy to use for console input.

The new release of Java (version 5.0) has a class called Scanner that simplifies reading from the console and reading from files. It is part of the java.util package, so we will have to remember to include an import declaration in our programs that use Scanner.

To use a Scanner you first have to construct one. You can construct a Scanner by passing it a reference to an input stream. To read from the console window, we pass it System.in:

Scanner console = new Scanner(System.in); Once constructed, you can ask the Scanner to return a value of a particular type. There are a number of different methods that all begin with the word "next" to obtain these various types of values.

Scanner Methods
Method Description
next()reads and returns the next token as a String
nextDouble()reads and returns a double value
nextInt()reads and returns an int value
nextLine()reads and returns the next line of input as a String

Typically we will use a variable to keep track of the value returned by one of these methods. For example, we might say:

int first = console.nextInt(); int second = console.nextInt(); int third = console.nextInt(); These statements read in values for three variables. When your program encounters these "next" calls on Scanner, it pauses and waits for a user to type a line of input in the console window. Whenever the computer pauses for input, it will pause for an entire line of input.

When executing these "next" methods, the computer will wait until it has been given the correct amount of data. If a user types in only two numbers when there are three calls on nextInt(), the computer will pause and wait for the user to type a second line. If after reading this second line, it doesn't have enough values, the computer will wait for the user to type a third line, and so on.

You can use the Scanner class to read input line by line using the nextLine() method, although we won't be using nextLine very much for now. The other "next" methods are all token based.

Token

A single element of input (e.g., one word, one number).

By default, the scanner uses whitespace to separate tokens.

Whitespace

Spaces, tab characters and "new line" characters.

Scanner looks at what the user types and uses the whitespace on the input line to break it up into individual tokens. For example, the following line of input:

        hello      there. how are         "you?"  all-one-token

would be split into six tokens:

        hello
        there.
        how
        are
        "you?"
        all-one-token
Notice that Scanner includes punctuation characters like periods, question marks, dashes and quotation marks in the tokens it generates. Also notice that we got just one token for "all-one-token". That's because there is no whitespace in the middle to break it up into different tokens. You can control how Scanner turns things into tokens (a process called "tokenizing" the input), but we won't be doing anything that fancy.

If a user types something that isn't an integer when you're calling nextInt(), such as XYZZY, the Scanner object generates something called an exception.

Exception

An error that prevents a program from continuing its normal execution.

We say that an exception is "thrown" when an error is encountered. When an exception is thrown, Java looks to see if you have written code to handle it. If not, program execution is halted and you will see what is known as a "stack trace" or "back trace." The stack trace shows you the series of methods that have been called in reverse order. We will see more about exceptions in a later chapter. For now, though, we will assume good user behavior.

4.2.1 An Example Interactive Program

Now that you know how to read information from the user, you can write your first interactive program. Here is a simple program that reads in two integers and does some calculations.

        // This program prompts the user for two integers and reports various
        // arithmetic calculations using those numbers.
        
        import java.util.*;
        
        public class SimpleMath {
            public static void main(String[] args) {
                System.out.println("This program does some simple math on two");
                System.out.println("integers.  If you give me two numbers to work");
                System.out.println("with, I'll do some calculations for you.");
                System.out.println();
        
                Scanner console = new Scanner(System.in);
        
                System.out.print("Give me two numbers---> ");
                int first = console.nextInt();
                int second = console.nextInt();
                System.out.println();
        
                System.out.println(first + " + " + second + " = " + (first + second));
                System.out.println(first + " - " + second + " = " + (first - second));
                System.out.println(first + " * " + second + " = " + (first * second));
                System.out.println(first + " / " + second + " = " + (first / second));
                System.out.println(first + " % " + second + " = " + (first % second));
                System.out.println(second + " / " + first + " = " + (second / first));
                System.out.println(second + " % " + first + " = " + (second % first));
            }
        }
The program starts by explaining what is going to happen. This is essential for interactive programs. If calls on Scanner "next" methods appear first, users would be expected to type input without knowing what to type. The fourth statement generates a blank line of output to separate the introductory text from what will follow.

The fifth statement constructs the Scanner object and the sixth is a prompt, a request for information from the user. We use a print statement instead of a println so that the user will type the values on the same line as the prompt (i.e., to the right of the prompt).

After prompting for values, the program reads in two numbers and writes out the results. The execution of this program would go something like this:

        This program does some simple math on two
        integers.  If you give me two numbers to work
        with, I'll do some calculations for you.
        
        Give me two numbers---> 29 536
        
        29 + 536 = 565
        29 - 536 = -507
        29 * 536 = 15544
        29 / 536 = 0
        29 % 536 = 29
        536 / 29 = 18
        536 % 29 = 14

4.3 Cumulative Sum

A common programming task is finding the sum of a series of numbers. The program in the last section added two numbers together by storing each value in its own variable. It would not be convenient, however, to define one hundred variables to add together one hundred numbers.

The trick is to keep a running tally and to process one number at a time. If you have a variable called sum, for example, you would add in the next number by saying:

sum = sum + next; or using the shorthand assignment operator:

sum += next; This statement says to take the old value of sum, add the value of a variable called next, and store this as the new value of sum. This operation is performed for each number to be summed. There is a slight problem when executing this statement for the first number, because there is no old value of sum the first time around. To get around this, you initialize sum to a value that will not affect the answer--zero.

Rather than obtaining all the numbers first and then doing all the processing, you alternate obtaining a number and processing it. This allows you to use a single variable next rather than many variables.

Here is a pseudocode description of the cumulative sum algorithm:

        sum = 0.
        for (all numbers to sum) {
            obtain "next".
            sum += next.
        }
To implement this algorithm, you must decide how many times to go through the loop and how to obtain a next value. Here is an interactive program using both doubles and ints that prompts the user for how many numbers to sum together and for the numbers themselves.

        // This program adds together a series of numbers for the user.  It first
        // asks the user how many numbers to sum, then asks for the series of
        // numbers and reports the sum.
        
        import java.util.*;
        
        public class FindSum {
            public static void main(String[] args) {
                System.out.println("This program will add together a series of real");
                System.out.println("numbers for you.");
                System.out.println();
        
                Scanner console = new Scanner(System.in);
        
                System.out.print("How many numbers do you want to add together? ");
                int totalNumber = console.nextInt();
        
                double sum = 0;
                for (int i = 1; i <= totalNumber; i++) {
                    System.out.print("    next--> ");
                    double next = console.nextDouble();
                    sum += next;
                }
                System.out.println("Sum = " + sum);
            }
        }
The program will execute something like this:

        This program will add together a series of real
        numbers for you.
        
        How many numbers do you want to add together? 5
            next--> 13.5
            next--> 12.85
            next--> -109.8
            next--> 102.45
            next--> 213.8
        Sum = 232.8
The cumulative sum algorithm and variations on it will be useful in many of the programming tasks you solve. How would you do a cumulative product, for example? Here is the pseudocode:

        product = 1.
        for (all numbers to multiply) {
            obtain "next".
            product *= next.
        }

4.4 If/Else Statements

The programs you write will often make decisions between different possible actions. The simplest way to introduce a decision branch into a program is using an if statement.

if (<test>) { <statement>; <statement>; ... <statement>; } The if statement, like the for loop, is a control structure. Notice that we once again see a Java keyword ("if") followed by parentheses and a set of curly braces enclosing a series of controlled statements.

The diagram below indicates the flow of control for the simple if statement. It performs the test and if it evalutes to true, it executes the controlled statements. If the test evaluates to false, we skip the controlled statements.

      +---------------+
      | is test true? |--yes--->---+
      +---------------+            |
              |                    |
             no                    V
              |        +-----------------------+
              |        |     execute the       |
              V        | controlled statements |
              |        +-----------------------+
              |                    |
              V                    V
    +--------------------+         |
    | execute statement  |<---<----+
    | after if statement |
    +--------------------+
For example:

        if (number > 0) {
            answer = Math.sqrt(number);
        }
By putting the assignment statement inside the if, we guarantee that it will be executed only if the test evaluates to true. This test is useful because you wouldn't want to ask for the square root of a negative number. The assignment statement is controlled by the if and, therefore, is indented to indicate the structure.

Another form of the if statement includes an else clause:

if (<test>) { <statement>; <statement>; ... <statement>; } else { <statement>; ... <statement>; <statement>; } This control structure is unusual in that it has two sets of controlled statements and two different keywords (if and else). The diagram below indicates the flow of control. It performs the test and depending upon whether it evalutes to true or false, it executes one or the other groups of statements.

                  +---------------+
                  | is test true? |
                  +---------------+
                        /   \
                       /     \
                     yes      no
                     /         \
                    /           \
                   V             V
+-----------------------+   +-----------------------+
|   execute the "if"    |   |  execute the "else"   |
| controlled statements |   | controlled statements |
+-----------------------+   +-----------------------+
                   \             /
                    \           /
                     \         /
                      V       V
               +--------------------+
               | execute statement  |
               | after if statement |
               +--------------------+
Thus, the if/else controls two different sets of statements, one to be executed when the test evaluates to true and the other to be executed when the test evaluates to false. For example:

        if (number >= 0) {
            answer = Math.sqrt(number);
        } else {
            answer = 0;
        }
As with the for loop, if you have a single statement to execute, then you don't need to include the curly braces. But the Sun convention is to include the curly braces even if you don't need them and we follow that convention in this book.

The if/else statements are controlled by a test. Simple tests compare two expressions to see if they are related in some way. Such a test is itself an expression that returns either true or false and is of the following form:

<expression> <relational operator> <expression> To evaluate such a test, you first evaluate the two expressions and then see if the given relation holds between the value on the left and the value on the right. If the relation does hold, the test evaluates to true. If not, the test evaluates to false.

The relational operators are:

Relational Operators
Operator Meaning Example Value
== equals 2 + 2 == 4 true
!= not equals 3.2 != 4.1 true
< less than 4 < 3 false
> greater than 4 > 3 true
<= less than or equals 2 <= 0 false
>= greater than or equals 2.4 >= 1.6 true

Notice that the test for equality involves two equals characters in a row ("=="). This is to distinguish it from the assignment operator ("=").

Because you use the relational operators as a new way of forming expressions, you must reconsider precedence. The following expression is made up of the constants 3, 2, 9, and the operations plus, times and equals.

3 + 2 * 2 == 9 Which of the operations is performed first? Because the relational operators have a lower level of precedence than the arithmetic operators, the answer is that the times is performed first, then the plus, then the "equals" test. In other words, Java will perform all of the "math" first before it tests for these relationships. This precedence scheme frees you from parenthesizing the left and right sides of a test using a relational operator. Using these precedence rules, the expression above is evaluated as follows:

        3 + 2 * 2 == 9
            \---/
        3 +   4   == 9
        \-----/
           7      == 9
           \---------/
              false
You can put arbitrary expressions on either side of the relational operator as long as they are of a compatible type. Here is a test with complex expressions on either side:

        (2 - 3 * 8) /  (435 % (7 * 2)) <= 3.8 - 4.5 / (2.2 * 3.8)
Below is an updated version of the precedence chart that includes these new operators. You will see that technically the equality comparisons are considered at a slightly different level of precedence than the other relational operators, but both sets of operators have a lower precedence than the arithmetic operators.

Java Operator Precedence
Description Operators
unary operators ++, --, +, -
multiplicative operators *, /, %
additive operators +, -
relational operators <, >, <=, >=
equality operators ==, !=
assignment operators =, +=, -=, *=, /=, %=

4.4.1 A Sample Program With Ifs

Let's put together what we have learned about the Scanner class with some simple if statements. Suppose that we want to read a sequence of numbers and compute the average. We can ask the user how many numbers to work with and use a for loop to prompt for each number. If we want to compute the average, we'll have to compute the overall sum. The code we saw for cumulative sum earlier in the chapter will work nicely for that. Then we can compute the average as the sum divided by the number of numbers, as in:

System.out.println("average = " + sum/totalNumber); There is one minor problem with this. Suppose that when we ask the user how many numbers to process, the user says to process 0 numbers. That would mean that we never enter our cumulative sum loop and we try to compute the value of 0 divided by 0. Java would print out that the average is "NaN". This cryptic message is short for "Not a Number". It would be better to print out some other kind of message that would indicate that there weren't any numbers to average. We can use an if/else for this purpose:

        if (totalNumber <= 0) {
            System.out.println("No numbers to average");
        } else {
            System.out.println("average = " + sum/totalNumber);
        }
We can fit in another use of if by counting how many negative numbers are entered by the user. You will often find yourself wanting to count how many times something occurs in a program. This is easy to accomplish with an if and an integer variable called a counter. You start by initializing the counter to 0:

int negatives = 0; You can use any name you want for the variable. Here I used the name "negatives" because that is what we're counting. The other essential ingredient is to increment the counter inside the loop if it passes the test we're interested in:

        if (next < 0) {
            negatives++;
        }
Putting this all together and adding an introduction and user prompts, we end up with the following complete program.
                                                                                                                                                                                                                                                                       
        import java.util.*;
        
        public class ExamineNumbers {
            public static void main(String[] args) {
                System.out.println("This program examines a sequence of numbers to");
                System.out.println("find the average as well as counting how many");
                System.out.println("are negative.");
                System.out.println();
        
                Scanner console = new Scanner(System.in);
        
                System.out.print("How many numbers do you want me to examine? ");
                int totalNumber = console.nextInt();
        
                int negatives = 0;
                double sum = 0.0;
                for (int i = 1; i <= totalNumber; i++) {
                    System.out.print("    next number? ");
                    double next = console.nextDouble();
                    sum += next;
                    if (next < 0) {
                        negatives++;
                    }
                }
                System.out.println();
        
                if (totalNumber <= 0) {
                    System.out.println("No numbers to average");
                } else {
                    System.out.println("average = " + sum/totalNumber);
                }
                System.out.println("# of negatives = " + negatives);
            }
        }
The program will execute something like this:

        This program examines a sequence of numbers to
        find the average as well as counting how many
        are negative.
        
        How many numbers do you want me to examine? 8
            next number? 2.7
            next number? 3.4
            next number? -19.7
            next number? 208.3
            next number? -42.7
            next number? 8
            next number? 92.3
            next number? -17.2
        
        average = 29.387500000000003
        # of negatives = 3

4.4.2 Nested If/Else

Many beginners write code that looks like this:

if (<test1>) { <statement1>; } if (<test2>) { <statement2>; } if (<test3>) { <statement3>; } This sequential structure is appropriate if you want to execute any combination of the three statements. You might write this code in a program for a questionnaire with three optional parts, any combination of which might be applicable for a given person. Often, however, you only want to execute one of a series of statements.

In such cases, it is better to nest the ifs:

if (<test1>) { <statement1>; } else { if (<test2>) { <statement2>; } else { if (<test3>) { <statement3>; } } } With this construct, you can be sure that one statement at most is executed. The first test to return true has its corresponding statement executed. If no tests return true, no statement is executed. If this is your objective this construct is more appropriate than the sequential if statements. It reduces the likelihood of errors and simplifies the testing process.

As you can see, nesting if statements like this leads to a lot of indentation. The indentation also isn't all that helpful because we really think of this as choosing one of a number of alternatives. K&R style has a solution for this as well. If an else is followed by an if, we put them on the same line:


if (<test1>) { <statement1>; } else if (<test2>) { <statement2>; } else if (<test3>) { <statement3>; } This way the various statements that we are choosing from all appear at the same level of indentation. Sun says that nested if/else statements should be indented in this way. There is a variation of this structure in which the final statement is controlled by an else instead of a test.

if (<test1>) { <statement1>; } else if (<test2>) { <statement2>; } else { <statement3>; } In this construct the final branch will always be taken when the earlier tests fail and, thus, the construct will always execute exactly one of the three statements. To explore these variations, consider the task of writing out whether a number is positive, negative, or zero.

You could structure this as three simple if statements as follows.

        if (number > 0) {
            System.out.println("Number is positive.");
        }
        if (number == 0) {
            System.out.println("Number is zero.");
        }
        if (number < 0) {
            System.out.println("Number is negative.");
        }
To determine how many of the printlns are potentially executed, you have to stop and think about the tests being performed. You shouldn't have to put that much effort into understanding this code. It would be more efficient and more clear to nest these if statements:

        if (number > 0) {
            System.out.println("Number is positive.");
        } else if (number == 0) {
            System.out.println("Number is zero.");
        } else if (number < 0) {
            System.out.println("Number is negative.");
        }
This solution, however, is not the best. You know that you want to execute one and only one println statement. This nested structure does not preclude the possibility of no statement being executed. If all three tests fail, no statement would be executed. With these particular tests that will never happen. If a number is neither positive nor zero, it must be negative. Thus, the final test here is unnecessary and misleading. You must think about the tests in order to know whether or not it is possible for all three of these branches to be skipped.

The best solution is the nested if/else with a final branch that is always taken if the first two tests fail:

        if (number > 0) {
            System.out.println("Number is positive.");
        } else if (number == 0) {
            System.out.println("Number is zero.");
        } else {
            System.out.println("Number is negative.");
        }
You can glance at this construct and see that exactly one println will be executed. You don't have to look at the tests being performed in order to realize this, it is a property of this kind of nested if/else. This is a good place to include a comment to make it painfully clear what is going on:

        if (number > 0) {
            System.out.println("Number is positive.");
        } else if (number == 0) {
            System.out.println("Number is zero.");
        } else {  // number must be negative
            System.out.println("Number is negative.");
        }
When you find yourself writing code to pick among alternatives like these, you have to analyze the particular problem to figure out how many of the branches you potentially want to execute. If any combination can be taken, use sequential if statements. If you want one or none of the branches to be taken, use the nested if/else with a test for each statement. If you want exactly one branch to be taken, use the nested if/else with a final branch controlled by an else rather than by a test.

The table below summarizes these choices.

If/Else Options
Situation Construct Basic form
You want to execute any combination of controlled statements sequential ifs if (<test1>) { <statement1>; } if (<test2>) { <statement2>; } if (<test3>) { <statement3>; }
You want to execute 0 or 1 of the controlled statements nested ifs ending in test if (<test1>) { <statement1>; } else if (<test2>) { <statement2>; } else if (<test3>) { <statement3>; }
You want to execute exactly 1 of the controlled statements nested ifs ending in else if (<test1>) { <statement1>; } else if (<test2>) { <statement2>; } else { <statement3>; }

4.4.3 Factoring if/else Statements

Suppose you are writing a program that plays a betting game with a user and you want to give different warnings about how much cash the user has. The following nested if/else distinguishes three different cases: money less than $500, which is considered low; money between $500 and $1000, which is considered okay; and money over $1000, which is considered good. Notice that the user is given different advice in each branch:

if (money < 500) { System.out.println("You have, $" + money + " left."); System.out.print("Your cash is dangerously low. Bet carefully."); System.out.print("How much do you want to bet? "); bet = console.nextInt(); } else if (money < 1000) { System.out.println("You have, $" + money + " left."); System.out.print("Your cash is somewhat low. Bet moderately."); System.out.print("How much do you want to bet? "); bet = console.nextInt(); } else { System.out.println("You have, $" + money + " left."); System.out.print("Your cash is in good shape. Bet liberally."); System.out.print("How much do you want to bet? "); bet = console.nextInt(); } This construct is repetitious and can be reduced using a technique called factoring. With it, you factor out common pieces of code from the different branches of the if/else. The technique is simple. The above construct creates three different branches depending upon the value of the variable money. You start by writing down the series of actions being performed in each branch and comparing them.

                            Depending upon money:
                                        |
                +-----------------------+-----------------------+
                |                       |                       |
           money < 500         500 <= money < 1000        Money >= 1000
                |                       |                       |
        Report money            Report money            Report money
        Warn about low money    Report okay money       Report good money
        Prompt for bet          Prompt for bet          Prompt for bet
        Read bet                Read bet                Read bet
You can factor at both the top and the bottom of such a construct. If you notice that the top statement in each branch is the same, you factor it out of the branching part and put it before the branch. Similarly, if the bottom statement in each branch is the same, you factor it out of the branching part and put it after the loop. You can factor the top statement in each of these branches and the bottom two statements.

                                Report money

                            Depending upon money:
                                        |
                +-----------------------+-----------------------+
                |                       |                       |
           money < 500         500 <= money < 1000        Money >= 1000
                |                       |                       |
        Warn about low money    Report okay money       Report good money

Prompt for bet Read bet

Thus, the code above can be reduced to the following which is more succinct.

System.out.println("You have, $" + money + " left."); if (money < 500) { System.out.print("Your cash is dangerously low. Bet carefully."); } else if (money < 1000) { System.out.print("Your cash is somewhat low. Bet moderately."); } else { System.out.print("Your cash is in good shape. Bet liberally."); } System.out.print("How much do you want to bet? "); bet = console.nextInt();

4.5 Testing Objects for Equality

The previous section mentioned that you can use the "==" and "!=" operators to test for equality and non-equality, respectively. Unfortunately, these operators do not work properly when testing for equality and non-equality of objects like Strings and Points, so we will have to learn a new way to test objects for equality.

Suppose that we were to execute the following lines of code.

Point p1 = new Point(3, 4); Point p2 = new Point(3, 4); Point p3 = p2; It will lead to three variables and two objects:

                    +--------------------+
           +---+    |   +----+    +----+ |
        p1 | +-+--> | x |  3 |  y |  4 | |
           +---+    |   +----+    +----+ |
                    +--------------------+

                    +--------------------+
           +---+    |   +----+    +----+ |
        p2 | +-+--> | x |  3 |  y |  4 | |
           +---+    |   +----+    +----+ |
                    +--------------------+
                              ^
           +---+              |
        p3 | --+--------------+
           +---+
Even though we have two Point objects, they both have coordinates (3, 4). So we would normally think of these points as being equal. But if we were to test whether (p1 == p2) the answer would be false. In performing this test, Java sees whether the variables p1 and p2 store exactly the same value. These variables store references to Point objects, but they aren't the Point objects themselves. So this question is asking whether p1 and p2 refer to the same object. Since they refer to different objects (since they point in different directions), the "==" test evaluates to false.

If we were instead to ask whether (p2 == p3) the answer would be true. That's because p2 and p3 store the same value. In other words, they both refer to the same object.

Java provides a second way of testing for equality that is intended for testing object equality. Every Java object has a method called "equals" that takes another object as an argument. So you ask an object whether it equals another object. For example, given the variables above we could test whether:

        if (p1.equals(p2)) {
            System.out.println("points are equal");
        }
In this case we are not comparing the variables, we are talking to the objects and asking the first one to see whether it equals the second one. Here the test evaluates to true and Java would print that the points are equal.

You will want to use the equals method to test for equality of Strings. For example, you might write code like the following to read a token from the console and to call one of two different methods depending upon whether the user responded with "yes" or "no". If the user types neither word, it prints an error message.

        System.out.print("yes or no? ");
        String s = console.next();
        if (s.equals("yes")) {
            processYes();
        } else if (s.equals("no")) {
            processNo();
        } else {
            System.out.println("You didn't type yes or no");
        }
This code will not work properly with "==" tests. Java has a special variation of the equals method for the String class that ignores case differences (capital versus small letters). The method is called equalsIgnoreCase. For example, you could rewrite the code above to recognize responses like "Yes", "YES", "No", NO", yES", etc.

        System.out.print("yes or no? ");
        String s = console.next();
        if (s.equalsIgnoreCase("yes")) {
            processYes();
        } else if (s.equalsIgnoreCase("no")) {
            processNo();
        } else {
            System.out.println("You didn't type yes or no");
        }

4.6 Throwing Exceptions

At the beginning of the chapter we described the idea of an exception and mentioned that Scanner objects can "throw" an exception if they are asked to do something unreasonable. In particular, if you call the nextInt() method of a Scanner object and it finds something that is not an integer, then it throws an exception.

In a later chapter we will see how to handle exceptions. For now we just want to explore some of the ways in which exceptions can occur and how we might want to generate them in our own code. Ideally programs execute without generating any errors, but in practice various problems arise. You might ask the user for an integer and the user will accidentally or perhaps even maliciously type something that is not an integer. Or you might execute code that has a bug in it.

Below is a program that always throws an exception because it tries to compute the value of 1 divided by 0, which is mathematically undefined.

        public class CauseException {
            public static void main(String[] args) {
                int x = 1/0;
                System.out.println(x);
            }
        }
When you run the program, you get the following error message:

        Exception in thread "main" java.lang.ArithmeticException: / by zero
                at CauseException.main(CauseException.java:3)
The problem is in line 3 when you ask Java to compute a value that can't be stored as an int. What is Java supposed to do with that value? It could ignore the error and continue executing, but that may not be the right thing to do. Instead Java throws an exception that stops the program from executing and gives us a warning that an arithmetic exception occurred while executing this line of code.

It is worth noting that division by 0 does not always produce an exception. We won't get an exception if we execute this line of code instead:

        double x = 1.0/0.0;
The program executes normally and produces the output "Infinity". This is because floating-point numbers follow a standard from the IEEE (the Institute of Electical and Electronics Engineers) that defines exactly what should happen in these cases and there are special values representing infinity and something called "NaN" (Not a Number).

We may want to throw exceptions ourselves in the code we write. For example, suppose that we want to write a method for computing the factorial of an integer. The factorial is defined as follows.

        n! (which is read as "n factorial") = 1 * 2 * 3 * ... * n
We can write a Java method that uses a cumulative product to compute this result:

public static int factorial(int n) { int product = 1; for (int i = 2; i <= n; i++) { product *= i; } return product; } We could test the method for various values with a loop:

        for (int i = 0; i <= 10; i++) {
            System.out.println(i + "! = " + factorial(i));
        }
which produces the following output:
        0! = 1
        1! = 1
        2! = 2
        3! = 6
        4! = 24
        5! = 120
        6! = 720
        7! = 5040
        8! = 40320
        9! = 362880
        10! = 3628800
It seems odd that the factorial method should return 1 when you ask for 0!, but that is actually part of the mathematical definition of the factorial function. It is returning 1 because the local variable product in the factorial method is initialized to 1 and we never enter the loop when the parameter n has the value 0. So this is actually desirable behavior for 0!.

But what if we are asked to compute the factorial of a negative number? The method returns the same value of 1. The mathematical definition of factorial says that the function is undefined for negative values of n. So we shouldn't even compute an answer when n is negative. We could and should include a comment about this restriction, but what if someone calls our factorial method with a negative value anyway? This is a good place to throw an exception and Java makes it easy. Java has a class called IllegalArgumentException that is meant to cover just this case. So all we have to do is to construct an object of that type and "throw" it. Java has a special keyword "throw" that is used to throw an exception.

So at the beginning of the factorial method we introduce a test on the value of n and throw an exception if n is negative:

        if (n < 0) {
            throw new IllegalArgumentException();
        }
You can also include some text when you construct the exception that will be displayed when the exception is thrown:

        if (n < 0) {
            throw new IllegalArgumentException("negative value in factorial");
        }
Incorporating these lines of code into our definition of the method, we obtain the following:

        public static int factorial(int n) {
            if (n < 0) {
                throw new IllegalArgumentException("negative value in factorial");
            }
            int product = 1;
            for (int i = 2; i <= n; i++) {
                product *= i;
            }
            return product;
        }
We can test this with the following main method:

public static void main(String[] args) { System.out.println(factorial(-1)); } When you execute this program, it stops executing with the following message:

        Exception in thread "main" java.lang.IllegalArgumentException: negative
        value in factorial
              at Factorial2.factorial(Factorial2.java:8)
              at Factorial2.main(Factorial2.java:3)
The message indicates that the program Factorial2 stopped running because an IllegalArgumentException was thrown. The system then shows you a backward trace of how it got there. It was in line 8 of the factorial method of the Factorial2 class. It got there because of a call in line 3 of method main of the Factorial2 class. This kind of information is very helpful in figuring out where the bugs are in your programs.

This is an example of "defensive programming." We don't intend to have bugs in the programs we write, but we're only human, so we want to build in mechanisms that will give us feedback when we make mistakes. Testing the values passed to methods and throwing IllegalArgumentException when a value is not appropriate is a great way to provide that feedback.

4.7 Revisiting Methods that Return Values

In chapter 3 we saw examples of simple calculating methods that would return a value, as in this method for coverting feet to miles:

        public static double miles(double feet) {
            return feet / 5280.0;
        }
Now that we know how to write if/else statements, we can have much more interesting examples involving return values. For example, we saw that the Math class has a method called max that returns the larger of two values. There are actually two different versions of the method, one that finds the max of two ints and one that finds the max of two doubles. Let's write our own version of the one that returns the larger of two ints.

The method takes two ints as parameters and returns the max of the two, so its header will look like this:

        public static int max(int x, int y) {
            ...
        }
We either want to return x or return y depending upon which one is larger. This is a perfect place to use an if/else:

        public static int max(int x, int y) {
            if (x > y) {
                return x;
            } else {
                return y;
            }
        }
This begins by testing whether x is greater than y. If it is, it executes the first branch by returning x. If not, it executes the else branch by returning y. There are three different cases to consider: x might be larger, y might be larger, or they might be equal. The code above executes the else branch when the values are equal, but it doesn't matter which return statement is executed when they are equal.

Remember that when Java executes a return statement, the method stops executing. It's like a command to Java to "get out of this method right now." That means that this method can also be written as follows:

        public static int max(int x, int y) {
            if (x > y) {
                return x;
            }
            return y;
        }
This version is equivalent in behavior because the statment "return x" inside the if statement will cause Java to exit the method immediately and not to execute the "return y" statement that comes after the if. If, on the other hand, we don't enter the if, then we proceed to the statement that follows it (return y).

Whether you use the first form or the second depends somewhat on personal taste. The if/else makes it more clear that the method is choosing between two alternatives. But some people prefer the second because it is shorter. Unfortunately, we can't rely on Sun to break the tie because the Java source code expresses this using something known as the "ternary operator" (a minor detail of Java that you can read about by googling "Java ternary operator").

4.8 Chapter Summary

 

4.9 Self-Check Exercises

  1. Write Java code to read an integer from the user, then print that number multiplied by 2. You may assume that the user types a valid integer.
  2. Write Java code that prompts the user to enter his/her full name, then prints the name in the opposite order. Here is an example dialogue with the user:
            Please enter your full name: Sammy Jankis
            Your name in reverse order is Jankis, Sammy
    
  3. Write Java code that prompts the user for a phrase and a number of times, then prints the phrase that many times. Here is an example dialogue with the user:
            What is your phrase? His name is Robert Paulson.
            How many times should I repeat the phrase? 3
    
            His name is Robert Paulson.
            His name is Robert Paulson.
            His name is Robert Paulson.
    
  4. Write a piece of code that calculates a student's grade average. The user will type a line of input containing the student's name, then a number of scores, followed by that many integer scores. Here are two example dialogues:
            Enter a student record: Maria 5 72 91 84 89 78
            Maria's grade is 82.8
    
            Enter a student record: Jordan 4 86 71 62 90
            Jordan's grade is 77.25
    
    For example, Maria's grade is 82.8 because her average of (72 + 91 + 84 + 89 + 78) / 5 equals 82.8.
  5. Translate each of the following English statements into logical tests that could be used in an if/else statement. Write the appropriate if statement with your logical test. Assume that three int variables x, y, and z have been declared.
  6. What are the results of the following relational expressions? int x = 4; int y = -3; int z = 4;
  7. Consider the following Java method, which is written incorrectly:
        // This method should return how many of its three
        // arguments are odd numbers.
        public static void printNumOdd(int n1, int n2, int n3) {
            int count = 0;
    
            if (n1 % 2 == 1) {
                count++;
            } else if (n2 % 2 == 1) {
                count++;
            } else if (n3 % 2 == 1) {
                count++;
            }
    
            System.out.println(count + " of the 3 numbers are odd.");
        }
    
    What is wrong with the code for printNumOdd? Under what cases will it print the correct answer, and when will it print an incorrect answer?

    What should be changed to fix the code? Can you think of a way to write the code correctly without any if/else statements?

  8. Write Java code to read an integer from the user, then prints "even" if that number is an even number or "odd" otherwise. You may assume that the user types a valid integer.
  9. Write Java code that asks the user to enter numbers, then prints the smallest and largest of all the numbers typed in by the user. You may assume the user enters a valid number greater than 0 for the number of numbers to read. Here is an example dialogue:
        How many numbers do you want to enter? 4
            Number 1: 5
            Number 2: 11
            Number 3: -2
            Number 4: 3
        Smallest = -2
        Largest = 11
    
  10. Write a method named medianOf3 that takes three integers as arguments and that returns the median of the three integers (i.e., the value in the middle of the three).
  11. Write a method named printRange that accepts two integers as arguments and prints the sequence of numbers between the two arguments. Print an increasing sequence if the first argument is smaller than the second, and otherwise print a decreasing sequence. If the two numbers are same, no output should be produced. Here are two sample calls to printRange:
        printRange(2, 7);
        printRange(19, 11);
    
    The output produced should be the following:
        2 3 4 5 6 7
        19 18 17 16 15 14 13 12 11
    
  12. Write a method named printTriangleType that accepts three integer arguments that represent the lengths of the sides of a triangle and prints what type of triangle it is. The three types are equilateral, isosceles, and scalene. An equilateral triangle has all 3 sides the same length, an isosceles has 2 sides the same length, and a scalene has 3 sides of different lengths. Here are some example calls to printTriangleType:
        printTriangleType(5, 7, 7);
        printTriangleType(6, 6, 6);
        printTriangleType(5, 7, 8);
        printTriangleType(2, 18, 2);
    
    The output produced should be the following:
        isosceles
        equilateral
        scalene
        isosceles
    
  13. The following code is poorly structured:
        int sum = 1000;
        Scanner console = new Scanner(System.in);
        System.out.print("Is your money multiplied 1 or 2 times? ");
        int times = console.nextInt();
    
        if (times == 1) {
            System.out.print("And how much are you contributing? ");
            int donation = console.nextInt();
            sum = sum + donation;
            count1++;
            total = total + donation;
        }
        if (times == 2) {
            System.out.print("And how much are you contributing? ");
            int donation = console.nextInt();
            sum = sum + 2 * donation;
            count2++;
            total = total + donation;
        }
    
    Rewrite it so that it has a better structure and avoids redundancy. To simplify things, you may assume that the user always types a 1 or 2. (How would the code need to be modified if the user might type any number? How would it be modified if the user might type anything, even something that is not a number?)
  14. What is the output of the following code?
            Point p1 = new Point(3, -2);
            Point p2 = new Point(4, 0);
            
            if (p1.equals(p2)) {
                System.out.println("first");
            }
            
            p2.translate(-1, -2);
            if (p1.equals(p2)) {
                System.out.println("second");
            }
            
            p2 = p1;
            p2.translate(2, 5);
            if (p1.equals(p2)) {
                System.out.println("third");
            }
            
            p2 = new Point(5, -3);
            p1.translate(1, 1);
            if (p1.equals(p2)) {
                System.out.println("fourth");
            }
  15. Write a piece of code that reads a shorthand text description of a color and prints the longer equivalent. Acceptable color names are B for Blue, G for Green, and R for Red. If the user types something other than B, G, or R, print an error message. Make your program case-insensitive so that the user can type an uppercase or lowercase letter. Here are some example dialogues:
        What color do you want? B
        You have chosen Blue.
    
        What color do you want? g
        You have chosen Green.
    
        What color do you want? Bork
        Unknown color: Bork
    
  16. Write a piece of code that reads a shorthand text description of a playing card and prints the longhand equivalent. The shorthand description of the card is the card's rank (2 through 9, J, Q, K, or A) followed by its suit (C, D, H, or S). You should expand the shorthand into the form "(Rank) of (Suit)". You may assume that the user types valid input. Here are two example dialogues:
        Enter a card: 9 S
        Nine of Spades
    
        Enter a card: K C
        King of Clubs
    
  17. Write Java code that prompts the user to enter one or more words, and prints whether that String is a palindrome. A palindrome is a string that reads the same forwards as it does backwards, such as "abba" or "racecar".

    (Hint: The previous chapter had a self-check exercise asking you to write a method called reverse that would return a String in reversed order. If you completed this exercise, you may find it useful here.)

    For added challenge, make the code case-insensitive, so that words like "Abba" or "Madam" would be considered palindromes.

  18. The last chapter had a self-check exercise asking to write a method quadratic that would find the roots of a quadratic equation of the form ax2 + bx + c = 0. The quadratic method was passed a, b, and c and then applied the following quadratic formula:

    Under what conditions would this formula fail? Modify the quadratic method so that it will reject invalid values of a, b, or c by throwing an exception. (If you did not complete the exercise in the previous chapter, just write the method's header and the exception-throwing code.)

4.10 Programming Problems

  1. Write a program that prompts for an integer and reports the factors of the integer (i.e., the numbers that go evenly into it).

  2. Write a program that prompts for the lengths of the sides of a triangle and reports the three angles.

  3. Write a program that prompts for a number and displays it in Roman numerals.

  4. Write a program that prompts for a date (month, day, year) and reports the day of the week for that date. It might be helpful to know that January 1st, 1601 was a Monday.

  5. A new tax law has just been passed by the government: the first $3,000 of income is free of tax, the next $5,000 is taxed at 10%, the next $20,000 is taxed at 20%, and the rest is taxed at 30%. Write an interactive program that prompts for a user's income and reports the corresponding tax.

  6. A useful technique for catching typing errors is to use a check-digit. For example, suppose that a school assigns a six-digit number to each student. A seventh digit can be determined from the other digits, as in:

            (1 * (1st digit) + 2 * (2nd digit) + ... + 6 * (6th digit)) % 10
          
    When someone types in a student number, they type all seven digits. If the number is typed incorrectly, the check-digit will fail to match in 90% of the cases. Write an interactive program that prompts for a six-digit student number and reports the check digit for that number using the scheme described above.


Stuart Reges
Marty Stepp
Last modified: Tue Jan 3 01:30:29 PST 2006

Chapter 5
Program Logic and Indefinite Loops

Copyright © 2005 by Stuart Reges and Marty Stepp

5.1 Introduction

The chapter begins by examining a new construct called a while loop that allows you to loop an indefinite number of times. Then it discusses the type boolean in greater detail. Then it explores what are known as assertions and their relationship to understanding the logic of programs. It ends with a discussion of other loop constructs available in Java.

5.2 While Loops

The loops we have looked at so far have been fairly simple loops that execute a predictable number of times. We call them "definite" loops because you know before the loop begins executing exactly how many times it will execute. Now we want to turn our attention to indefinite loops where you don't know how many times a loop will execute. These come up often in interactive programs and file processing. For example, you don't know in advance how many times a user might want to play a game and you won't know before you look at a file exactly how much data it stores.

The while loop is the first indefinite loop we will study. It has the following syntax:

while (<test>) { <statement>; <statement>; ... <statement>; } The diagram below indicates the flow of control for the while loop. It performs its test and, if the test evaluates to true, executes the controlled statements. It continually tests again and executes again if the test evaluates to true. Only when the test evaluates to false does the loop terminate.


                                   |
                                   V
                           +---------------+
              +----<---no--| is test true? |--yes--->--+
              |            +---------------+           |
              |                ^                       V
              V                |      +-----------------------------------+
              |                |      | execute the controlled statements |
              |                |      +-----------------------------------+
              |                ^                       |
              V                |                       V
              |                |                       |
              |                +---<-----<-------<-----+
              V
    +-------------------+
    | execute statement |
    | after while loop  |
    +-------------------+
Here is an example of a while loop.

        int number = 1;
        while (number <= 200) {
            number *= 2;
        }
Recall that the "*=" operator says to "multiply the variable by" a certain amount, in this case multiplying the variable by 2. Thus, this loop initializes an integer variable called number to 1 and then doubles it while it is less than or equal to 200. On the surface, this looks like the following.

        int number = 1;
        if (number <= 200) {
            number *= 2;
        }
The difference between the two is that the while loop executes multiple times. It loops until the test evaluates to false. The if statement executes the doubling statement once, leaving number equal to 2. The while loop executes the doubling statement indefinitely until the test evaluates to false. This while loop executes the assignment statement eight times, setting number to the value 256 (the first power of 2 that is greater than 200).

The while loop tests its condition before executing the statement it controls. It performs its test at the top of the loop, as indicated in this pseudocode.

        loop
            evaluate test.
            if test evaluates to false, then exit.
            execute controlled statement.
        end of loop
Notice that a while loop will not execute its controlled statement if its test evaluates to false the first time it is evaluated.

Here is a while with two statements in the loop.

        int number = 1;
        while (number <= max) {
            System.out.println("Hi there");
            number++;
        }
This while loop is more or less equivalent to the following for loop.

        for (int number = 1; number <= max; number++) {
            System.out.println("Hi there");
        }
Suppose you want to find the smallest divisor of a number other than 1. Here are some examples of what you are looking for:

Number Factors Smallest Divisor
102 * 52
153 * 53
255 * 55
313131
777 * 117

Here is a pseudocode description of how you might do this.

        start divisor at 2.
        while (the current value of divisor does not work) {
            increase divisor.
        }
You don't start divisor at 1 because you are looking for the first divisor greater than 1. To modify this code you must be more explicit about what makes a divisor work. A divisor of a number has no remainder when the number is divided by it. You can rewrite this as:

        start divisor at 2.
        while (the remainder of number/divisor is not 0) {
            increase divisor.
        }
Here is a use for the mod operator which gives the remainder for integer division. The following while loop performs this task:

        int divisor = 2;
        while (number % divisor != 0) {
            divisor++;
        }
One problem you will undoubtedly encounter in writing your while loops is the infamous infinite loop. Consider the following code:

        int number = 1;
        while (number > 0) {
            number++;
        }
Because number begins as a positive value and the loop makes it larger, this loop will continue indefinitely. You must be careful in formulating your while loops to avoid situations where a piece of code will never finish executing. Every time you write a while loop you should consider when and how it will finish executing.

5.2.1 Fencepost Loops (aka loop and a half)

In programming we often find ourselves with a particular kind of loop known as a fencepost loop. To understand the concept, consider the following problem. You want to put up a fence that is 100 yards long and you want to have a post every 10 yards. How many posts do you need? If you do a quick division in your head, you might say that you need 10 posts, but actually you need 11 posts. That's because fences begin and end with posts. In other words, the fence looks like this:

        post, wire, post, wire, ..., post, wire, post
Because you want posts on both the far left and the far right, you can't use the following simple loop because it doesn't plant the final post.

        for (the length of the fence) {
            plant a post.
            attach some wire.
        }
Switching the order of the two operations doesn't help, because you miss the first post. The problem with this loop is that it produces the same number of posts as sections of wire, but we know we need an extra post. That's why this problem is also sometimes referred to as the "loop and a half" problem because we want to execute one half of this loop (planting a post) one extra time.

One solution is to plant one of the posts either before or after the loop. The usual solution is to do it before.

        plant a post.
        for (the length of the fence) {
            attach some wire.
            plant a post.
        }
Notice that the order of the two operations in the body of the loop is now reversed because the initial post is planted before you enter the loop.

As a simple example, consider the problem of writing out the integers between 1 and 10 separated by commas.

In other words, we want to get this output:

        1, 2, 3, 4, 5, 6, 7, 8, 9, 10
This is a classic fencepost problem because we want to write out 10 numbers but only 9 commas. In our fencepost terminology, writing a number is the "post" part of the task and writing a comma is the "wire" part. So implementing the pseudocode above, we print the first number before the loop:

        System.out.print(1);
        for (int i = 2; i <= 10; i++) {
            System.out.print(", " + i);
        }
        System.out.println();
The principle applies equally well to while loops, as we'll see in the next two sections.

5.2.2 Sentinel Loops

Suppose you want to read a series of numbers from the user and compute their sum. You could ask the user in advance how many numbers to read, but that isn't always convenient. What if the user has a long list of numbers to enter? One way around this is to pick some special input value that will signal the end of input. We call this a sentinel value.

Sentinel

A special value that signals the end of input.

For example, you could tell the user to enter the value -1 to stop entering numbers. But how do we structure our code to make use of this sentinel? In general, we want to do the following.

        sum = 0.
        while (we haven't seen the sentinel) {
            prompt & read.
            add it to the sum.
        }
But we don't want to add the sentinel value into our sum. This is a classic fencepost or loop-and-a-half problem. We want to prompt for and read the sentinel, but we don't want to add it to the sum.

We can use the usual fencepost solution of doing the first prompt and read before the loop and reversing the order of the two steps in the body of the loop.

        sum = 0.
        prompt & read.
        while (we haven't seen the sentinel) {
            add it to the sum.
            prompt & read.
        }
We can refine this pseudocode by introducing a variable for the number that we read from the user:


        sum = 0.
        prompt & read a value into n.
        while (n is not the sentinel) {
            add n to the sum.
            prompt & read a value into n.
        }
This translates fairly easily into Java code.

Scanner console = new Scanner(System.in); int sum = 0; System.out.print("next integer (-1 to quit)? "); int number = console.nextInt(); while (number != -1) { sum += number; System.out.print("next integer (-1 to quit)? "); number = console.nextInt(); } System.out.println("sum = " + sum); When this code is executed, the interaction looks like this:

        next integer (-1 to quit)? 34
        next integer (-1 to quit)? 19
        next integer (-1 to quit)? 8
        next integer (-1 to quit)? 0
        next integer (-1 to quit)? 17
        next integer (-1 to quit)? 204
        next integer (-1 to quit)? -1
        sum = 282

5.2.3 A loop with priming and pseudorandom numbers

We often want our programs to exhibit apparently random behavior. This often comes up in game playing programs where we need our program to make up a number for the user to guess or to shuffle a deck of cards or to pick a word from a list of words for the user to guess. Programs are, by their very nature, predictable and non-random. But we can produce values that seem to be random. Such values are called pseudorandom because they are produced algorithmically.

Pseudorandom numbers

Numbers that, although they are derived from a predictable and well-defined algorithm, mimic the properties of numbers chosen at random.

Java provides several mechanisms for obtaining pseudorandom numbers. You can call the method Math.random() from the Math class to obtain a random floating point value that has the property that:

0.0 <= Math.random() < 1.0 This is a quick and easy way to get a random number and you can use multiplication to change the range of the numbers produced. But Java provides a class called Random that can be easier to use. It is included in the java.util package, so you'd have to include an import declaration at the beginning of your program to use it. Once you construct a Random object, you can call its method nextInt passing it a maximum integer. The number returned will be between 0 (inclusive) and the maximum (exclusive). For example, if you call nextInt(100), you will get a number between 0 and 99. Obviously you can add 1 to the number to have a range between 1 and 100.

Let's look at a simple program that picks numbers between 1 and 10 until a particular number comes up. Let's use the Random class to construct an object for generating our pseudorandom numbers:

Random r = new Random(); Our loop should look something like this (where number is the value the user has asked us to generate):

int result; while (result != number) { result = r.nextInt(10) + 1; System.out.println("next number = " + result); } Notice that we have to declare the variable result outside the while loop because it appears in the while loop test. The code above has the right approach, but Java won't accept it. It generates an error message that the variable result might not be initialized. This is an example of a loop that would need priming.

Priming a loop

Initializing variables before a loop to "prime the pump" and guarantee that we enter the loop.

We want to set the variable result to something that will cause us to enter the loop, but in some sense we don't care what value we set it to as long as it gets us into the loop. We would want to be careful not to set it to the value the user wants us to generate. We are dealing with values between 1 and 10, so we could set result to a value like -1 that is clearly outside the range of numbers we are working with. We sometimes refer to this as a "dummy" value because we don't actually process it. Later in this chapter we will see a variation of the while loop that wouldn't require this kind of priming.

Below is the complete program solution.

        import java.util.*;
        
        public class Pick {
            public static void main(String[] args) {
                System.out.println("This program picks random numbers from 1 to 10");
                System.out.println("until a particular number comes up.");
                System.out.println();
        
                Scanner console = new Scanner(System.in);
                Random r = new Random();
        
                System.out.print("Pick a number between 1 and 10--> ");
                int number = console.nextInt();
        
                int result = -1;
                int count = 0;
                while (result != number) {
                    result = r.nextInt(10) + 1;
                    System.out.println("next number = " + result);
                    count++;
                }
                System.out.println("Your number came up after " + count + " times");
            }
        }
Depending upon the sequence of numbers returned by the Random object, it might end up picking the given number quickly, as in this sample execution:

        This program picks random numbers from 1 to 10
        until a particular number comes up.
        
        Pick a number between 1 and 10--> 2
        next number = 7
        next number = 8
        next number = 2
        Your number came up after 3 times
or it might take a while to pick the number, as in this sample execution:

        This program picks random numbers from 1 to 10
        until a particular number comes up.
        
        Pick a number between 1 and 10--> 10
        next number = 9
        next number = 7
        next number = 7
        next number = 5
        next number = 8
        next number = 8
        next number = 1
        next number = 5
        next number = 1
        next number = 9
        next number = 7
        next number = 10
        Your number came up after 12 times

5.3 Type boolean

George Boole was such a good logician that a type has been named for him. You use the Java type boolean to describe logical true/false relationships. Recall that boolean is one of the primitive types like int, double and char.

Without realizing it, you have already used booleans. if/else statements and while loops are controlled by expressions that specify tests. The following expression:

number % 2 == 0 is a test for divisibility by 2. It is also a boolean expression. Boolean expressions are meant to capture the concepts of truth and falsity, so it is not surprising that the domain of type boolean has only two values--true and false. The words true and false are keywords in Java. They are the literal values of type boolean. All boolean expressions, when evaluated, will return one or the other of these literals.

To understand this better, remember what these terms mean for type int. The literals of type int include 0, 1, 2, and so on. Because these are literals of type int, you can do things like the following:

int number1 = 1; int number2 = 0; Consider what you can do with variables of type boolean. Suppose you define variables called test1 and test2 of type boolean. These variables can only take on two possible values--true and false. You can say:

boolean test1 = true; boolean test2 = false; You can also write a statement that copies the value of one boolean variable to another, as with variables of any other type.

test1 = test2; You also know that the assignment statement can use expressions:

number1 = 2 + 2; and that the simple tests you have been using are boolean expressions. That means you can say things like the following.

test1 = (2 + 2 == 4); test2 = (3 * 100 < 250); These assignment statements say, "set this boolean variable according to the truth value returned by the following test." The first statement sets the variable test1 to true, because the test evaluates to true. The second sets the variable test2 to false, because the second test evaluates to false. The parentheses are not needed, but make the statements more readable.

Many beginners don't understand these assignment statements and write code like the following.

        if (x < y) {
            less = true;
        } else {
            less = false;
        }
This is a redundant statement. First, it evaluates the truth value of the test (x < y). If the test evaluates to true, it executes the if part and assigns less the value true. If the truth evaluates to false, is executes the else part and assigns less the value false. Since you are assigning less the truth value of the if/else test, you should do it directly.

less = (x < y); Obviously, then, the assignment statement is one of the operations you can perform on variables of type boolean.

You form complicated boolean expressions using what are known as the logical operators.

Logical Operators
Operator Meaning Example Value
&& and (conjunction) (2 == 2) && (3 < 4) true
|| or (disjunction) (1 < 2) || (2 == 3) true
! not (negation) !(2 == 2) false

The not operator ("!") reverses the truth value of its operand. If an expression evaluates to true, its negation evaluates to false and vice versa. You can express this using a truth table. The following truth table has two columns, one for a variable and one for its negation. The table shows for each value of the variable, the corresponding value of the negation.

Truth Table for Not ("!")
p !p
truefalse
falsetrue

In addition to the negation operator, there are two logical connectives you will use, and ("&&") and or ("||"). You use these connectives to tie two boolean expressions together, creating a new boolean expression. The truth table below shows that the and operator evaluates to true only when both of its individual operands are true:

Truth Table for And ("&&")
p q p && q
truetruetrue
truefalsefalse
falsetruefalse
falsefalsefalse

The truth table below shows that the or operator evaluates to true except when both operands are false.

Truth Table for Or ("||")
p q p || q
truetruetrue
truefalsetrue
falsetruetrue
falsefalsefalse

The Java or operator has a slightly different meaning than the English "or." In English you say, "I'll study tonight or I'll go to a movie." One or the other will be true, but not both. The or operator behaves differently. If both operands are true, the overall proposition is true.

You generally use the logical operators when what you have to say does not reduce to one test. For example, suppose you want to do something if a number is between 1 and 10. You might say:

        if (number >= 1) {
            if (number <= 10) {
                doSomething();
            }
        }
You can say this more easily using and:

        if (number >= 1 && number <= 10) {
            doSomething();
        }
People use the words "and" and "or" all the time. Java only allows you to use them in the strict logical sense, however. So be careful not to write code like the following:

        if (x == 1 || 2 || 3) {
            doSomething();
        }
You can only use the and and or operators to combine boolean expressions together. Otherwise, the computer will not understand what you mean. To express this using the boolean or, you have to string three different boolean expressions together.

        if (x == 1 || x == 2 || x == 3) {
            doSomething();
        }
Now that you have seen the operators and, or, and not, you must again consider the precedence of operators. Section 2.3.3 has a table showing Java's precedence rules. The not operator appears at the top with the highest level of precedence. The other two logical operators have a fairly low precedence, lower than the arithmetic and relational operators but higher than the assignment operators. Between the two, the and operator has a higher level of precedence than the or operator. These levels of precedence answer questions that arise when evaluating expressions like the following.

        if (!first && second || third) {
            doSomething();
        }
Here, the computer evaluates the not first, the and second, and then the or.

Below is the precedence table once more including these new operators.

Java Operator Precedence
Description Operators
unary operators !, ++, --, +, -
multiplicative operators *, /, %
additive operators +, -
relational operators <, >, <=, >=
equality operators ==, !=
logical and &&
logical or ||
assignment operators =, +=, -=, *=, /=, %=, &&=, ||=

5.3.1 Flags and Other boolean Variables

if/else statements are controlled by a boolean test. The test can be a boolean variable as well as a boolean expression. Consider, for example, the following code.

        if (number > 0) {
            System.out.println("positive");
        } else {
            System.out.println("negative");
        }
It could be rewritten as follows.

        boolean positive = (number > 0);
        if (positive) {
            System.out.println("positive");
        } else {
            System.out.println("negative");
        }
Boolean variables add to the readability of programs because they allow you to give names to tests. Consider the kind of code you would generate for an address book program. You might have some integer variables that describe certain attributes of a person: looks, to store a rough estimate of physical beauty (on a scale of 1-10); IQ, to store intelligence quotient; income, to store gross annual income; and snothers, to track intimate friends (snother is short for "significant other"). Given these variables to specify the attributes of a person, you can develop various tests of suitability. Boolean variables are useful here to give names to those tests and add greatly to the readability of the code.

boolean cute = (looks >= 9); boolean smart = (IQ > 125); boolean rich = (income > 100000); boolean available = (snothers == 0); boolean awesome = cute && smart && rich && available; You might find occasion to use a special kind of boolean variable called a flag. Typical use is within loops to record error conditions or to signal completion. Different flags test different conditions. The analogy is to a referee at a game that watches for a particular illegal action and throws a flag if it happens. For example, in a football game there is a special line called the line of scrimmage that player's aren't supposed cross until the ball is in play. If one of them crosses that line too early, a referee blows a whistle and throws a flag on the ground to signal what is called "off sides." Let's look at a pseudocode description of what that referee might do.

        for (each step taken by a team member) {
            if (this step puts the player over the scrimmage line) {
                set offsides to true.
            } else {
                set offsides to false.
            }
        }
        if (offsides) {
            impose penalty.
        }
This does not perform the way you want it to because offsides can be reset to false after having been set to true. This is not what we want. A team is not forgiven by stepping back to their side of the scrimmage line. Once the whistle blows and the flag is thrown, it should stay thrown. So we need pseudocode more like this:

        for (each step taken by a team member) {
            if (this step puts the player over the scrimmage line) {
                set offsides to true.
            }
        }
        if (offsides) {
            impose penalty.
        }
There is one problem with this pseudocode. What happens if nobody goes offsides? How does the flag get a value? You need to initialize it. The simple fix is to start the flag out as false outside the loop, assuming innocence until guilt is proven. Here is the pseudocode. It shows you the basic way to use flags in programs.

        set offsides to false.
        for (each step taken by a team member) {
            if (this step puts the player over the scrimmage line) {
                set offsides to true.
            }
        }
        if (offsides) {
            impose penalty.
        }
Let's introduce a flag into the cumulative sum code we saw in the previous chapter.

double sum = 0; for (int i = 1; i <= totalNumber; i++) { System.out.print(" next--> "); double next = console.nextDouble(); sum += next; } System.out.println("Sum = " + sum); Suppose we want to know if the sum ever goes negative. Notice that this isn't the same question of whether the sum ends up being negative. It might switch back and forth between positive and negative. This is a lot like what happens with a bank account. You might make a series of deposits and withdrawals, but the bank wants to know if you overdrew your account anywhere along the way. Using the pseudocode above, we can modify this loop to keep track of whether the sum ever goes negative and we can report it after the loop.

        double sum = 0;
        boolean negative = false;
        for (int i = 1; i <= totalNumber; i++) {
            System.out.print("    next--> ");
            double next = console.nextDouble();
            sum += next;
            if (sum < 0) {
                negative = true;
            }
        }
        System.out.println("Sum = " + sum);
        if (negative) {
            System.out.println("Sum went negative");
        } else {
            System.out.println("Sum never went negative");
        }

5.3.2 Boolean Zen

In 1974 Robert Pirsig started a cultural trend with his book Zen and the Art of Motorcycle Maintenance: An Inquiry into Values. A slew of later books copied the title so that we got "Zen and the Art of X" where X was Poker, Knitting, Writing, Foosball, Guitar, Public School Teaching, Making a Living, Falling in Love, Quilting, Stand-Up Comedy, the SAT, Flower Arrangement, Fly Tying, Systems Analysis, Fatherhood, Screenwriting, Diabetes Maintenance, Intimacy, Helping, Street Fighting, Murder, and on and on. There was even a book on Zen and the Art of Anything.

We join this cultural trend by discussing Zen and the Art of Type Boolean. It seems to take a while for many novices to get used to boolean expressions. Novices often write overly complex expressions involving boolean values because they don't seem to understand the simplicity that is possible when you "get" how boolean works.

For example, suppose that you want to write a static method that determines whether or not a number is even. We might call the method isEven. It would take a value of type int and would return true if the int is even and false if it is not. So the method would look like this:

        public static boolean isEven(int n) {
	    ...
        }
So how do we write the body of this method? We can use the mod operator to see if the remainder when we divide by 2 is 0. If we find that (n % 2) is 0, then we know the number is even. If not, then we know the number is not even (that it is odd). The method has a boolean return type, so we want to return the value true when (n % 2) is 0 and we want to return the value false when it is not. So we can write the method as follows:

        public static boolean isEven(int n) {
	    if (n % 2 == 0) {
	        return true;
	    } else {
	        return false;
            }
        }
This works, but it is more verbose than it needs to be. There is a much simpler way to write this. Think about what the code above is doing. It evaluates the expression (n % 2 == 0). That expression is of type boolean, which means that it evaluates to either true or false. The if/else says that if the expression evaluates to true, then return true, and if it evaluates to false, then return false. But why have the if/else? If we are going to return true when the expression evaluates to true and return false when it evaluates to false, then we can just return the value of the expression directly:

        public static boolean isEven(int n) {
	    return (n % 2 == 0);
        }
Even this version can be simplified because the parentheses are not necessary, although they make it clearer exactly what is being returned. We are testing whether n % 2 equals 0 and are returning the result (true when it does equal 0, false when it does not).

Consider an analogy to integer expressions. To someone who understands boolean zen, the if/else version of this method looks as odd as the following code:

        if (x == 1) {
            return 1;
        } else if (x == 2) {
            return 2;
        } else if (x == 3) {
            return 3;
        } else if (x == 4) {
            return 4;
        } else if (x == 5) {
            return 5;
        }
If you always want to return the value of x, then why not just say:

        return x;
A similar confusion can occur when students use boolean variables. In the last section we looked at a variation of the cumulative sum code that used a boolean variable called "negative" to keep track of whether or not the sum ever goes negative. We then used an if/else to print a message reporting the result:

        if (negative) {
            System.out.println("Sum went negative");
        } else {
            System.out.println("Sum never went negative");
        }
Some novices would write this code as follows:

        if (negative == true) {
            System.out.println("Sum went negative");
        } else {
            System.out.println("Sum never went negative");
        }
The comparison is unnecessary because the if/else is expecting an expression of type boolean to appear inside the parentheses. A boolean variable is already of the appropriate type, so you don't need to test whether it equals true, it either is true or it isn't (in which case it is false). To someone who understand boolean zen, the test above seems as redundant as saying:

        if ((negative == true) == true) {
            ...
Novices also often write tests like the following:

        if (negative == false) {
            ...
This makes more sense because the test is doing something useful in that it switches the meaning of the boolean variable (evaluating to true if the variable is false and evaluating to false if the variable is true). But the negation operator is designed to do this kind of switching of boolean values, so this test is better written as:

        if (!negative) {
            ...
You should get used to reading the exclamation mark as "not", so this test would be read as "if not negative." To those who understand boolean zen, that is a more concise way to express this than to think about testing whether negative is equal to false.

5.3.3 Checking for User Errors

The Scanner class has methods that allow you to perform a test before you read something. In other words, it allows you to look before you leap. For each of the "next" methods of the Scanner class, there is a corresponding "has" method that tells you whether or not you can perform the given operation.

For example, we will often want to read an int using a Scanner object. But what if the user types something other than an int? Scanner lets you test this. You would call the method nextInt() to read the int. Scanner has a corresponding method hasNextInt() that tells you whether or not reading an int is possible right now. To answer the question, the Scanner object looks at the next token and sees if it can be interpreted as an integer.

This can be confusing at first because we tend to think of certain sequences of characters as being one and only one kind of thing. But when we read tokens, they can be interpreted in different ways. The following program will allow us to explore this.

        import java.util.*;
        
        public class ExamineInput1 {
            public static void main(String[] args) {
                System.out.println("This program examines a token and tells you");
                System.out.println("the ways in which it could be read.");
                System.out.println();
        
                Scanner console = new Scanner(System.in);
        
                System.out.print("token? ");
                System.out.println("  hasNextInt = " + console.hasNextInt());
                System.out.println("  hasNextDouble = " + console.hasNextDouble());
                System.out.println("  hasNext = " + console.hasNext());
            }
        }

Here is what happens when we enter the token "348":

        This program examines a token and tells you
        the ways in which it could be read.

        token? 348
          hasNextInt = true
          hasNextDouble = true
          hasNext = true
As you'd expect, the call on hasNextInt() returns true, which means that we could interpret this token as an integer. But the scanner would also allow us to interpret this token as a double, which is why hasNextDouble() also returns true. But notice that hasNext() also returns true. That means that we could call the next() method to read this in as a String.

Here's another execution, this time for the token "348.2":

        This program examines a token and tells you
        the ways in which it could be read.

        token? 348.2
          hasNextInt = false
          hasNextDouble = true
          hasNext = true
Finally, consider this execution for the token "hello":

        This program examines a token and tells you
        the ways in which it could be read.

        token? hello
          hasNextInt = false
          hasNextDouble = false
          hasNext = true
It can't be interpreted as an int or double. It can only be interpreted as a String.

In general, you want to write programs that don't make assumptions about user input. We saw, for example, that the Scanner object can throw an exception if the user types the wrong kind of data. It's better to write programs that can deal with user errors. Such programs are referred to as being robust.

Robust

A program is said to be robust if it is able to execute even when presented with illegal data.

Consider the following code fragment:

Scanner console = new Scanner(System.in); System.out.print("How old are you in years? "); int age = console.nextInt(); What if the user types something that is not an integer? If that happens, the Scanner will throw an exception on the call to nextInt(). We saw in the last chapter that we can test whether or not the next token can be interpreted as an integer with the hasNextInt() method. So we can test before reading an int whether the user has typed an appropriate value.

What if the user types something other than an integer? Then we'd want to discard the input, print out some kind of error message and prompt for a second input. We'd want this in a loop so that we'd keep discarding input and generating error messages until the user gives us legal input.

Here is a first attempt at a solution in pseudocode:

        while (user hasn't given us an integer) {
            prompt.
            discard input.
            generate an error message.
        }
        read the integer.
This reflects what we want to do in general. We want to keep prompting, discarding and generating an error message as long as the input is illegal and then we want to read the integer when the input finally becomes legal. But we don't want to discard the input or generate an error message in that final case where the user gives us legal input. In other words, the last time through the loop we want to do just the first of these 3 steps (prompting, but not discarding and not generating an error message). This is another classic fencepost problem and we can solve it in the usual way by putting the initial prompt before the loop and changing the order of the operations within the loop.


        prompt.
        while (user hasn't given us an integer) {
            discard input.
            generate an error message.
            prompt.
        }
        read the integer.
This is fairly easy to turn into Java code:

Scanner console = new Scanner(System.in); System.out.print("How old are you in years? "); while (!console.hasNextInt()) { console.next(); // to discard the input System.out.println("That is not an integer. Please try again."); System.out.print("How old are you in years? "); } int age = console.nextInt(); In fact, this is such a common operation that it is worth turning this into a static method: public static int getInt(Scanner console, String prompt) { System.out.print(prompt); while (!console.hasNextInt()) { console.next(); System.out.println("That is not an integer. Please try again."); System.out.print(prompt); } return console.nextInt(); } Using this method, we can rewrite our original code to the following.

Scanner console = new Scanner(System.in); int age = getInt(console, "How old are you in years? "); When you execute this code, the interaction looks like this:

        How old are you in years? what?
        That is not an integer.  Please try again.
        How old are you in years? 18.4
        That is not an integer.  Please try again.
        How old are you in years? ten
        That is not an integer.  Please try again.
        How old are you in years? darn!
        That is not an integer.  Please try again.
        How old are you in years? help
        That is not an integer.  Please try again.
        How old are you in years? 19

5.4 The do/while Loop

As we have seen, the while loop tests at the "top" of the loop before it executes its controlled statement. Java has an alternative known as the do/while loop that tests at the "bottom" of the loop. It has the following syntax:

do { <statement>; ... <statement>; } while (<test>); For example int number = 1; do { number *= 2; } while (number <= 200); This loop produces the same result as the corresponding while loop, doubling the variable number until it reaches 256, which is the first power of 2 greater than 200. But unlike the while loop, the do/while loop always executes its controlled statements at least once.

Recall that the while loop operates like this, testing before executing the controlled statement:

        loop
            evaluate test.
            if test evaluates to false, then exit.
            execute controlled statement.
        end of loop
The do/while loop executes like this, always executing the controlled statement at least once:

        loop
            execute controlled statement.
            evaluate test.
            if test evaluates to false, then exit.
        end of loop
There are many programming problems where the do/while is the more appropriate statement. This occurs often with interactive programs where you know you want to do something at least once. For example, you might have a loop that allows a user to play a game multiple times but you can be fairly sure that the user wants to play at least once. Or if you are playing a guessing game with the user, you will always have to obtain at least one guess.

We saw a situation like this earlier with the program that picked pseudorandom numbers between 1 and 10 until a certain number is picked. You know you have to pick at least once, so a do/while is appropriate. The while loop version required priming, which involved initializing a variable before the loop.

With the do/while version, we don't need to initialize because we know the loop will execute at least once:

        import java.util.*;
        
        public class Pick2 {
            public static void main(String[] args) {
                System.out.println("This program picks random numbers from 1 to 10");
                System.out.println("until a particular number comes up.");
                System.out.println();
        
                Scanner console = new Scanner(System.in);
                Random r = new Random();
        
                System.out.print("Pick a number between 1 and 10--> ");
                int number = console.nextInt();
        
                int result;
                int count = 0;
                do {
                    result = r.nextInt(10) + 1;
                    System.out.println("next number = " + result);
                    count++;
                } while (result != number);
                System.out.println("Your number came up after " + count + " times");
            }
        }
Notice that, as always, we use curly braces to turn multiple statements into a block. You might be tempted to move the declaration for the variable result inside the do/while loop, but that won't work because it appears in the loop test which is outside the curly braces.

5.4.1 Break and "forever" loops

The while loop has its test at the top of the loop and the do/while has its test at the bottom of the loop. That should lead you to wonder whether it might be desirable to have the test in the middle of the loop. There is a way to accomplish this in Java, although it requires some odd looking loops.

Java has a special statement called "break" that will exit a loop. You can use it to break out of any of the loops we have seen (while, do/while, for). Loops with break statements can be difficult to understand, so the practice is generally discouraged. But there is an interesting application of break to form a loop where the test occurs in the middle. The problem is that we still need to choose one of the standard loop constructs (while, do/while, for). One common choice is to form what appears to be an infinite loop with a while loop:

while (true) { <statement>; ... <statement>; }

Because the boolean literal "true" always evaluates to true, this while loop appears to execute indefinitely. But you can include a test in the middle of the loop with a break statement. This technique is useful for solving the fencepost problem.

Recall that we wrote the following code to solve a fencepost problem:

Scanner console = new Scanner(System.in); int sum = 0; System.out.print("next integer (-1 to quit)? "); int number = console.nextInt(); while (number != -1) { sum += number; System.out.print("next integer (-1 to quit)? "); number = console.nextInt(); } System.out.println("sum = " + sum); The code to prompt and read appears twice, once before the loop executes and once at the bottom of the loop. Using a while loop with a break, we can eliminate this redundancy:

Scanner console = new Scanner(System.in); int sum = 0; while (true) { System.out.print("next integer (-1 to quit)? "); int number = console.nextInt(); if (number == -1) { break; } sum += number; } System.out.println("sum = " + sum); Keep in mind that the while loop test is not the real test for this loop. The real test appears in the middle when we see if number is equal to -1, in which case we break. By having the test in the middle, we have a simpler solution to the "loop and a half" problem. We exit the loop on the final iteration after doing the "half" that we want.

Another form of this loop is to use a for loop that has none of the usual initialization, test and update code. The for loop still needs the parentheses and semicolons, but the rest can be empty. This leads to the following rather odd looking code:

Scanner console = new Scanner(System.in); int sum = 0; for (;;) { System.out.print("next integer (-1 to quit)? "); int number = console.nextInt(); if (number == -1) { break; } sum += number; } System.out.println("sum = " + sum); The "for (;;)" has the same effect as the "while (true)" loop. In other words, it would normally be an infinite loop. It's okay in this case because the true loop test appears in the middle of the loop with a break.

People tend to either love or hate this version of the for loop and for the same reason. The people who hate it say, "This looks too strange." The people who love it say, "I'm glad it looks strange because it makes it clear that the real loop test is somewhere else." Some people read the "for (;;)" as "forever" and refer to these as "forever loops."

Many advocates of structured programming argue that neither of these loops is a reasonable alternative. They argue that the break statement is like the "goto" construct that many computer scientists have argued leads to unstructured "spaghetti" code. Obviously different programmers will make up their own minds about whether they like this construct or not.

5.5 Assertions and Program Logic

Logicians concern themselves with assertions.

Assertion

A declarative sentence that is either true or false.

The following are all assertions.

        2 + 2 equals 4
        The sun is larger than the earth.
        x > 45
        It was raining.
        The rain in Spain falls mainly on the plain.
The following are not assertions. The first is a question and the second is a command.

        How much do you weigh?
        Take me home.
Some assertions are true or false depending upon context.

        x > 45                  This depends on x.
        It was raining.         This depends on when and where.
You can pin down whether they are true or false by providing a context.

        when x = 13, x > 45
        On July 4, 1776 in Philadelphia, it was raining.
To write programs correctly and efficiently, you must learn to make assertions about your programs and to understand the contexts in which those assertions will be true. For example, if you are trying to obtain a nonnegative number from the user, you want the assertion "Number is nonnegative" to be true. What happens if you use a simple prompt and read?

        System.out.print("Please give me a nonnegative number--> ");
        double number = console.nextDouble();
        // Is number nonnegative here?
The user can ignore your request and input a negative number anyway. In fact, users often input values that you don't expect, most often because they are confused. Given the uncertainty of user input, this particular assertion is sometimes true and sometimes false. It might be important to be certain that this assertion is true for something that appears later in the program. For example, if you are going to take the square root of that number, you must be sure it is nonnegative. Otherwise, you might end up with a bad result.

Using a loop we can guarantee that the number we get is nonnegative.

        System.out.print("Please give me a nonnegative number--> ");
        double number = console.nextDouble();
        while (number < 0.0) {
            System.out.print("That was a negative number.  Try again--> ");
            number = console.nextDouble();
        }
        // Is number nonnegative here?
You know that number will be nonnegative after the loop; otherwise, you would not exit the while loop. As long as a user gives negative values, your program stays in the while loop and continues to prompt for input.

This doesn't mean that the number should be nonnegative after the loop. It means the number will be nonnegative. By working through the logic of the program, you can see that this is a certainty. It is an assertion of which you are sure. You could even prove it if need be. Such an assertion is called a provable assertion.

Provable assertion

An assertion that can be proven to be true at a particular point in program execution.

Provable assertions help to identify unnecessary bits of code. Consider these statements:

        int x = 0;
        if (x == 0) {
            System.out.println("This is what I expect.");
        } else {
            System.out.println("how can that be?");
        }
The if/else is not necessary. You know what the assignment statement does, so you know that it will set x to zero. Testing whether or not it's zero is like saying, "Before I proceed, I'm going to check that 2 + 2 equals 4." Because the if part of this if/else is always executed, you can prove that these lines of code always do the same thing.

int x = 0; System.out.println("This is what I expect."); This code is simpler and, therefore, better. Programs are complex enough without adding unnecessary code.


5.5.1 Reasoning About Assertions

The focus on assertions comes out of a field of computer science known as program verification.

Program Verification

A field of computer science that involves reasoning about the formal properties of programs to prove the correctness of a program.

For example, consider the properties of the simple if statement:

if (<test>) { // test is always true here ... } You enter the body of the if statement only if the test is true, which is why you know that it must be true at that particular point in program execution. You can draw a similar conclusion about what is true in an if/else statement:

if (<test>) { // test is always true here ... } else { // test is never true here ... } We can also draw a conclusion inside the body of a while loop:

while (<test>) { // test is always true here ... } But in the case of the while loop, we can draw an even stronger conclusion. We know that as long as the test evaluates to true, we keep going back into the loop. That means we can conclude that after the loop is done executing, the test can no longer be true:

while (<test>) { // test is always true here ... } // test is never true here The test can't be true after the loop because if it had been true, we would have executed the body of the loop again.

These observations about the properties of if statements, if/else statements and while loops provide a good start for proving certain assertions about programs. Often proving assertions requires a deeper analysis of what the code actually does. For example, suppose that you have a variable called x of type int and you execute the following if/else:

        if (x < 0) {
            // x < 0 is always true here
            x = -x;
        }
        // but what about x < 0 here?
We wouldn't normally be able to conclude anything about x being less than 0 after the if statement, but we can if we think about the different cases. If x had been greater than or equal to 0 before the if statement, then it will still be greater than or equal to 0 after the if statement. And if x is less than 0 before the if statement, then it will be equal to -x afterwards. When x is less than 0, -x is greater than 0. Thus, in either case, we know that after the if statement executes, x will be greater than or equal to 0.

Programmers naturally apply this kind of reasoning when writing their programs. Program verification researchers are trying to figure out how to do this kind of reasoning in a formal, verifiable way.

5.5.2 The Java assert statement

The concept of assertions has become so popular among software practitioners that many programming languages provide support for testing assertions. Java added support for testing assertions starting with version 1.4 of the language. The syntax for the assert statement is:

assert <boolean test>; As in:

        assert (x < 0);
Parentheses have been included here to make the boolean expression very clear, but the parentheses are not required.

Programmers often build assertions into their programs to have the computer check to make sure that all of the programmer's assumptions are correct. In general, we expect these assertions to succeed. When the assertion fails, that signals a problem. It means that the programmer has a logic error that is preventing the assumptions from holding true. If the boolean test in the assert statement evaluates to false, we say that the assertion fails. When an assertion fails, an exception is thrown that stops the program from executing.

Testing of assertions can be expensive, so Java gives you the ability to control whether assertions are enabled or disabled. That way you can enable assertion checking while you are developing and testing a program to make sure it works properly but you can disable assertion checking when you're fairly confident that the program works and you want to speed up the program. By default, assertion checking is disabled. You can enable assertion checking by giving the "-ea" option when you run your Java program.

5.6 Preconditions and Postconditions

You can formally describe the workings of a method using assertions. For example, you can give a series of assertions that describe what will be true when a method is done executing. Such assertions are called postconditions of a method. For example, to describe the job of a person on an auto assembly line, you might use a postcondition like, "The bolts that secure the left front tire are on the car and tight."

Postconditions are not the whole story. Employees on an assembly line depend on each other. A line worker can't add bolts and tighten them if the left tire isn't there or if there are no bolts. You specify conditions like these by using preconditions, assertions that must be true before a task is performed. The assembly line worker might have preconditions like, "the left tire is mounted properly on the car; there are at least 8 bolts in the supply box; and a working wrench is available." You describe the task fully, then, by saying that the worker can make the postconditions true if the preconditions are true before starting.

Precondition

An assertion that must be true before a method executes to guarantee that it can perform its task.

Postcondition

An assertion that the method guarantees will be true after it finishes executing as long as the preconditions were true before it was called.

Methods, like workers on an assembly line, need to work together, each solving its portion of the task, in order to solve the overall task. The preconditions and postconditions describe the dependencies between methods. To understand this better, consider this nonprogramming example with the following methods and their conditions.

        makeBatter
                pre : bowl is clean.
                post: bowl has batter in it; bowl is dirty.
        bakeCake
                pre : bowl has batter in it; pan is clean.
                post: cake is baked; pan is dirty.
        washDishes
                pre : none.
                post: bowl and pan are clean.
When you call these methods, you must fit them together so the preconditions are always satisfied before a method executes. For example, if you suppose that the bowl and pan are initially clean, you can make a cake as follows.

        makeBatter.
        bakeCake.
Here is a trace of its execution.

        --> bowl and pan are clean.
        makeBatter.
        --> bowl has batter in it; bowl is dirty; pan is clean.
        bakeCake.
        --> cake is baked; bowl and pan are dirty.
The preconditions are satisfied before each method is executed. However, if you want a loop to make cakes, you can't do it this way.

        for (many cakes) {
            makeBatter.
            bakeCake.
        }
The first cake is made properly, but not the second. The error occurs because the preconditions for makeBatter are not satisfied on the second execution of the loop.

You need a clean bowl and pan to execute makeBatter.

        --> bowl and pan are clean.
        makeBatter.
        --> bowl has batter in it; bowl is dirty; pan is clean.
        bakeCake.
        --> cake is baked; bowl and pan are dirty.
        makeBatter.
You need to change your solution.

        for (many cakes) {
            makeBatter.
            bakeCake.
            washDishes.
        }
The execution of washDishes leaves you with a clean bowl and pan and guarantees that you satisfy the preconditions of makeBatter on the next iteration.

5.7 Chapter Summary

 

5.8 Self-Check Exercises

    For each of the following while loops, determine:
  1.         int x = 1;
            while (x < 100) {
                System.out.print(x + " ");
                x += 10;
            }
    
  2.         int x = 2;
            while (x < 200) {
                System.out.print(x + " ");
                x *= x;
            }
    
  3.         int max = 10;
            while (max < 10) {
                System.out.println("count down: " + max);
                max--;
            }
    
  4.         String word = "a";
            while (word.length() < 10) {
                word = "b" + word + "b";
            }
            System.out.println(word);
    
  5.         int x = 250;
            while (x % 3 != 0) {
                System.out.println(x);
            }
    

  6.         int x = 100;
            while (x > 0) {
                System.out.println(x / 10);
                x = x / 2;
            }
    
  7. Consider the following method:
            public static void mystery(int x) {
                int y = 1;
                int z = 0;
                while (2 * y <= x) {
                    y *= 2;
                    z++;
                }
                System.out.println(y + " " + z);
            }
    
    For each call below, indicate what output is produced.
            mystery(1);
            mystery(6);
            mystery(19);
            mystery(39);
            mystery(74);
    
  8. Consider the following method:
            public static int mystery(int x, int y) {
                while (x != 0 && y != 0) {
                    if (x < y) {
                        y -= x;
                    } else {
                        x -= y;
                    }
                }
                return x + y;
            }
    
    For each call below, indicate what value is returned.
            mystery(3, 3)
            mystery(5, 3)
            mystery(2, 6)
            mystery(12, 18)
            mystery(30, 75)
    
  9. Convert each of the following for loops into an equivalent while loop.
  10. The following code is an incorrect loop to read numbers until the user types a valid ZIP code (a number between 1 and 99999 inclusive). What is wrong with the code? Explain the mistake and describe a way to fix it.
            Scanner console = new Scanner(System.in);
            System.out.print("Enter a ZIP code: ");
            int number = 0;
            while (number < 1 || number > 99999) {
                System.out.print("Invalid ZIP code.  Try again: ");
                number = console.nextInt();
            }
    
  11. Write a static method named printFactors that accepts an integer as its parameter and uses a fencepost loop to print the factors of that number, separated by the word " and ". For example, the number 24's factors should print as:
    1 and 2 and 3 and 4 and 6 and 12 and 24

    You may assume that the number parameter's value is greater than 0, or for extra challenge, you may throw an exception if it is 0 or negative.

  12. Write a static method named printLetters that accepts a String as its parameter and uses a fencepost loop to print the letters of the String, separated by commas. For example, if the actual parameter's value is "Rabbit", your code should print the following:
            R, a, b, b, i, t

    Your method should print nothing if the empty string "" is passed.

  13. Write a sentinel loop that repeatedly prompts the user for numbers. Once the number -1 is typed, the maximum and minimum of all numbers typed are displayed. For example:
            Type a number (or -1 to stop): 5
            Type a number (or -1 to stop): 2
            Type a number (or -1 to stop): 17
            Type a number (or -1 to stop): 8
            Type a number (or -1 to stop): -1
            Maximum was 17
            Minimum was 2
    
    If -1 is the first number typed, you should not print any maximum or minimum. For example:
            Type a number (or -1 to stop): -1
    
  14. Write a sentinel loop that repeatedly prompts the user for names. Once an empty line is typed, all names typed are displayed. For example:
            Type a person's name (or an empty line to stop): Marty
            Type a person's name (or an empty line to stop): Andrea
            Type a person's name (or an empty line to stop): Stuart
            Type a person's name (or an empty line to stop):
            Welcome to all: Marty Andrea Stuart
    
  15. Write a sentinel loop that repeatedly prompts the user for numbers. Once any number less than zero is typed, the average of all numbers typed is displayed. Display the average as a double. For example:
            Type a number: 7
            Type a number: 4
            Type a number: 16
            Type a number: -4
            Average was 9.0
    
    If the first number typed is negative, do not print an average. For example:
            Type a number: -2
    
  16. The following code is not robust against invalid user input. Describe how to change the code so that it will not proceed until the user has entered a valid age and GPA. (Assume that any int is a legal age and that any double is a legal GPA.)
            Scanner console = new Scanner(System.in);
            System.out.print("Type your age: ");
            int age = console.nextInt();
    
            System.out.print("Type your GPA: ");
            double gpa = console.nextDouble();
    
    For added challenge, modify the code so that it rejects invalid ages (for example, numbers less than 0) and GPAs (say, numbers less than 0.0 or greater than 4.0).
  17. What is the output of the following code:
            Scanner console = new Scanner(System.in);
            System.out.print("Type something for me! ");
    
            if (console.hasNextInt()) {
                int number = console.nextInt();
                System.out.println("Your IQ is " + number);
            } else if (console.hasNext()) {
                String token = console.next();
                System.out.println("Your name is " + token);
            }
    
  18. Write a piece of code that prompts the user for a number and then prints a different message depending on whether the number was an integer or a real number. Here are two example dialogues:
            Type a number: 42.5
            You typed the real number 42.5
    
            Type a number: 3
            You typed the integer 3
    
  19. Write code that prompts for three integers and then averages them and prints the average. Make your code robust against invalid input. (You may want to use the getInt method shown in this chapter.)
  20. What range of values can each variable a, b, c, d, and e have?
            Random rand = new Random();
            int a = rand.nextInt(100);
            int b = rand.nextInt(20) + 50;
            int c = rand.nextInt(20 + 50);
            int d = rand.nextInt(100) - 20;
            int e = rand.nextInt(10) * 4;
    
  21. Write code that generates a random integer between 0 and 10 inclusive.
  22. Write code that generates a random odd integer (not divisible by 2) between 50 and 99 inclusive.
  23. Write code that prints a random number of lines between 2 and 10 lines inclusive, where each line contains a random number of 'x' characters between 5 and 19 inclusive. For example:
            xxxxxxx
            xxxxxxxxxxxxxxxxxx
            xxxxxxxxxxxx
            xxxxxxxxxxxxxx
            xxxxxx
            xxxxxxxxxxx
            xxxxxxxxxxxxxxxx
    
  24. What is the value of each of the following boolean expressions?
            int x = 27;
            int y = -1;
            int z = 32;
            boolean b = false;
    
    For each of the following do/while loops, determine:
  25.         int x = 100;
            do {
                System.out.println(x);
                x = x / 2;
            } while (x % 2 == 0);
    
  26.         String str = "/\\";
            do {
                str += str;
            } while (str.length() < 10);
            System.out.println(str);
    
  27. Identify the various assertions below as being either always true, never true or sometimes true/sometimes false at various points in program execution. The comments in the method below indicate the points of interest.
            public static int mystery(Scanner console, int x) {
                int y = console.nextInt();
                int count = 0;
    
                // Point A
                while (y < x) {
                    // Point B
                    if (y == 0) {
                        count++;
                        // Point C
                    }
    
                    y = console.nextInt();
                    // Point D
                }
    
                // Point E
                return count;
            }
    
    Categorize each assertion at each point below with either ALWAYS, NEVER or SOMETIMES.
                       y < x    y == 0    count > 0
                     /------------------------------\
            Point A: |        |         |           |
                     |--------+---------+-----------|
            Point B: |        |         |           |
                     |--------+---------+-----------|
            Point C: |        |         |           |
                     |--------+---------+-----------|
            Point D: |        |         |           |
                     |--------+---------+-----------|
            Point E: |        |         |           |
                     \------------------------------/
    
  28. Identify the various assertions below as being either always true, never true or sometimes true/sometimes false at various points in program execution. The comments in the method below indicate the points of interest.
            public static int mystery(int n) {
                Random r = new Random();
                int a = r.nextInt(3) + 1;
                int b = 2;
    
                // Point A
                while (n > b) {
                    // Point B
                    b = b + a;
    
                    if (a > 1) {
                        n--;
    
                        // Point C
                        a = r.nextInt(b) + 1;
                    } else {
                        a = b + 1;
                        // Point D
                    }
                }
    
                // Point E
                return n;
            }
    
    Categorize each assertion at each point below with either ALWAYS, NEVER or SOMETIMES.
                       n > b     a > 1     b > a
                     /----------------------------\
            Point A: |        |         |         |
                     |--------+---------+---------|
            Point B: |        |         |         |
                     |--------+---------+---------|
            Point C: |        |         |         |
                     |--------+---------+---------|
            Point D: |        |         |         |
                     |--------+---------+---------|
            Point E: |        |         |         |
                     \----------------------------/
    

5.9 Programming Problems

  1. Write an interactive program that prompts for an integer and displays the same number in binary. How would you modify the program to make it display in base 3 rather than base 2?

  2. Write an interactive program that prompts for two integers and displays the greatest common divisor of the two numbers. The greatest common divisor (GCD) of two integers a and b is the largest integer that is a factor of both a and b.

    One efficient way to compute the GCD of two numbers is to use Euclid's algorithm, which states the following:

            GCD(A, B) = GCD(B, A % B)
            GCD(A, 0) = Absolute value of A
    
  3. Write an interactive program that reads lines of input from the user and converts each line into "Pig Latin" language. Pig Latin is simply English but with the initial consonant sound moved to the end of each word, followed by "ay". Words that begin with vowels simply have an "-ay" appended. For example, the phrase:

    The deepest shade of mushroom blue

    would have the following appearance in Pig Latin:

    e-Thay eepest-day ade-shay of-ay ushroom-may ue-blay

    Terminate the program when the user types a blank line.

  4. Write a program that plays a guessing game with the user. The program should generate a random number between 1 and some maximum number such as 100, then prompt the user repeatedly to guess the number. Once the user guesses correctly, print a message showing the number of guesses used.

    Consider extending this program by making it repeat the game until the user selects to stop, and then printing statistics about the player's total and average number of guesses.


Stuart Reges
Marty Stepp
Last modified: Mon Jan 2 23:12:34 PST 2006

Chapter 6
File Processing

Copyright © 2005 by Stuart Reges and Marty Stepp

6.1 Introduction

In chapter 4 we saw how to construct a Scanner object to read input from the Console. Now we will see how to construct Scanner objects to read input from files. The idea is fairly straightforward, but Java does not make it easy to read from input files. This is unfortunate because many interesting problems can be formulated as file processing tasks. Many introductory computer science classes have abandoned file processing altogether or the topic has moved into the second course because it is considered too advanced for novices.

There is nothing intrinsically complex about file processing. The languages C++ and C# provide mechanisms for easily reading and writing files. But Java was not designed for file processing and Sun has not been particularly eager to provide a simple solution. They did, however, introduce the Scanner class as a way to simplify some of the details associated with reading files. The result is that file reading is still awkward in Java, but at least the level of detail is manageable.

Before we can write a file processing program, we have to explore some issues related to Java exceptions. Remember that exceptions are errors that halt the execution of a program. In the case of file processing, we might try to open a file that doesn't exist, which would generate an exception.

6.2 Using a Scanner to Read an External File

An external file is a collection of characters and numbers that appear on one or more lines. They are external because they are not contained within the program and are not obtained from the user during execution. They are outside the scope of the program and its execution. You create external input files before the execution of a program. For example, you might create a file called "numbers.dat" with the following content.

        308.2 14.9 7.4
        2.8
        
        
        3.9 4.7 -15.4
        2.8
You can create such a file with an editor like NotePad on Windows or TextEdit on the Macintosh. Then you might write a program that processes this external input file and produces some kind of report. Such a program should be general enough that it could process any external input file that matches a specific format.

The Scanner class that we have been using since chapter 4 is flexible in that we can attach Scanner objects to many different kinds of input. You can think of a Scanner object as being like a faucet that you can attach to a pipe that has water flowing through it. For example, in a house we'd attach a faucet to a pipe carrying water from the city or from a well. But we also see faucets in mobile homes and airplanes where the source of water is different.

We have been constructing our Scanner objects by passing System.in to the Scanner constructor:

        Scanner console = new Scanner(System.in);
This instructs the computer to construct a Scanner that reads from the console (i.e, pausing for input from the user). Instead of passing "System.in" to the constructor, we can pass information about the external input file to have the input come from the file rather than from the console. In particular, we can construct Scanner objects by passing an object of type File. A File object stores information about where to find an external input file. File objects in turn are constructed by passing a String that represents the file's name. For example, given our file called "numbers.dat", we can construct a File object that is linked to it:

        new File("numbers.dat")
And using this File object we can construct a Scanner object:

        new Scanner(new File("numbers.dat"))
Putting this all together, we'd say something like the following:

        Scanner input = new Scanner(new File("numbers.dat"));
This particular line of code or something like it will appear in all of your file processing programs. Once we've constructed the Scanner so that it reads from the file, we can manipulate it like any other Scanner. When we read from the console, we always prompt before reading to give the user an indication of what kind of data we want. When we read from a file, we don't need to prompt because the data is already there, stored in the file. For example, we might write the following short program to read 5 numbers from the file and to echo the five numbers along with their sum:


        // Flawed program--doesn't even compile.
        
        import java.io.*;
        import java.util.*;
        
        public class Echo1 {
            public static void main(String[] args) {
                Scanner input = new Scanner(new File("numbers.dat"));
                
                double sum = 0.0;
                for (int i = 1; i <= 5; i++) {
                    double next = input.nextDouble();
                    System.out.println("number " + i + " = " + next);
                    sum += next;
                }
                System.out.println("Sum = " + sum);
            }
        }
The File class that we want to use is in a package known as java.io ("io" is short for "input/output"), which is why there is an import declaration at the beginning of the program for the java.io package. We also have the import declaration for java.util to get access to the Scanner class. Unfortunately, even with these imports the program doesn't compile. The compiler will give a message like the following:

    Echo1.java:8: unreported exception java.io.FileNotFoundException; must be
    caught or declared to be thrown
            Scanner input = new Scanner(new File("numbers.dat"));
                            ^
    1 error
The issue involves exceptions, which were first introduced in chapter 4. Remember that exceptions are errors that prevent a programming from continuing normal execution. In this case the compiler is worried that it might not be able to find a file called "numbers.dat". What is it supposed to do if that happens? It wouldn't have any way to continue executing the rest of the code because it wouldn't have a file to read from.

If the program is unable to locate the specified input file, it will throw what is known as a FileNotFoundException. This particular exception is known as a checked exception.

Checked Exception

An exception that must be caught or specifically declared in the header of the method that might generate it.

Because FileNotFoundException is a checked exception, we can't just ignore it. Java provides a construct known as the try/catch statement for handling such errors. Later in this chapter we will see how to use a try/catch to handle this error. But for now we will use a less sophisticated but simpler approach. Java allows us to avoid handling this error as long as we clearly indicate the fact that we aren't handling the error. In particular, we can include what is known as a "throws" clause in the header for the main method to clearly state the fact that our main method might generate this exception:

        // This version does compile because of the throws clause in the header
        // for method main.
        
        import java.io.*;
        import java.util.*;
        
        public class Echo2 {
            public static void main(String[] args) throws FileNotFoundException {
                Scanner input = new Scanner(new File("numbers.dat"));
                
                double sum = 0.0;
                for (int i = 1; i <= 5; i++) {
                    double next = input.nextDouble();
                    System.out.println("number " + i + " = " + next);
                    sum += next;
                }
                System.out.println("Sum = " + sum);
            }
        }
This version of the program compiles and executes properly, generating the following output:

        number 1 = 308.2
        number 2 = 14.9
        number 3 = 7.4
        number 4 = 2.8
        number 5 = 3.9
        Sum = 337.19999999999993
If you add up those numbers by hand, you get the answer 337.2. Java comes up with a slightly different answer because of roundoff errors. These values are converted into what is known as "binary" or "base 2" and are stored with a limited accuracy, so it is possible to get these slight variations.

The preceding program read exactly 5 numbers from the file. More typically we read indefinitely using a while loop as long as there are more numbers to read. Remember that the Scanner class includes a series of "has" methods that parallel the various "next" methods. In this case, we are using nextDouble to read a value of type double, so we can use hasNextDouble to test whether there is such a value to read.


        // Variation that reads while there are more numbers to read.
        
        import java.io.*;
        import java.util.*;
        
        public class Echo3 {
            public static void main(String[] args) throws FileNotFoundException {
                Scanner input = new Scanner(new File("numbers.dat"));
                
                double sum = 0.0;
                int count = 0;
                while (input.hasNextDouble()) {
                    double next = input.nextDouble();
                    count++;
                    System.out.println("number " + count + " = " + next);
                    sum += next;
                }
                System.out.println("Sum = " + sum);
            }
        }
This program would work on an input file with any number of numbers. Our file happens to have eight numbers and when we run this version of the program, we get the following output:

        number 1 = 308.2
        number 2 = 14.9
        number 3 = 7.4
        number 4 = 2.8
        number 5 = 3.9
        number 6 = 4.7
        number 7 = -15.4
        number 8 = 2.8
        Sum = 329.29999999999995

6.2.1 Structure of Files and Consuming Input

We think of text as being two-dimensional, but from the computer's point of view, each file is really just a one-dimensional sequence of characters. For example, consider the file called "numbers.dat" that we saw in the last section:

        308.2 14.9 7.4
        2.8
        
        
        3.9 4.7 -15.4
        2.8
We think of this as a 6-line file with text going across and down and two blank lines in the middle. The computer views the file differently. When someone types in a file like this, they hit the "Enter" key to go to a new line. This inserts special "new line" characters in the file. We have seen that the escape sequence "\n" can be used to produce a newline character for output. We can annotate the file above with "\n" characters to indicate the end of each line:

        308.2 14.9 7.4\n
        2.8\n
        \n
        \n
        3.9 4.7 -15.4\n
        2.8\n
Once we have marked the end of each line, we no longer need to use a 2-dimensional representation. We can collapse this to a one-dimensional sequence of characters:

        308.2 14.9 7.4\n2.8\n\n\n3.9 4.7 -15.4\n2.8\n
From this one-dimensional sequence, we can reconstruct the various lines of the file. This is how the computer views the file, as a one-dimensional sequence of characters including special characters that represent "new line". On some systems, including Windows machines, there are two different characters that represent "new line", but for our discussion, we'll use just "\n" to represent both. Objects like Scanner handle these differences for us so we can generally ignore them, but for those who are interested, the brief explanation is that Windows machines end each line with a "\r" followed by a "\n".

To process a file the Scanner object keeps track of a current position in the file. You can think of this as a cursor or pointer into the file.

Input cursor

A pointer to the current position in an input file.

When the Scanner object is first constructed, this cursor points to the beginning of the file. But as we perform various "next" operations, this cursor moves forward. The Echo3 program from the last section processes the file through a series of calls on nextDouble. Let's take a moment to examine in detail how that works. When the Scanner is first constructed, the input cursor will be positioned at the beginning of the file (indicated below with an up-arrow pointing at the first character in the file):

        308.2 14.9 7.4\n2.8\n\n\n3.9 4.7 -15.4\n2.8\n
        ^
        |
      input
      cursor
After the first call on nextDouble, the cursor will be positioned in the middle of the first line after the token "308.2".

        308.2 14.9 7.4\n2.8\n\n\n3.9 4.7 -15.4\n2.8\n
             ^
             |
           input
           cursor
We refer to this process as consuming input.

Consuming input

Moving the input cursor forward past some input.

The first call on nextDouble consumes the text "308.2" from the input file and leaves the input cursor positioned at the first character after this token. Notice that this leaves the input cursor positioned at a space. When the second call is made on nextDouble, the Scanner first skips past this space to get to the next token and then consumes the text "14.9" and leaves the cursor positioned at the space that follows it:

        308.2 14.9 7.4\n2.8\n\n\n3.9 4.7 -15.4\n2.8\n
                  ^
                  |
                input
                cursor
A third call on nextDouble skips the space it is positioned at and consumes the text "7.4".

        308.2 14.9 7.4\n2.8\n\n\n3.9 4.7 -15.4\n2.8\n
                      ^
                      |
                    input
                    cursor
At this point, the input cursor is positioned at the newline character at the end of the first line of input. A fourth call on nextDouble skips past this newline character and consumes the text "2.8".

        308.2 14.9 7.4\n2.8\n\n\n3.9 4.7 -15.4\n2.8\n
                           ^
                           |
                         input
                         cursor
Notice that in skipping past the first newline character, the input cursor has moved into data stored on the second line of input (the 2.8). At this point, the input cursor is positioned at the end of the second line of input because it has consumed the "2.8" token. When a fifth call is made on nextDouble, the Scanner finds two newline characters in a row. This isn't a problem for the Scanner, because it simply skips past any leading whitespace characters (spaces, tabs, newline characters) until it finds an actual token. So it skips both of these newline characters and consumes the text "3.9".

        308.2 14.9 7.4\n2.8\n\n\n3.9 4.7 -15.4\n2.8\n
                                    ^
                                    |
                                  input
                                  cursor
At this point the input cursor is positioned in the middle of the fifth line of input (the third and fourth lines were blank). The sixth call on nextDouble consumes the text "4.7":


        308.2 14.9 7.4\n2.8\n\n\n3.9 4.7 -15.4\n2.8\n
                                        ^
                                        |
                                      input
                                      cursor
The seventh call consumes the text "-15.4":

        308.2 14.9 7.4\n2.8\n\n\n3.9 4.7 -15.4\n2.8\n
                                              ^
                                              |
                                            input
                                            cursor
And the eight call consumes the text "2.8":

        308.2 14.9 7.4\n2.8\n\n\n3.9 4.7 -15.4\n2.8\n
                                                   ^
                                                   |
                                                 input
                                                 cursor
At this point the input cursor is positioned at the newline character at the end of the file. If you attempted to call nextDouble again, it would throw an exception because there are no more tokens left to process. But remember that the Echo3 program has a while loop that calls hasNextDouble() before calling nextDouble() to make sure that there actually is a double value to process. When the input cursor reaches the newline at the end of the file, the Scanner notices that there are no more double values to read and it returns false when hasNextDouble is called. That stops the program from executing.

When you call methods like hasNextDouble, the Scanner looks ahead in the file to see whether there is a next token and whether it could be interpreted as being of that type (in this case a double). So the Echo3 program will continue executing until it reaches the end of the file or until it encounters a token that could not be interpreted as a double.

Scanner objects are very flexible about when and how you consume input. You can consume part of the input in one section of code, then do some other work, then come back to consuming more of the input in another section of code. You decide exactly how much input to consume at a time through the calls you make on the Scanner object.

Keeping track of exactly where the input cursor is positioned can be tricky. If the data is line-oriented, it is best to read it in a line-oriented manner. We will see how to do that in a later section.

6.2.2 File Names

In the previous section we used the file name "numbers.dat". When Java finds you using a simple name like that (also called a relative file path), it looks in the current directory to find the file.

The definition of "current directory" varies depending upon what Java environment you are using. If you are using the TextPad environment, then the current directory is the directory in which your program appears. This is the behavior we'll assume for the examples in this textbook. Other environments may have different behavior. For example if you use the DrJava environment, you will find that the current directory is the directory where the DrJava program is stored. Make sure you know your how your environment deals with this and if necessary, adjust its settings to make it compatible with our examples.

You can also use a fully-qualified file name (sometimes called an absolute file path). For example, if you are on a Windows machine and you have stored the file in a directory known as c:\data, we could use a file name like this:

        Scanner input = new Scanner(new File("c:\\data\\numbers.dat"));
Notice that we have to use the escape sequence "\\" to represent a single backslash character. This approach works well when you know exactly where your file is going to be stored on your system.

But if you create a folder on your desktop, what is the absolute path of that folder? Most operating systems have a notion of a 'home directory.' Let's assume that your user name is smith. In Windows XP and 2000, your home directory is generally located in the folder C:\Documents and Settings\smith. If you created a folder on your desktop named HW6, the absolute path of this folder would be C:\Documents and Settings\smith\Desktop\HW6. On Linux, the equivalent would likely be /home/smith/Desktop/HW6. On a Macintosh running OS X, it is usually /Users/smith/Desktop/HW6. If you are working in a shared laboratory or on shared computer systems, the home directory may be set up differently.

Sometimes rather than writing a file's path name in the code ourselves, we ask the user for a file name. In the last chapter we saw a program called FindSum that prompted the user for a series of numbers to add together. Below is a variation that prompts the user for the name of a file of numbers to be added together.


        // This program adds together a series of numbers from a file.  It prompts
        // the user for the file name, then reads the file and reports the sum.
        
        import java.io.*;
        import java.util.*;
        
        public class FindSum2 {
            public static void main(String[] args) throws FileNotFoundException {
                System.out.println("This program will add together a series of real");
                System.out.println("numbers from a file.");
                System.out.println();
                
                Scanner console = new Scanner(System.in);
                
                System.out.print("What is the file name? ");
                String name = console.nextLine();
                
                Scanner input = new Scanner(new File(name));
                System.out.println();
                
                double sum = 0;
                while (input.hasNextDouble()) {
                    double next = input.nextDouble();
                    sum += next;
                }
                System.out.println("Sum = " + sum);
            }
        }
We read the file name using a call on nextLine() to read an entire line of input from the user. This allows the user to type in file names that have spaces in them. Notice that we still need the "throws FileNotFoundException" in the header for main because even though we are prompting the user for a file name, there won't necessarily be a file of that name.

If we have this program read from the file "numbers.dat" that we saw in the last section, then the program would execute like this:

        This program will add together a series of real
        numbers from a file.
        
        What is the file name? numbers.dat
        
        Sum = 329.29999999999995
The user also has the option of specifying a full file name, as in:

        This program will add together a series of real
        numbers from a file.
        
        What is the file name? c:\data\numbers.dat
        
        Sum = 329.29999999999995
Notice that the user doesn't have to type two backslashes to get a single backslash. That's because the Scanner object that reads the user's input is able to read it without escape sequences.


6.2.3 Useful Methods of File Objects

A File object is a representation of a potential file on the user's computer. Depending on the String passed to the File constructor, the File object might represent a file that already exists, or it might represent one that we'd like to create in our program. Creating a File object does not in itself create a new file on the user's hard drive, but we can ask a File object to create the corresponding real file if we so desire.

Here is a list of several methods of File objects that you might use:

Useful Methods of File Objects
Method Description
delete() deletes the given file
exists() whether or not this file exists on the system
getAbsolutePath() the full path where this file is located
getName() the name of this file as a String, without any path attached
getPath() the path where this file is located, without the actual file's name
isDirectory() whether this file represents a directory/folder on the system
isFile() whether this file represents a file (non-folder) on the system
length() the number of characters in this file
mkdirs() creates the directory represented by this file, if it does not exist
renameTo(File) changes this file's name to be the given file's name

6.2.4 A more complex input file

Suppose that you have an input file that has information about how many hours have been worked by each employee of a company. For example, it might look like the following:

        Erica 7.5 8.5 10.25 8 8.5
        Greenlee 10.5 11.5 12 11 10.75
        Simone 8 8 8
        Ryan 6.5 8 9.25 8
        Kendall 2.5 3
The idea is that we have a list of hours worked by each employee and we want to find out the total hours worked by each individual. We can construct a Scanner object linked to this file to solve this task. As you start writing more complex file processing programs, you will want to divide the program into methods to break up the code into logical subtasks. In this case, we can open the file in main and write a separate method to process the file.

Most file processing will involve while loops because we won't know in advance how much data the file has in it. We'll choose different tests depending upon the particular file we are processing, but they will almost all be calls on the various "has" methods of the Scanner class. We basically want to say, "while you have more data for me to process, let's keep reading."

In this case we have a series of input lines that each begin with a name. For this program we are assuming that names are simple, with no spaces in the middle. That means we'll be reading them with a call on the next() method. As a result, our overall test involves seeing if there is another name in the input file:

while (input.hasNext()) { <process next person> } So how do we process one person? We have to read their name and then read their list of hours. If you look at the sample input file, you will see that the list of hours is not always the same length. This is a common occurrence in input files. For example, some employees might have worked on 5 different days while others worked only 2 days or 3 days. So we will use a loop for this as well. This is a nested loop. The outer loop is handling one person at a time and the inner loop will handle one number at a time. The task is a fairly straightforward cumulative sum:

double sum = 0.0; while (input.hasNextDouble()) { sum += input.nextDouble(); } Putting this all together, we end up with the following complete program.

        // This program reads an input file of hours worked by various employees.  Each
        // line of the input file should have an employee's name (without any spaces)
        // followed by a list of hours worked, as in:
        //
        //     Erica 7.5 8.5 10.25 8
        //     Greenlee 10.5 11.5 12 11
        //     Ryan 6.5 8 9.25 8
        //
        // The program reports the total hours worked by each employee.
        
        import java.io.*;
        import java.util.*;
        
        public class HoursWorked {
            public static void main(String[] args) throws FileNotFoundException {
                Scanner input = new Scanner(new File("hours.dat"));
                process(input);
            }
            
            public static void process(Scanner input) {
                while (input.hasNext()) {
                    String name = input.next();
                    double sum = 0.0;
                    while (input.hasNextDouble()) {
                        sum += input.nextDouble();
                    }
                    System.out.println("Total hours worked by " + name + " = " + sum);
                }
            }
        }
Notice that we again need the "throws FileNotFoundException" in the header for main. We don't need to include this in the "process" method because the code to open the file appears in method main.

If we put the input above into a file called "hours.dat" and execute the program, we get the following result.

        Total hours worked by Erica = 42.75
        Total hours worked by Greenlee = 55.75
        Total hours worked by Simone = 24.0
        Total hours worked by Ryan = 31.75
        Total hours worked by Kendall = 5.5

6.3 Line-based input and String-based Scanners

So far we have been looking at programs that process input token by token. We often find ourselves working with input files that are line-based where each line of input represents a different case to be handled separately from the rest. This was true of the data file in the last section where each line of input represented information for a different employee of the company.

This leads to two different styles of file processing.

Token-Based Processing

Processing input token by token (e.g., one word at a time or one number at a time).

Line-Based Processing

Processing input line by line (i.e., reading in entire lines of input at a time).

Most file processing involves a combination of these two styles and the Scanner class is flexible enough to allow us to write programs that have both styles of processing. For line-based processing, we would want to use the nextLine and hasNextLine methods of the Scanner class. The basic structure will look something like this:

while (input.hasNextLine()) { String text = input.nextLine(); <process text> } This loop reads the input line by line until it runs out of lines to process. Suppose we want to rewrite the HoursWorked program of the last section using this approach. Recall that the main method looked like this:

        public static void main(String[] args) throws FileNotFoundException {
            Scanner input = new Scanner(new File("hours.dat"));
            process(input);
        }
If we incorporate the pattern for line-based reading, we end up with this main:

public static void main(String[] args) throws FileNotFoundException { Scanner input = new Scanner(new File("hours.dat")); while (input.hasNextLine()) { String text = input.nextLine(); <process text> } } The good thing about this change is that our program is reading the file line by line because each line contains information about a different person. But we still have to fill in the "process text" part and here we run into a problem. The text is an input line that has a name and a series of numbers representing the number of hours worked on different days. In other words, the input line is composed of several pieces (several tokens) that we want to process piece by piece.

Fortunately there is a convenient way to do this. We can construct a Scanner object from an individual String. Remember that just as a faucet can be attached to different sources of water (a faucet in a house attached to city water or well water versus a faucet on an airplane attached to a tank of water), we can attach a Scanner to different sources of input. We've seen how to attach it to the console (System.in) and to a file (passing a File object). We can also attach it to an individual String. For example, we might write this code:

        Scanner input = new Scanner("18.4 17.9 8.3 2.9");
This constructs a Scanner that gets its input from the String that we used to construct it. This Scanner has an input cursor just like a Scanner linked to a file. Initially the input cursor is positioned at the first character in the String and it moves forward as we read tokens from the Scanner.

Below is a short program to demonstrate this:

        import java.util.*;
        
        public class StringScannerExample {
            public static void main(String[] args) {
                Scanner input = new Scanner("18.4 17.9 8.3 2.9");
                while (input.hasNextDouble()) {
                    double next = input.nextDouble();
                    System.out.println(next);
                }
            }
        }
It produces the following output:

        18.4
        17.9
        8.3
        2.9
Notice that it produces four lines of output because there were four numbers in the String that we used to construct the Scanner.

When we have a file that requires this combination of line-based processing and token-based processing, we can use a slightly different approach by constructing a different String-based Scanner for each line of the input file:

while (input.hasNextLine()) { String text = input.nextLine(); Scanner data = new Scanner(text); <process data> } With this approach, we end up with a lot of Scanner objects. We have a Scanner object called input that is keeping track of the external input file. We use that Scanner to read entire lines of input. But each time through this while loop, we construct a new Scanner that stores the data from a single line of input. We can then used token-based processing for these "little" Scanner objects because they each contain just a single line of data in them.

This combination of line-based and token-based processing is powerful. You will find that the template above (and slight variations) can be used to process a large variety of input files.

In the case of the HoursWorked program, each input line contained information for a single employee. Processing it involves reading the name and then reading numbers as long as there are more numbers to read. We can put this all together into a new version of the program:

        // This program reads an input file of hours worked by various employees.  Each
        // line of the input file should have an employee's name (without any spaces)
        // followed by a list of hours worked, as in:
        //
        //     Erica 7.5 8.5 10.25 8
        //     Greenlee 10.5 11.5 12 11
        //     Ryan 6.5 8 9.25 8
        //
        // The program reports the total hours worked by each employee.
        
        import java.io.*;
        import java.util.*;
        
        public class HoursWorked2 {
            public static void main(String[] args) throws FileNotFoundException {
                Scanner input = new Scanner(new File("hours.dat"));
                while (input.hasNextLine()) {
                    String text = input.nextLine();
                    Scanner data = new Scanner(text);
                    process(data);
                }
            }
            
            // Processes data from the given Scanner (name and list of hours worked)
            public static void process(Scanner input) {
                String name = input.next();
                double sum = 0.0;
                while (input.hasNextDouble()) {
                    sum += input.nextDouble();
                }
                System.out.println("Total hours worked by " + name + " = " + sum);
            }
        }
Notice that the main method includes the line-based processing of reading entire lines of input from the file. Each such line is turned into its own Scanner and we call the "process" method to handle the tokens within the line. The "process" method uses token-based processing (calling the methods next() and nextDouble()). Notice that it now has just a single while loop versus the nested while loop in the original because it is now processing just one line of data rather than the entire file.

6.4 Case Study: Weighted GPA

The HoursWorked program required that names have no spaces in them. This isn't a very practical restriction. It would be more convenient to be able to type anything for a name, including spaces. One way to do that is to put the name on a separate line from the rest of the data. For example, suppose that you want to compute weighted GPAs for a series of students. Suppose, for example, that a student has a 3-unit 3.0, a 4-unit 2.9, a 3-unit 3.2 and a 2-unit 2.5. We can compute an overall GPA that is weighted by the individual units for each course.

So we might have an input file that has its data on pairs of lines. For each pair the name will appear on the first line and the grade data will appear on the second line. For example, we might have an input file that looks like this:

        Erica Kane
        3 2.8 4 3.9 3 3.1
        Greenlee Smythe
        3 3.9 3 4.0 4 3.9
        Ryan Laveree
        2 4.0 3 3.6 4 3.8 1 2.8
        Adam Chandler
        3 3.0 4 2.9 3 3.2 2 2.5
        Adam Chandler, Jr
        4 1.5 5 1.9
This data is line-based, but some of the lines have individual tokens that we'll need to process. So we'll want to use a slight variation of the template given in the last section. Recall that in general the way that we get a combination of line-based and token-based processing is to write code like this:

while (input.hasNextLine()) { String text = input.nextLine(); Scanner data = new Scanner(text); <process data> } For this file, our data appears as pairs of lines where the first line stores the name and the second line stores the grade data. We won't need to process the name token by token, but we'll need to process the grades as individual tokens. So we can use the following slight variation that reads two lines of data each time through the loop and that constructs a Scanner object for the second line of input that contains the grade information:


while (input.hasNextLine()) { String name = input.nextLine(); String grades = input.nextLine(); Scanner data = new Scanner(grades); <process this student's data> } To complete this program, we have to figure out how to process the grade data in our Scanner called "data". This is a good place to introduce a static method. The code above involves processing the overall file. The task of processing one list of grades is a lower level task that can be split off into its own method. Let's call it processGrades. Obviously it can't do its work without the Scanner object that has the grades, so we'll pass that as a parameter. What exactly needs to be done? The plan was to compute a weighted GPA for each student. So this method needs to read the individual grades and turn that into a single GPA score.

Weighted GPAs involve computing a value known as the "quality points" for each grade. The quality points are defined as the units times the grade. The weighted GPA is calculated by dividing the total quality points by the total units. So we just need to add up the total quality points and add up the total units, then divide. This involves a pair of cumulative sum tasks that we can express in pseudocode as follows:

        set total units to 0.
        set total quality points to 0.
        while (more grades to process) {
            read next units and next grade.
            add next units to total units.
            add (next units) * (next grade) to total quality points.
        }
        set gpa to (total quality points)/(total units).
This is fairly simple to translate into Java code by incorporating our Scanner object called "data":

        double totalQualityPoints = 0.0;
        double totalUnits = 0;
        while (data.hasNextInt()) {
            int units = data.nextInt();
            double grade = data.nextDouble();
            totalUnits += units;
            totalQualityPoints += units * grade;
        }
        double gpa = totalQualityPoints/totalUnits;
Because our Scanner object data was constructed from a single line of input, we can process just one person's grades with this loop. There is still a potential problem. What if there are no grades? Some students might have dropped all of their classes, for example. There are several ways we might handle that situation, but let's assume that it is appropriate to use a GPA of 0.0 in that case.

Making that correction and putting this into a method, we end up with the following code.

        public static double processGrades(Scanner data) {
            double totalQualityPoints = 0.0;
            double totalUnits = 0;
            while (data.hasNextInt()) {
                int units = data.nextInt();
                double grade = data.nextDouble();
                totalUnits += units;
                totalQualityPoints += units * grade;
            }
            if (totalUnits == 0) {
                return 0.0;
            } else {
                return totalQualityPoints/totalUnits;
            }
        }
Recall that our high-level code looked like this: while (input.hasNextLine()) { String name = input.nextLine(); String grades = input.nextLine(); Scanner data = new Scanner(grades); <process this student's data> } We can now start to fill in the details of what it means to "process this student's data." We will call the method we just wrote to process the grades for this student and to turn it into a weighted GPA and then print the results:

        double gpa = processGrades(data);
        System.out.println("GPA for " + name + " = " + gpa);
This would complete the program, but let's add one more calculation. Let's compute the max and min GPA that we see among these students. We can accomplish this fairly easily with some simple if statements after the println:

        if (gpa > max) {
            max = gpa;
        }
        if (gpa < min) {
            min = gpa;
        }
We simply compare the current gpa against what we currently consider the max and min, resetting if the new gpa represents a new max or a new min. But how do we initialize these variables? We have two approaches to choose from. One approach involves initializing the max and the min to the first value in the sequence. We could do that, but it would make our loop much more complicated than it is currently. The second approach involves setting the max to the lowest possible value and setting the min to the highest possible value. This approach isn't always possible because we don't always know how high or low our values might go. But in the case of GPAs, we know that they will always be between 0.0 and 4.0.

Thus, we can initialize the variables as follows:

        double max = 0.0;
        double min = 4.0;
It may seem odd to set the max to 0 and the min to 4, but that's because we are intending to have them reset inside the loop. If the first student has a GPA of 3.2, for example, then this will constitute a new max (higher than 0.0) and a new min (lower than 4.0). Of course, it's possible that all students end up with a 4.0, but then our choice of 4.0 for the min is appropriate. Or all students could end up with a 0.0, in which case our choice of a max of 0.0 is appropriate.

Putting this all together we get the following complete program.

        // This program reads an input file with GPA data for a series of students
        // and reports a weighted GPA for each.  The input file should consist of
        // a series of line pairs where the first line has a student's name and the
        // second line has a series of grade entries.  The grade entries should be
        // a number of units (an integer) followed by a grade (a number between 0.0
        // and 4.0).  For example, the input might look like this:
        //
        //     Erica Kane
        //     3 2.8 4 3.9 3 3.1
        //     Greenlee Smythe
        //     3 3.9 3 4.0 4 3.9
        //     Ryan Laveree
        //     2 4.0 3 3.6 4 3.8 1 2.8
        //
        // The program reports the weighted GPA for each student along with the
        // max and min GPA.
        
        import java.io.*;
        import java.util.*;
        
        public class Gpa {
            public static void main(String[] args) throws FileNotFoundException {
                Scanner input = new Scanner(new File("gpa.dat"));
                process(input);
            }
            
            public static void process(Scanner input) {
                double max = 0.0;
                double min = 4.0;
                while (input.hasNextLine()) {
                    String name = input.nextLine();
                    String grades = input.nextLine();
                    Scanner data = new Scanner(grades);
                    double gpa = processGrades(data);
                    System.out.println("GPA for " + name + " = " + gpa);
                    if (gpa > max) {
                        max = gpa;
                    }
                    if (gpa < min) {
                        min = gpa;
                    }
                }
                System.out.println();
                System.out.println("max GPA = " + max);
                System.out.println("min GPA = " + min);
            }
            
            public static double processGrades(Scanner data) {
                double totalQualityPoints = 0.0;
                double totalUnits = 0;
                while (data.hasNextInt()) {
                    int units = data.nextInt();
                    double grade = data.nextDouble();
                    totalUnits += units;
                    totalQualityPoints += units * grade;
                }
                if (totalUnits == 0) {
                    return 0.0;
                } else {
                    return totalQualityPoints / totalUnits;
                }
            }
        }
Once again our main method has the "throws FileNotFoundException" in its header. This program executes as follows assuming the data above is placed in a file called "gpa.dat".

        GPA for Erica Kane = 3.3299999999999996
        GPA for Greenlee Smythe = 3.9299999999999997
        GPA for Ryan Laveree = 3.6799999999999997
        GPA for Adam Chandler = 2.9333333333333336
        GPA for Adam Chandler, Jr = 1.7222222222222223
        
        max GPA = 3.9299999999999997
        min GPA = 1.7222222222222223

6.5 Using PrintStream to Create Output Files

All of our programs so far have sent their output to the console window by calling System.out.print or System.out.println. Just as we can read input from an external file instead of reading from the console, we can write output to an external file instead of writing it to the console. There are many ways to accomplish this. The simplest approach is to take advantage of what you already know. By now, you've learned all about how print and println statements work. We can leverage that knowledge to allow you to easily create external output files.

If you look at Sun's Java documentation, you will find that System.out is a variable that stores a reference to an object of type PrintStream. The print and println statements you've been writing are calls on methods that are part of the PrintStream class. The variable System.out stores a reference to a special PrintStream object that is tied to the console window. But we can construct other PrintStream objects that send their output to other places. Suppose, for example, that we want to send output to a file called results.txt. We can construct a PrintStream object as follows:

PrintStream output = new PrintStream(new File("results.txt")); This looks a lot like the line of code we've been using to construct a Scanner tied to an external input file. In this case, the computer is creating an external output file. If there is no such file, then the program creates it. If there is such a file, it overwrites the current version. Initially the file will be empty. It will end up containing whatever output you tell it to produce through calls on print and println.

This line of code can generate an exception if Java is unable to create the file you've described. There are many reasons this might happen. You might not have write access to the directory or the file might be locked in some way because it is being used by another program. Just as with the line of code that creates a file-based Scanner, this line of code potentially throws a FileNotFoundException. That means that Java will require us to have the "throws" clause in whatever method contains this line of code. The simplest approach is to have this line of code in main. In fact, it is common practice to have the main method begin with the lines of code that deal with these external files (both input and output).

Once you have constructed a PrintStream object, how do you use it? You already know what to do. Just think of everything that you know about System.out. You've been writing print and println statements for a long time now and they've always begun with either System.out.print or System.out.println. In this case, we have a variable called output that is tied to an external output file. So you end up saying things like output.print and output.println.

As a simple example, remember that in Chapter 1, we looked at the following variation of the simple "hello world" program that produces several lines of output:

        public class Hello3 {
            public static void main(String[] args) {
                System.out.println("Hello, world!");
                System.out.println();
                System.out.println("This program produces three lines of output.");
            }
        }
Below is a variation that sends its output to a file called "hello.txt":

        import java.io.*;
        import java.util.*;
        
        public class Hello4 {
            public static void main(String[] args) throws FileNotFoundException {
                PrintStream output = new PrintStream(new File("hello.txt"));
                output.println("Hello world.");
                output.println();
                output.println("This program produces three lines of output.");
            }
        }
When you run this new version of the program, a curious thing happens. Nothing seems to happen. That's because we're used to having the output of the program go to the console window. In this case, the output was directed to a file instead. After the program finishes executing, we can open up the file called "hello.txt" and we'll find that it contains the following:

        Hello world.
        
        This program produces three lines of output.
The main point is that everything you've learned to do with System.out you can also do with these PrintStream objects that are tied to files.

We can also write methods that take PrintStream objects as parameters. For example, consider the task of fixing the spacing for a series of words. We might have a line of text that has erratic spacing, as in:

        a       new  nation,     conceived  in    liberty
Suppose we want to print this text with the spacing fixed so that there is exactly one space between each pair of words:

        a new nation, conceived in liberty
How do we do that? Assume that we are writing a method that is passed a String to echo and a PrintStream object to send the output to:

public static void echoFixed(String text, PrintStream output) We can construct a Scanner from the String and then use the next() method to read one word at a time. Recall that the Scanner class ignores whitespace, so we'll get just the individual words without all of the spaces between them. As we read words, we need to echo them to the PrintStream object. Here's a first attempt:

        Scanner data = new Scanner(text);
        while (data.hasNext()) {
            output.print(data.next());
        }
This code does a great job of deleting the long sequences of spaces from the String, but it goes too far. It eliminates all of the spaces. We want one space between each pair of words, so we have to include some spaces for our output:

        Scanner data = new Scanner(text);
        while (data.hasNext()) {
            output.print(data.next() + " ");
        }
This ends up looking pretty good, but it prints an extra space at the end of the line. If we want to get rid of that space so that we truly have spaces appearing only between pairs of words, we have to change this slightly. This is a classic fencepost problem. We want the spaces between the words, so we have one more word than we have spaces. We can use the typical solution of processing the first word before the loop begins and turning around the order of the other two operations inside the loop (print a space and then the word):

        Scanner data = new Scanner(text);
        output.print(data.next());
        while (data.hasNext()) {
            output.print(" " + data.next());
        }
This now works well for almost all cases. By including our fencepost solution of echoing the first word before the loop begins, we've introduced an assumption that there is a first word. If the String has no words at all, then this call on next() will throw an exception. So we need a test for the case where the String has no words at all. And if we also want this to produce a complete line of output, we have to include a call on println to complete the line of output after printing the individual words. Putting all of this together, we get the following:


        public static void echoFixed(String text, PrintStream output) {
            Scanner data = new Scanner(text);
            if (data.hasNext()) {
                output.print(data.next());
                while (data.hasNext()) {
                    output.print(" " + data.next());
                }
            }
            output.println();
        }
Notice how we are now calling output.print and output.println instead of calling System.out.print and System.out.println. An interesting thing about this method is that it can be used to send output to an external output file but it can also be used to send output to System.out. The method header indicates that it works on any PrintStream. So we can call it to send output to a PrintStream object tied to an external file or we can call it to send output to System.out.

Below is a complete program that uses this method to fix the spacing in an entire input file of text. To underscore the flexibility of the method, the program sends its output to both the external file and the console. So you'll see the output appear in the console window as usual, but you'll also find that a file has been constructed called words2.txt that contains the output.

        import java.io.*;
        import java.util.*;
        
        public class FixSpacing {
            public static void main(String[] args) throws FileNotFoundException {
                Scanner input = new Scanner(new File("words.txt"));
                PrintStream output = new PrintStream(new File("words2.txt"));
                while (input.hasNextLine()) {
                    String text = input.nextLine();
                    echoFixed(text, output);
                    echoFixed(text, System.out);
                }
                output.close();
            }
            
            public static void echoFixed(String text, PrintStream output) {
                Scanner data = new Scanner(text);
                if (data.hasNext()) {
                    output.print(data.next());
                    while (data.hasNext()) {
                        output.print(" " + data.next());
                    }
                }
                output.println();
            }
        }

Given the following input file:

            four         score    and
        seven    years ago    our
           fathers brought  forth on this     continent 
        a       new  nation,     conceived  in    liberty
          and  dedicated   to  the  proposition    that
           all   men    are     created    equal
It produces the following output file called "words2.txt":

        four score and
        seven years ago our
        fathers brought forth on this continent
        a new nation, conceived in liberty
        and dedicated to the proposition that
        all men are created equal

6.6 Try/Catch Statements

Including the clause "throws FileNotFoundException" in the header for main allows our programs to compile, but it's not a very satisfying solution to the underlying problem. To actually handle the potential error, we'd want to use something called a try/catch statement. We will not be exploring all of the details of try/catch, but we will examine how to write some basic try/catch statements that we could use for file processing.

The try/catch statement has the following general syntax.

try { <statement>; <statement>; ... <statement>; } catch (<type> <name>) { <statement>; <statement>; ... <statement>; } Notice that it is divided into two blocks using the keywords "try" and "catch". The first block has the code you want to execute. The second block has error recovery code that should be executed if an exception is thrown. So think of this as saying, "Try to execute these statements, but if something goes wrong, I'm going to give you some other code in the catch part that you should execute if an error occurs."

Notice that the catch part of this statement has a set of parentheses in which you include a type and name. The type should be the type of exception you are trying to catch. The name can be any legal identifier. For example, in the case of our Scanner code, we know that a FileNotFoundException might be thrown. What do we do if the exception occurs? That's a tough question, but for now let's just write an error message.


        try {
            Scanner input = new Scanner(new File("numbers.dat"));
        } catch (FileNotFoundException e) {
            System.out.println("File not found");
        }
This code says to try constructing the Scanner from the file "numbers.dat" but if the file is not found, then print an error message instead. This is the basic idea we want to follow, but there are several issues we must address to make this code work for us. First of all, there is a scope issue. The variable input isn't going to be much use to us if it's trapped inside the try block. So we have to declare the Scanner variable outside the try/catch statement:

        Scanner input;
        try {
            input = new Scanner(new File("numbers.dat"));
        } catch (FileNotFoundException e) {
            System.out.println("File not found");
        }
We have a bigger problem in that simply printing an error message isn't a good way to recover from this problem. How is the program supposed to proceed with execution if it can't read from the file? It probably can't. So what would be a more appropriate way to recover from the error? That depends a lot on the particular program you are writing, so the answer is likely to vary from one program to the next.

Let's explore how you might handle this when you are prompting the user for a file name in the console window. In that case, we could keep prompting the user until they give us a legal file name. Let's begin by modifying the code above to prompt and read a file name.

        Scanner input;
        System.out.print("What is the name of the input file? ");
        String name = console.nextLine();
        try {
            input = new Scanner(new File(name));
        } catch (FileNotFoundException e) {
            System.out.println("File not found");
        }
This code catches the potential exception and prints an error message, but we want to add a loop that executes while the user has not given us a legal file name. We want it to look something like this:

Scanner input; while (user hasn't given a legal name) { <prompt for name> <try to open file, generating error message if illegal> } We have a classic problem of how to prime this loop so that it enters the first time through. We're trying to construct a Scanner from a file. When we succeed, we'll be giving a value to the variable "input". Can we initialize input to something that would indicate that we aren't yet done? The answer is yes. There is a special keyword in Java called "null" that is used to represent "no object". We can initialize the variable input to null as a way to say, "This variable doesn't yet point to an actual object." The primary advantage of initializing the variable to null is that we can test whether it's null in the while loop.

Scanner input = null; while (input == null) { <prompt for name> <try to open file, generating error message if illegal> } We start the variable with the value null, so it enters the while loop the first time through. If the code in the try/catch fails to properly open the file, then the variable will still be null and we'll execute the loop a second time, prompting for another file name and trying to open it. If the code in the try/catch fails again, then we generate yet another error message and go through the loop a third time.

We can combine this pseudocode with the try/catch code we saw earlier. It seems prudent to modify the error message to make it clear that the user is being given another chance to enter a legal file name.

        Scanner input = null;
        while (input == null) {
            System.out.print("What is the name of the input file? ");
            String name = console.nextLine();
            try {
                input = new Scanner(new File(name));
            } catch (FileNotFoundException e) {
                System.out.println("File not found.  Please try again.");
            }
        }
This loop executes repeatedly until the call on "new Scanner" inside the try block succeeds and gives the variable input a non-null value. This code could be included in method main, although we'd have to construct a Scanner for console input to be able to prompt the user for a file name. Below is a variation of the HoursWorked program that prompts for a file name.

        // This program reads an input file of hours worked by various employees.  Each
        // line of the input file should have an employee's name (without any spaces)
        // followed by a list of hours worked, as in:
        //
        //     Erica 7.5 8.5 10.25 8
        //     Greenlee 10.5 11.5 12 11
        //     Ryan 6.5 8 9.25 8
        //
        // The program reports the total hours worked by each employee.
        
        import java.io.*;
        import java.util.*;
        
        public class HoursWorked2 {
            public static void main(String[] args) throws FileNotFoundException {
                Scanner input = new Scanner(new File("hours.dat"));
                while (input.hasNextLine()) {
                    String text = input.nextLine();
                    Scanner data = new Scanner(text);
                    process(data);
                }
            }
            
            // Processes data from the given Scanner (name and list of hours worked)
            public static void process(Scanner input) {
                String name = input.next();
                double sum = 0.0;
                while (input.hasNextDouble()) {
                    sum += input.nextDouble();
                }
                System.out.println("Total hours worked by " + name + " = " + sum);
            }
        }
Notice that we no longer need the "throws FileNotFoundException" in the header for main because we handle the potential exception. Here is a log of execution showing what happens when the user types in illegal file names:

        What is the name of the input file? ours.dat
        File not found.  Please try again.
        What is the name of the input file? hours.txt
        File not found.  Please try again.
        What is the name of the input file? data.dat
        File not found.  Please try again.
        What is the name of the input file? file.dat
        File not found.  Please try again.
        What is the name of the input file? hours.dat
        Total hours worked by Erica = 42.75
        Total hours worked by Greenlee = 55.75
        Total hours worked by Simone = 24.0
        Total hours worked by Ryan = 31.75
        Total hours worked by Kendall = 5.5
This code for opening a file is complicated enough that you might want to put it in its own static method. Below is a final variation that includes a method called getInput that prompts the user for a legal file name that can be used to construct a Scanner.

        import java.io.*;
        import java.util.*;
        
        public class HoursWorked3 {
            public static void main(String[] args) {
                Scanner console = new Scanner(System.in);
                Scanner input = null;
                while (input == null) {
                    System.out.print("What is the name of the input file? ");
                    String name = console.nextLine();
                    try {
                        input = new Scanner(new File(name));
                    } catch (FileNotFoundException e) {
                        System.out.println("File not found.  Please try again.");
                    }
                }
                while (input.hasNextLine()) {
                    String text = input.nextLine();
                    Scanner data = new Scanner(text);
                    process(data);
                }
            }
            
            // Processes data from the given Scanner (name and list of hours worked)
            public static void process(Scanner input) {
                String name = input.next();
                double sum = 0.0;
                while (input.hasNextDouble()) {
                    sum += input.nextDouble();
                }
                System.out.println("Total hours worked by " + name + " = " + sum);
            }
        }
The code we have written for opening a file tends to be fairly standard in that we could use it without modification in many programs. We refer to this as "boilerplate" code.
Boilerplate Code

Code that tends to be the same from one program to another.

The method getInput is a good example of the kind of boilerplate code that you might use in many different file-processing programs.

6.7 Chapter Summary

6.8 Self-Check Exercises

  1. What is wrong with the following line of code? Scanner input = new Scanner(new File("C:\temp\new files\test.dat"));

    (Hint: Try printing the above String.)

  2. If your Java program is located on a Windows machine in the folder C:\Documents and Settings\amanda\My Documents\programs, answer the following:
  3. If your Java program is located on a Linux machine in the folder /home/amanda/Documents/hw6, answer the following:
  4. For the next several questions consider a file named readme.txt that contains the following contents:
            6.7        This file has
                    several input lines.
            
              10 20         30   40
            
            test

    What would be the output from the following code when run on the above readme.txt file?

            Scanner input = new Scanner(new File("readme.txt"));
            int count = 0;
            while (input.hasNextLine()) {
                System.out.println("input: " + input.nextLine());
                count++;
            }
            System.out.println(count + " total");
    
  5. What would be the output from the code in the previous exercise if the calls to hasNextLine and nextLine are replaced by calls to hasNext and next, respectively?
  6. What would be the output from the code in the previous exercise if the calls to hasNextLine and nextLine are replaced by calls to hasNextInt and nextInt, respectively? How about hasNextDouble and nextDouble?
  7. Write a program that prints itself to the console as output. That is, if the program is stored in Example.java, it will open the file Example.java and print its contents.
  8. Write a static method named getFileName that repeatedly prompts the user for a file name until the user types the name of a file that exists on the system, then returns that file name as a String.
  9. Write a static method named printEntireFile that accepts a String for a file name as its parameter, and prints the contents of that file to the console as output. Assume that the file exists.
  10. Write a static method named doubleSpace that accepts two Strings representing file names as its parameters, writing into the second file a double-spaced version of the text in the first file. You can achieve this by inserting a blank line between each line of output.

    To make it more challenging, try to make your code so that it would work even if the two Strings were the same (in other words, to write the double-spaced output back into the same file).

  11. Write a static method named wordWrap that accepts a String representing a file name as its parameter and outputs each line of the file to the console, word-wrapping all lines that are longer than 60 characters. For example, if a line contains 112 characters, replace it by two lines: one containing the first 60 characters and another containing the final 52 characters. A line containing 217 characters would be wrapped into four lines: three of length 60 and a final line of length 37.
  12. Modify the preceding word-wrap method so that it outputs the newly-wrapped text back into the original file. (Be careful -- don't output into a file while you are reading it!) Also, modify it to use a class constant for the maximum line length rather than hard-coding 60.
  13. Modify the preceding word-wrap method so that it only wraps whole words, never chopping a word in half. Assume that a word is any whitespace-separated token and that all words are under 60 characters in length.
  14. Write a program that prompts the user for a file name, then reads that file (assuming that its contents consist entirely of integers) and prints the maximum, minimum, sum, count (number of integers in the file), and average of the numbers. For example, if the file numberinput.dat has the following contents:
            4 -2 18
            15 31
            
            27

    Your program would produce the following output:

            What is the name of the input file? numberinput.dat
            Maximum = 31
            Minimum = -2
            Sum = 93
            Count = 6
            Average = 15.5
  15. Modify the previous program so that it works even on an input file that contains non-integer tokens. Your code should skip over any tokens that are not valid integers. For example, if the file numberinput2.dat has the following contents:
            4   billy bob   -2 18  2.54
            15 31 NotANumber
            
            true  'c' 27

    Your program would produce the same output as in the previous exercise.

  16. Write a static method named collapseSpaces that accepts a String representing a file name as its parameter, then reads that file and outputs it with all its tokens separated by a single space. Your code will collapse sequences of multiple spaces into a single space. That is, if a line of the file contains the following text:
       many   spaces   on  this      line!

    The same line of the file would be output as follows:

    many spaces on this line!
  17. Write a static method named readEntireFile that accepts a String representing a file name as its parameter, then reads that file and returns the entire text contents of that file as a String.
  18. Write a static method named stripHtmlTags that accepts a String representing a file name as its parameter, then reads that file, assuming that the file contains an HTML web page, and prints the file's text with all HTML tags removed. A tag is any text between < and > characters. For example, if the file contains the following text: <html> <head> <title>My web page</title> </head> <body> <p>There are many pictures of my cat here, as well as my <b>very cool</b> blog page, which contains <font color="red">awesome</font> stuff about my trip to Vegas. <p>Here's my cat now: <img src="cat.jpg"> </body> </html>

    Your program should output the following text:

    My web page There are many pictures of my cat here, as well as my very cool blog page, which contains awesome stuff about my trip to Vegas. Here's my cat now:

    You may assume that the file is a well-formed HTML document and that no tag contains a < or > character inside itself.

  19. Write a static method named stripComments that accepts a String representing a file name as its parameter, then reads that file, assuming that the file contains a Java program, and prints the file's text with all comments removed. A tag is any text on a line from // to the end of the line, and any text between /* and */ characters. For example, if the file contains the following text:
            import java.util.*;
    
            /* My program
            by Suzy Student */
            public class Program {
                public static void main(String[] args) {
    
                    System.out.println("Hello, world!");  // prints a message
                }
    
                public static /* Hello there */ void foo() {  // comment here
                    System.out.println("Goodbye!");
                } /* */
            }
    

    Your program should output the following text:

            import java.util.*;
    
    
            public class Program {
                public static void main(String[] args) {
    
                    System.out.println("Hello, world!");
                }
    
                public static  void foo() {
                    System.out.println("Goodbye!");
                }
            }
    

6.9 Programming Problems

  1. Students are often told that their term papers should have a certain number of words in them. Counting words in a long paper is a tedious task, but the computer can help. Write a program that counts the number of words in a paper assuming that consecutive words are separated either by spaces or end-of-line characters. You could then extend the program to count not just the number of words, but the number of lines and the total number of characters in the file.

  2. Write a program that takes as input lines of text like:

            This is some
            text here.
    
    and produces as output the same text inside a box, as in:

            +--------------+
            | This is some |
            | text here.   |
            +--------------+
    
    Your program will have to assume some maximum line length (e.g., 12 above).

  3. Write a program that compares two files and prints information about the differences between them. For example, if a file data1.txt exists with the following contents:
            This file has a great deal of
            text in it which needs to
    
            be processed.
    
    and another file data2.txt exists with the following contents:
            This file has a grate deal of
            text in it which needs to
    
            bee procesed.
    
    Then a dialogue of the user running your program might look like this: Enter a first file name: data1.txt Enter a second file name: data2.txt Differences found: Line 1: < This file has a great deal of > This file has a grate deal of Line 4: < be processed. > bee procesed.
  4. Write a program that prompts the user for a file name, assuming that the file contains a Java program. Your program should read the file and print its contents properly indented. When you see a { left brace character in the file, increase your indentation level by four spaces. When you see a } right brace character, decrease your indentation level by four spaces. You may assume that the file has only one opening or closing brace per line, that every block statement such as if or for uses braces rather than omitting them, and that every relevant occurrence of a { or } character in the file occurs at the end of a line.

    If the file BadlyIndented.java contains the following contents:

            // This file is poorly indented!
               public class BadlyIndented {
            public static void main(String[] args) {
              System.out.println("Hello, world!");
              myMethod();
              }
              
                     public static void myMethod() {
                     System.out.println("How ugly I am!");   
                      if (1 + 1 == 3) {
                System.out.println("the sky is falling.");
                      }
                               else {
                             System.out.println("Okay.");
               }
                  }
             }

    Your program should produce the following output:

            What is the name of the input file? BadlyIndented.java
            
            // This file is poorly indented!
            public class BadlyIndented {
                public static void main(String[] args) {
                    System.out.println("Hello, world!");
                    myMethod();
                }
            
                public static void myMethod() {
                    System.out.println("How ugly I am!");
                    if (1 + 1 == 3) {
                        System.out.println("the sky is falling.");
                    }
                    else {
                        System.out.println("Okay.");
                    }
                }
            }

    Consider using a class constant for the number of spaces to indent (4), so that it could be easily changed later.


Stuart Reges
Marty Stepp
Last modified: Sun Nov 6 13:15:41 PST 2005

Chapter 7
Arrays

Copyright © 2005 by Stuart Reges and Marty Stepp

7.1 Introduction

The sequential nature of files severely limits the number of interesting things you can easily do with them. The algorithms you have examined so far have all been sequential algorithms: algorithms that can be performed by examining each data item once in sequence. There is an entirely different class of algorithms that can be performed when you have random access of the data, when you can access the data items as many times as you like and in whatever order you like.

This chapter examines a new object that provides this random access--the array. The discussion of array applications begins with some sequential algorithms that are enhanced by arrays.

7.2 Arrays

Arrays, like files, are structured objects that have individual components. The array structure, however, allows random access. You can access any component at any time, no matter what you have accessed already.

An array is a collection of data values that have the same type. These different components are indexed by integers. The indexes differentiate the different components. This is similar to the way post office boxes are set up. Each post office has a series of boxes that all store the same thing: mail. The boxes are indexed with numbers so that you can refer to an individual box by using a description like "post office box 884."

You might, for example, have a series of temperature readings that you want to store. You could keep them in a series of different variables:

double temperature1; double temperature2; double temperature3; But you can simplify this scheme by using an array:

double[] temperature = new double[3]; This declares a variable called temperature of type "double[]" (an array of doubles). Arrays are objects, so they need to be constructed by a call on "new". The right-hand side of this declaration tells Java to construct an array of doubles that is 3 long. They are all initialized to 0.0:


                                    +-------+
                                [0] |  0.0  |
                    +---+           +-------+
        temperature | +-+--->   [1] |  0.0  |
                    +---+           +-------+
                                [2] |  0.0  |
                                    +-------+
As the picture above indicates, the variable temperature is not itself the array. Instead, it stores a reference to the array. Notice that the lowest index of the array is 0. This is a convention that goes back to the C programming language and is referred to as "0 based indexing".

Once declared, we can refer to individual elements of this array using the square bracket notation:

temperature[0] = 75.2; temperature[1] = 68.4; temperature[2] = 70.3; which would modify the array to have the following values:

                                    +-------+
                                [0] |  75.2 |
                    +---+           +-------+
        temperature | +-+--->   [1] |  68.4 |
                    +---+           +-------+
                                [2] |  70.3 |
                                    +-------+
As noted above, the type of this variable is "double[]". In general, you can add empty square brackets after any Java type to form a new type (an array of those type of values). For example, the following are legal types:

        int
        double
        char
        String
Which means that the following are legal types:

        int[]: an array of ints
        double[]: an array of double values
        char[]: an array of characters
        String[]: an array of Strings
In fact, you can apply this idea indefinitely to form what are known as multidimensional arrays:

        int: one int
        int[]: a one-dimensional array of ints
        int[][]: a 2-dimensional grid of ints
        int[][][]: a 3-dimensional collection of ints
        ...
Arrays are always indexed using integers with a starting index of 0, but when constructed, you specify how many elements you would like in this particular array. For example, to have a list of 100 temperatures, we could have instead declared our variable as follows.

double[] temperature = new double[100]; Java always allocates a contiguous block of memory for each array, which is what allows you to quickly access any element. If you executed the declaration above and put some values into the components of temperature, it would look something like this.

                             +-------+
                        [0]  |  74.3 |
                             +-------+
                        [1]  |  85.2 |
                             +-------+
                        [2]  | 102.3 |
            +---+            +-------+
temperature | +-+--->   [3]  |  99.8 |
            +---+            +-------+
                        [4]  | 101.7 |
                             +-------+
                      [...]  |  ...  |
                             +-------+
                       [99]  |  84.6 |
                             +-------+
Sometimes we draw the array sideways:

                         [0]     [1]     [2]     [3]     [4]    [...]    [99]
            +---+     +-------+-------+-------+-------+-------+-------+-------+
temperature | +-+---> |  74.3 |  85.2 | 102.3 |  99.8 | 101.7 |  ...  |  84.6 |
            +---+     +-------+-------+-------+-------+-------+-------+-------+
The key point is that this represents a contiguous block of memory that can be indexed using a specific array index. For example, temperature[3] refers to the value with index 3, which, because of zero-based indexing, turns out to be the fourth value in the array (in the picture above, the box with the value 99.8 in it).

You are not restricted to simple literal values inside of the brackets. You can use any integer expression. This allows you to combine arrays with loops, which greatly simplifies the code you write. For example, suppose we want to find the average of the values in our array. We could do so by finding the sum and dividing by the length.

double sum = 0.0; sum += temperature[0]; sum += temperature[1]; sum += temperature[2]; sum += temperature[3]; ... sum += temperature[99]; double average = sum/100; Obviously this code is very tedious and redundant. The only thing that changes in the various summing statements is the specific index we are accessing. We can use a for loop to capture this redundancy:


double sum = 0.0; for (int i = 0; i < 100; i++) { sum += temperature[i]; } double average = sum/100; This is a very concise way to access all elements of the array. The code works when the array has a length of 100, but you can imagine the array having a different length. We can improve this code by referring to the length field of the array. Every array keeps track of its length and we can ask for it by putting .length after the name of an array variable (temperature.length in our example). Notice that this is similar to how we ask a String for its length, although there we have to include parentheses as in str.length(). This is one of those unfortunate inconsistencies in Java that you just have to remember.

Using the length field, we can improve the code above as follows.

double sum = 0.0; for (int i = 0; i < temperature.length; i++) { sum += temperature[i]; } double average = sum/temperature.length; This code provides a template that you will see often with array processing code: a for loop that starts at 0 and that continues while the loop variable is less than the length of the array, doing something with [i] in the body of the loop.

It is possible to refer to an illegal index of an array, in which case Java throws an exception. For example, if we were to accidentally use <= in the for loop above instead of <, then the loop would attempt to access temperature[100], which is not a legal element of our array. If your code makes such an illegal reference, Java will halt your program with an ArrayIndexOutOfBoundsException.

7.2.1 Program Example--Tally

Suppose you have an input file of integers between 0 and 10 and you want to compute how many of each number are in the input file. To do so, you must first recognize that you are doing eleven separate tallies. You are tallying the occurrences of the number 0, the number 1, the number 2, the number 3, and so on up to and including the number 10. You will more easily solve the overall task if you first solve the task of performing one tally. To tally the occurrences of the number 0, you simply count how often they occur in the input file.

        set count to 0.
        while (more numbers to read) {
            read a number.
            if (the number is 0) {
                increment total.
            }
        }
        report the value of count.
To solve the task of tallying all eleven numbers, you can use an array with one component for each of the numbers you are tallying. Because the simple tally involves a single int variable, you will want your array to have int components. That way you will have a different int variable for each of the eleven tallying tasks. Here is the declaration you would use for the array.

int[] count = new int[11]; Here is what count looks like initially:

                   [0]     [1]     [2]     [3]     [4]    [...]    [10]
      +---+     +-------+-------+-------+-------+-------+-------+-------+
count | +-+---> |   0   |   0   |   0   |   0   |   0   |  ...  |   0   |
      +---+     +-------+-------+-------+-------+-------+-------+-------+
You could solve the problem by processing the file once looking for 0's, then on a second pass looking for 1's, and then a third pass looking for 2's, and so on. This is highly inefficient, however. It is more efficient to do all the tallying operations in parallel. Thus, our pseudocode becomes:

        set all counts to 0.
        while (more numbers to read) {
            read a number.
            increment the count for the component corresponding to number.
        }
        report the value of each count.
How do we increment the appropriate component of total depending on the number read in? We use the number read in to index the component we are interested in. Thus, instead of saying

        increment the count for the component corresponding to number.
You can approach Java more by saying

        increment count[number].
One last point you should consider is what happens if number is out of range. What if the file contains a number that is less than 0 or greater than 10? You would obtain an array index out of bounds. There are many ways to account for this problem. One simple solution is to keep track of how many illegal values appear. Thus, the line above is replaced by:

        if (number is in range) {
            increment count[number].
        } else {
            increment number of illegal values.
        }
This requires introducing a new variable that tallies the number of illegal values. You would initialize the variable to 0 before the loop.


        set all counts to 0.
        set illegal count to 0.
        while (more numbers to read) {
            read a number.
            if (number is in range) {
                increment count[number].
            } else {
                increment number of illegal values.
            }
        }
        report the value of each count.
        report the number of illegal values.
This now can be fairly easily translated into Java. The program introduces a class constant for the maximum value to be tallied.

        // This program reads a series of values between 0 and MAX from the user and
        // reports the frequency of occurrence of each value.
        
        import java.io.*;
        import java.util.*;
        
        public class Tally {
            public static final int MAX = 10;  // maximum legal value
            
            public static void main(String[] args) throws FileNotFoundException {
                Scanner input = new Scanner(new File("tally.dat"));
                tally(input);
            }
            
            public static void tally(Scanner input) {
                int[] count = new int[MAX + 1];
                int numBad = 0;
                
                while (input.hasNextInt()) {
                    int next = input.nextInt();
                    if (next >= 0 && next <= MAX) {
                        count[next]++;
                    } else {
                        numBad++;
                    }
                }
                
                System.out.println("Value\tOccurrences");
                for (int i = 0; i < count.length; i++) {
                    System.out.println(i + "\t" + count[i]);
                }
                System.out.println("Other\t" + numBad);
            }
        }
Given an input file like the following.

        2 8 0 7 4 12 0 2 1 8 7 1 7 4 4 6 79 5 8 5 1 2 7 4 3 2 4
        2 7 0 4 5 4 10 2 0 275 93 1 5 4 5 6 8 1 2 6 -999 9 5 7
        0 64 3 4 2 3 10 2 4 9 0 5 3 -36 9 1 0 6 2 2 1 6 6 1 3 8 6
The program would produce the following output.


        Value   Occurrences
        0       7
        1       8
        2       11
        3       5
        4       10
        5       7
        6       7
        7       6
        8       5
        9       3
        10      2
        Other   7

7.2.2 Shifting Values in an Array

We often want to move a series of values in an array. For example, suppose we have an array of integers that stores the sequence of values (3, 8, 9, 7, 5) and we want to rotate the values so that the value at the front of the list goes to the back and the order of the other values stays the same. In other words, we want to move the 3 to the back, yielding the list (8, 9, 7, 5, 3). Let's explore how to write code to perform that action.

Suppose that we have a variable of type int[] called list of length 5 that stores the values (3, 8, 9, 7, 5):

                     [0]   [1]   [2]   [3]   [4]
         +---+     +-----+-----+-----+-----+-----+
    list | +-+---> |  3  |  8  |  9  |  7  |  5  |
         +---+     +-----+-----+-----+-----+-----+
The 3 at the front of the list is supposed to go to the back of the list and the other values are supposed to rotate. We can make the task easier by storing the value at the front of the list (the 3 in our example) into a local variable:

        int first = list[0];
With that value safely tucked away, what we have left to do is to shift the other four values left by one position:

                     [0]   [1]   [2]   [3]   [4]
         +---+     +-----+-----+-----+-----+-----+
    list | +-+---> |  3  |  8  |  9  |  7  |  5  |
         +---+     +-----+-----+-----+-----+-----+
                          /     /     /     /
                         /     /     /     /
                        /     /     /     /
                       V     V     V     V
         +---+     +-----+-----+-----+-----+-----+
    list | +-+---> |  8  |  9  |  7  |  5  |  5  |
         +---+     +-----+-----+-----+-----+-----+
The overall task breaks down into four different shifting operations, each of which is a simple assignment statement:

        list[0] = list[1];
        list[1] = list[2];
        list[2] = list[3];
        list[3] = list[4];
Obviously we'd want to write this as a loop rather than writing a series of individual assignment statements. Each of the statements above is of the form:

        list[i] = list[i + 1];
We replace list element [i] with the value currently stored in list element [i + 1], which shifts that value to the left. We want this inside of a for loop. Many array operations that involve accessing each element of the array can be written using a for loop that looks like this:

        for (int i = 0; i < list.length; i++) {
            do something with list[i]
        }
That's not a bad starting point for this operation. So we can use the assignment statement we came up with inside of this for loop body:

        for (int i = 0; i < list.length; i++) {
            list[i] = list[i + 1];
        }
This is almost the right answer, but it has a classic problem known as an "off by one bug". This loop will execute 5 times for our sample array, but we want to shift just four different values. We want to do this for i equal to 0, 1, 2 and 3, but not for i equal to 4. So this loop goes one too many times. On the last iteration of the loop when i is equal to 4 we execute this line of code:

            list[i] = list[i + 1];
which becomes:

            list[4] = list[5];
There is no value list[5] because the array has only 5 elements with indexes 0 through 4. So this code generates an ArrayIndexOutOfBoundsException. To fix the problem, we alter the loop so that it stops one early:

        for (int i = 0; i < list.length - 1; i++) {
            list[i] = list[i + 1];
        }
Notice that in place of the usual list.length we use (list.length - 1). That causes the loop to stop one early. You can think of the minus one in this expression as offsetting the plus one in the assignment statement.

Of course, there is one detail left to deal with. After shifting values to the left, we have made room at the end of the list for the value that used to be at the front of the list. Recall that we stored it in a local variable called first. But we have to actually place it there after the loop executes:

        list[list.length - 1] = first;
Putting all of this together into a static method we get:

        public static void rotateLeft(int[] list) {
            int first = list[0];
            for (int i = 0; i < list.length - 1; i++) {
                list[i] = list[i + 1];
            }
            list[list.length - 1] = first;
        }
Notice that this is written as a void method. We don't need to return the array even though we have changed it. Because arrays are objects, the method is passed a reference to the location of the array. In other words, it knows where the array lives. Thus, any changes that are made by the method are changing the array object itself.

An interesting variation is to rotate the values to the right instead of rotating to the left, which is the inverse operation. So in this case, we want to take the value that is currently at the end of the list and bring it to the front. So if a variable called list stores the values (3, 8, 9, 7, 5) beforehand, then it should bring the 5 to the front and store the values (5, 3, 8, 9, 7).

We can again begin by tucking away the value that is being rotated into a temporary variable:

        int last = list[list.length - 1];
Then we want to shift values to the right:

                     [0]   [1]   [2]   [3]   [4]
         +---+     +-----+-----+-----+-----+-----+
    list | +-+---> |  3  |  8  |  9  |  7  |  5  |
         +---+     +-----+-----+-----+-----+-----+
                        \     \     \     \
                         \     \     \     \
                          \     \     \     \
                           V     V     V     V
         +---+     +-----+-----+-----+-----+-----+
    list | +-+---> |  3  |  3  |  8  |  9  |  7  |
         +---+     +-----+-----+-----+-----+-----+
In this case the four individual assignment statements would be:

        list[1] = list[0];
        list[2] = list[1];
        list[3] = list[2];
        list[4] = list[3];
Or written more generally:

        list[i] = list[i - 1];
If we put this inside of the standard for loop we get:

        for (int i = 0; i < list.length; i++) {
            list[i] = list[i - 1];
        }
There are two problems with this code. First, there is another off-by-one bug. The first assignment statement we want to perform would set list[1] to be what is currently in list[0], but this loop sets list[0] to list[-1]. That generates an ArrayIndexOutOfBoundsException because there is no value list[-1]. So we don't want to start i at 0, we want to start it at 1:

        for (int i = 1; i < list.length; i++) {
            list[i] = list[i - 1];
        }
This doesn't work either. It avoids the ArrayIndexOutOfBoundsException, but think about what it does. The first time through the loop it assigns list[1] to what is in list[0]:

                     [0]   [1]   [2]   [3]   [4]
         +---+     +-----+-----+-----+-----+-----+
    list | +-+---> |  3  |  8  |  9  |  7  |  5  |
         +---+     +-----+-----+-----+-----+-----+
                        \
                         \
                          \
                           V
         +---+     +-----+-----+-----+-----+-----+
    list | +-+---> |  3  |  3  |  9  |  7  |  5  |
         +---+     +-----+-----+-----+-----+-----+
What happened to the value 8? We have overwritten it with the value 3. So the next time through the loop we set list[2] to be list[1]:

                     [0]   [1]   [2]   [3]   [4]
         +---+     +-----+-----+-----+-----+-----+
    list | +-+---> |  3  |  3  |  9  |  7  |  5  |
         +---+     +-----+-----+-----+-----+-----+
                              \
                               \
                                \
                                 V
         +---+     +-----+-----+-----+-----+-----+
    list | +-+---> |  3  |  3  |  3  |  7  |  5  |
         +---+     +-----+-----+-----+-----+-----+
You might say, "Wait a minute...list[1] isn't a 3, it's an 8." It was an 8 when we started, but the first iteration of the loop replaced the 8 with a 3. And now we've copied that 3 into the spot where 9 used to be. So the next time through the loop we set list[3] to be what is in list[2]:

                     [0]   [1]   [2]   [3]   [4]
         +---+     +-----+-----+-----+-----+-----+
    list | +-+---> |  3  |  3  |  3  |  7  |  5  |
         +---+     +-----+-----+-----+-----+-----+
                                    \
                                     \
                                      \
                                       V
         +---+     +-----+-----+-----+-----+-----+
    list | +-+---> |  3  |  3  |  3  |  3  |  5  |
         +---+     +-----+-----+-----+-----+-----+
This overwrites the 7 with a 3. The final time through the loop we set list[4] to be what is currently in list[3]:

                     [0]   [1]   [2]   [3]   [4]
         +---+     +-----+-----+-----+-----+-----+
    list | +-+---> |  3  |  3  |  3  |  3  |  5  |
         +---+     +-----+-----+-----+-----+-----+
                                          \
                                           \
                                            \
                                             V
         +---+     +-----+-----+-----+-----+-----+
    list | +-+---> |  3  |  3  |  3  |  3  |  3  |
         +---+     +-----+-----+-----+-----+-----+
So this loop ends up putting 3 into every cell of the array. Obviously that is not what we want. To make this code work, we have to run the loop in reverse order (from right-to-left instead of left-to-right). So let's back up to where we started:

                     [0]   [1]   [2]   [3]   [4]
         +---+     +-----+-----+-----+-----+-----+
    list | +-+---> |  3  |  8  |  9  |  7  |  5  |
         +---+     +-----+-----+-----+-----+-----+
We tucked away the final value of the list into a local variable. That frees up the final array position. So we start by first assigning list[4] to be what is in list[3]:

                     [0]   [1]   [2]   [3]   [4]
         +---+     +-----+-----+-----+-----+-----+
    list | +-+---> |  3  |  8  |  9  |  7  |  5  |
         +---+     +-----+-----+-----+-----+-----+
                                          \
                                           \
                                            \
                                             V
         +---+     +-----+-----+-----+-----+-----+
    list | +-+---> |  3  |  8  |  9  |  7  |  7  |
         +---+     +-----+-----+-----+-----+-----+
This wipes out the 5 that was a the end of the list, but we have that value tucked away in a local variable. And once we have performed this assignment statement, we free up list[3], which means now we can set list[3] to be what is currently in list[2]:

                     [0]   [1]   [2]   [3]   [4]
         +---+     +-----+-----+-----+-----+-----+
    list | +-+---> |  3  |  8  |  9  |  7  |  7  |
         +---+     +-----+-----+-----+-----+-----+
                                    \
                                     \
                                      \
                                       V
         +---+     +-----+-----+-----+-----+-----+
    list | +-+---> |  3  |  8  |  9  |  9  |  7  |
         +---+     +-----+-----+-----+-----+-----+
Now list[2] is free, so we can set list[2] to be what is currently in list[1]:


                     [0]   [1]   [2]   [3]   [4]
         +---+     +-----+-----+-----+-----+-----+
    list | +-+---> |  3  |  8  |  9  |  9  |  7  |
         +---+     +-----+-----+-----+-----+-----+
                              \
                               \
                                \
                                 V
         +---+     +-----+-----+-----+-----+-----+
    list | +-+---> |  3  |  8  |  8  |  9  |  7  |
         +---+     +-----+-----+-----+-----+-----+
And we end by setting list[1] to be what is currently in list[0]:

                     [0]   [1]   [2]   [3]   [4]
         +---+     +-----+-----+-----+-----+-----+
    list | +-+---> |  3  |  8  |  8  |  9  |  7  |
         +---+     +-----+-----+-----+-----+-----+
                        \
                         \
                          \
                           V
         +---+     +-----+-----+-----+-----+-----+
    list | +-+---> |  3  |  3  |  8  |  9  |  7  |
         +---+     +-----+-----+-----+-----+-----+
At this point, the only thing left to do is to put the 5 that we tucked away into a local variable at the front of the list and we're done:

         +---+     +-----+-----+-----+-----+-----+
    list | +-+---> |  5  |  3  |  8  |  9  |  7  |
         +---+     +-----+-----+-----+-----+-----+
We can reverse the for loop by changing the i++ to i-- and adjusting the initialization and test. Putting all of this together, we get the following method:

        public static void rotateRight(int[] list) {
            int last = list[list.length - 1];
            for (int i = list.length - 1; i > 0; i--) {
                list[i] = list[i - 1];
            }
            list[0] = last;
        }

7.3 Variable length data

So far we have discussed an array that is in some sense full: all of its elements were in use. This is not always the case. Often we build an array with a maximum in mind, but only fill it up partially.

Suppose we want to use an array to process an input file of integers where the various input lines have different lengths. Some lines might have many integers on them and some might have very few. Because the lines differ in length, we don't know how big to make the array. Therefore, the best we can do is to set some arbitrary maximum for the length of a line and make the array that large.

Suppose that we assume that no input line has more than 30 integers on it. Then we can define an array of length 30 as follows:

int[] numbers = new int[30]; To solve this problem we define not only an array for the integers, but also an integer variable to keep track of how many elements of the array are currently being used. We can call this variable "length". If we were to read the following input line:

        18 203 14 9 8 7
We would expect the variables numbers and length to look like this.

                          [0]   [1]   [2]   [3]   [4]   [5]   [6]  [...]  [29]
            +---+       +-----+-----+-----+-----+-----+-----+-----+-----+-----+
    numbers | +-+--->   |  18 | 203 |  14 |  9  |  8  |  7  |  0  | ... |  0  |
            +---+       +-----+-----+-----+-----+-----+-----+-----+-----+-----+

            +---+
    length  | 6 |
            +---+
The integers from the input line appear in the first six cells of this array. Because six cells of the array are in use, we store the value 6 in the memory cell for length. The elements 6 through 29 are 0 in this picture, but they could really have any value at all. By keeping track of the length, we can be sure to use only elements of the array that are currently active.

Let's examine a program that uses this data structure. Consider the problem of reversing the integers on each line of a file. Why does this problem require an extra structure? With a file you can read the numbers from a line in sequential order. You can't, however, read them backwards. An array has no such limitations. To reverse the line you read it into an array and then manipulate the array. You should think of this as a transfer of information. The numbers from the input line are transferred from the file structure to the array structure. Once transferred, you can manipulate the numbers using the array and not worry about the file until it is time to process another line. Thus, your pseudocode description would be:

        while (more lines to process) {
            read next input line.
            transfer line from file to array.
            write array contents in reverse order.
        }
Let's assume that as in the program examples in the last chapter, we will read each line of the file into a String and use that String to construct a Scanner for that particular input line. To transfer numbers from the Scanner for a particular input line into the array, we'd do the following.

        while (more numbers to read) {
            read a number into the next array component.
        }
To improve this pseudocode we need to specify what we mean by the next array component. Because we want to store the first number in component [0], the next in component [1], and so on, we need an integer variable to keep track of this. We also need to keep track of the length. Fortunately, we can easily combine these two tasks.

        set length to 0.
        while (more numbers to read) {
            read a number into numbers[length].
            increment length.
        }
Some novices reverse the order of the two lines inside the while loop and that makes a certain amount of sense. It seems like you'd want to increase your length first and then store the value in the array. But remember that arrays use zero-based indexing. That means that length is always one ahead of where you last stored something. In other words:

        store the first value in position [0] and remember a length of 1
        store the second value in position [1] and remember a length of 2
        store the third value in position [2] and remember a length of 3
        store the fourth value in position [3] and remember a length of 4
        ...
As a result, it makes sense to store first and then increment length rather than the other way around.

Writing the contents of the array in reverse order is a simple operation. We run our loop backwards using i-- instead of i++. Below is a complete program that puts all of this together.


        // This program takes an input file of numbers and prints the numbers with
        // each line reversed in order.
        
        import java.io.*;
        import java.util.*;
        
        public class ReverseLines {
            
            public static final int MAX_NUMBERS = 30;  // max # of numbers on a line
            
            public static void main(String[] args) throws FileNotFoundException {
                Scanner input = new Scanner(new File("reverse.dat"));
                processFile(input);
            }
            
            // processes the file line by line, reversing each line
            public static void processFile(Scanner input) {
                while (input.hasNextLine()) {
                    Scanner data = new Scanner(input.nextLine());
                    reverseLine(data);
                }
            }
            
            // reads the numbers in the given Scanner, writing them in reverse order
            public static void reverseLine(Scanner data) {
                int[] numbers = new int[MAX_NUMBERS];
                int length = 0;
                
                while (data.hasNextInt()) {
                    numbers[length] = data.nextInt();
                    length++;
                }
                
                for (int i = length - 1; i >= 0; i--) {
                    System.out.print(numbers[i] + " ");
                }
                System.out.println();
            }
        }
Given the following input file.

        18 203 14 9 8 7
        2 3 4 5
        6 8 4
        12 14 19 23 43 23 33 34 43 23 98 76 36 57 37 28 38 54 58 39 29 39
        18 42 19 23 40 702
        83
        4 2 3 1 0 4 2
The program would produce the following output.

        7 8 9 14 203 18 
        5 4 3 2 
        4 8 6 
        39 29 39 58 54 38 28 37 57 36 76 98 23 43 34 33 23 43 23 19 14 12 
        702 40 23 19 42 18 
        83 
        2 4 0 1 3 2 4 

7.4 Limitations of arrays

You should be aware of some general limitations of arrays.

There is one distinct advantage of arrays being objects, which is that you can pass them as parameters to another method and that other method will be able to change the contents of the array.

7.5 Initializing arrays

Java has a special syntax for initializing an array when you know exactly what you want to put into it. For example, you could write the following code to initialize an array of ints and an array of Strings.

int[] numbers = new int[5]; numbers[0] = 13; numbers[1] = 23; numbers[2] = 480; numbers[3] = -18; numbers[4] = 75; String[] words = new String[6]; words[0] = "hello"; words[1] = "how"; words[2] = "are"; words[3] = "you"; words[4] = "feeling"; words[5] = "today"; This works, but it's a rather tedious way to declare these arrays. Java provides a shorthand:

int[] numbers = {13, 23, 480, -18, 75}; String[] words = {"hello", "how", "are", "you", "feeling", "today"}; You use the curly braces to enclose a series of values that will be stored in the array. Order is important. The first value will go into index 0, the second value will go into index 1 and so on. Java counts how many values you include and constructs an array of just the right size. It then stores the various values into the appropriate spots in the array.

This is one of only two places where Java will construct an object without an explicit call on new. The other place we saw this was with String literals where Java constructs String objects for you without you having to call new. Both of these are conveniences for programmers because these tasks are so common that the designers of the language wanted to make it easy to do them.

7.6 Case Study: Hours Worked

Let's look at a more complex program example that involves using arrays. Suppose that you have an input file that has data indicating how many hours an employee has worked with each line of the input file indicating the hours worked for a different week. Each week has 7 days, so there could be up to 7 numbers listed on each line. We generally consider Monday to be the start of the work week, so let's assume that each line lists hours worked on Monday followed by hours worked on Tuesday, and so on, and ending with hours worked on Sunday. But we'll allow the lines to have fewer than 7 numbers if someone worked fewer than 7 days. Below is a sample input file that we'll call "hours.txt":

        8 8 8 8 8
        8 4 8 4 8 4 4
        8 4 8 4 8
        3 0 0 8 6 4 4
        8 8
        0 0 8 8
        8 8 4 8 4
Let's write a program that reads this input file, reporting totals for each row and each column. The totals for each row will tell us how many hours the person has worked each week. The totals for each column will tell us how many hours the person worked on Mondays versus Tuesdays versus Wednesdays, and so on.

It's clear that the number 7 is likely to show up in several places in this program, so it makes sense to define a class constant that we can use instead of the magic number:

public static final int DAYS = 7; // # of days in a week Our main method can be fairly short, opening the input file we want to read and calling a method to process the file:

public static void main(String[] args) throws FileNotFoundException { Scanner input = new Scanner(new File("hours.txt")); processFile(input); } We saw in chapter 6 that for line-oriented data, we can generally use the following pattern as a starting point for file processing code:

        while (input.hasNextLine()) {
            String text = input.nextLine();
            process text.
        }
This loop allows us to process one line at a time. But each line has internal structure because there are up to 7 numbers on each line. So in this case it makes sense to construct a Scanner for each input line and to write a separate method for processing a line of data:

        while (input.hasNextLine()) {
            String text = input.nextLine();
            Scanner data = new Scanner(text);
            call method to process data.
        }
So what are we supposed to do to process a line of data? One thing we have to do is to add up the hours worked during the week and report that number. If that's all we had to do, then we could solve this as a simple file-processing problem and avoid arrays altogether. But we also have to add up columns, reporting the total hours worked on Monday, Tuesday, and so on.

This is a perfect application for an array. We are, in effect, solving seven different cumulative sum tasks. We want to sum up all hours worked on Monday, all hours worked on Tuesday, and so on. So having an array of 7 integers will allow us to calculate these column totals. So our pseudocode becomes:

        int[] total = new int[DAYS];
        while (input.hasNextLine()) {
            String text = input.nextLine();
            Scanner data = new Scanner(text);
            call method to process data.
            update total to include this week's data.
        }
        report total.
Remember that in cumulative sum we always initialize our sum to 0. When we construct an int array, Java initializes the variables to 0. So our total array will store 7 integers each with value 0:

                       [0]     [1]     [2]     [3]     [4]     [5]     [6]
          +---+     +-------+-------+-------+-------+-------+-------+-------+
    total | +-+---> |   0   |   0   |   0   |   0   |   0   |   0   |   0   |
          +---+     +-------+-------+-------+-------+-------+-------+-------+
So how do we process the data from an input line in such a way that we can report the sum across and also add the values into this total array? The first line of the sample input file is "8 8 8 8 8". There are several approaches we could take, but if we're going to be manipulating an array to keep track of the total for each column, then it makes sense to create a second array that represents the data for the current week. In effect, we transfer the data from the input file into an array. Once we have the data in array form, we can do all sorts of manipulations on it.

In our pseudocode above we indicate that we should "call a method to process data" (the Scanner that contains the next line's worth of data). So let's have that method transfer the data from the Scanner into an array. It should take the Scanner as a parameter and should return a reference to a new array that it constructs. Thus, our pseudocode becomes:


        int[] total = new int[DAYS];
        while (input.hasNextLine()) {
            String text = input.nextLine();
            Scanner data = new Scanner(text);
            int[] next = transferFrom(data);
            do any processing necessary on next.
            update total to include this week's data.
        }
        report total.
The code necessary to transfer data from the Scanner into an array is not particularly complex, but it makes sense to separate this kind of operation into another method to keep the file processing method short and easy to read. We will write the code for transferFrom shortly, but we are actually fairly close to finishing this pseudocode.

We have just three pieces left to fill in. Once we have transfered the data from an input line into a Scanner, what do we do with it? We have to report the total hours for this particular week and we have to add this week's hours into our cumulative sum for the columns. Neither of these operations is particularly complex, but we can again simplify our file-processing code by introducing methods that perform most of the details associated with these tasks. We might often find ourselves wanting to add up the numbers in an array, so it makes sense to have a method called sum that we can use to add up this row. We might also find ourselves wanting to add one array to another (the array equivalent of saying sum += next), so it also makes sense to make that a separate method.

The final element to fill in for our pseudocode is reporting the total. This is something that also can be easily put in a separate method. With these three methods in mind, we can finish translating our pseudocode into actual code:

        int[] total = new int[DAYS];
        while (input.hasNextLine()) {
            String text = input.nextLine();
            Scanner data = new Scanner(text);
            int[] next = transferFrom(data);
            System.out.println("Total hours = " + sum(next));
            addTo(total, next);
        }
        System.out.println();
        print(total);

7.6.1 The transferFrom Method

The transferFrom method is given a Scanner as a parameter and is supposed to construct a new array that has the numbers from the Scanner. Each input line has at most 7 numbers, but it might have fewer. As a result, it makes sense to use a while loop that tests whether there are more numbers left to read from the Scanner.

        construct an array of 7 integers.
        while (data.hasNextInt()) {
            process data.nextInt().
        }
In this case, processing the next integer from the input means storing it in the array. The first number should go into position 0, the second number in position 1, the third number in position 2 and so on. That means that we need some kind of integer counter that goes up by one each time through the loop.

        construct an array of 7 integers.
        start i at 0.
        while (data.hasNextInt()) {
            store data.nextInt() in position i of the array.
            increment i.
        }
This is now fairly easy to translate into actual code.

int[] result = new int[DAYS]; int i = 0; while (data.hasNextInt()) { result[i] = data.nextInt(); i++; } return result; It may seem odd to have a method return an array, but it makes perfect sense to do so in Java. Arrays are objects, so what is returned is a reference to the array. We have to be careful to specify this properly in our header. We are returning something of type int[], so the header would look like this:

public static int[] transferFrom(Scanner data)

7.6.2 The sum Method

This is the simplest of the four array-processing methods that we need to write. We simply want to add up the numbers stored in the array. This is a classic cumulative sum problem and we can use a for loop to accomplish the task.

public static int sum(int[] numbers) { int sum = 0; for (int i = 0; i < numbers.length; i++) { sum += numbers[i]; } return sum; } In the for loop test, we could have used the class constant DAYS. I have instead used the length field of the array itself because this is such a general purpose operation that we might use it for other programs.

7.6.3 The addTo Method

The idea for this method is to write the array equivalent of:

sum += next; We will be given two arrays, one with the total hours and one with the next week's hours. Let's consider where we'll be in the middle of processing the sample input file. The first three lines of input are as follows:

        8 8 8 8 8
        8 4 8 4 8 4 4
        8 4 8 4 8
Suppose we have properly processed the first two lines and have just read in the third line for processing. Then our two arrays will look like this:

                       [0]     [1]     [2]     [3]     [4]     [5]     [6]
          +---+     +-------+-------+-------+-------+-------+-------+-------+
    total | +-+---> |  16   |  12   |  16   |  12   |  16   |   4   |   4   |
          +---+     +-------+-------+-------+-------+-------+-------+-------+

                       [0]     [1]     [2]     [3]     [4]     [5]     [6]
          +---+     +-------+-------+-------+-------+-------+-------+-------+
     next | +-+---> |   8   |   4   |   8   |   4   |   8   |   0   |   0   |
          +---+     +-------+-------+-------+-------+-------+-------+-------+
It would be nice if we could just say:

total += next; Unfortunately, you can't use operations like "+" and "+=" on arrays. But those operations can be performed on simple integers and these arrays are composed of simple integers. So we basically just have to tell the computer to do 7 different "+=" operations on the individual array elements. This can be easily written with a for loop.

public static void addTo(int[] total, int[] next) { for (int i = 0; i < DAYS; i++) { total[i] += next[i]; } }

7.6.4 The print Method

The final method we have to write involves printing out the column totals (the totals for each day of the week). The basic code involves a simple for loop, as in:

for (int i = 0; i < DAYS; i++) { System.out.println(total[i]); } But we don't want to simply write out 7 lines of output each with a number on it. It would be nice to give some information about what the numbers mean. We know that total[0] represents the total hours worked on various Mondays and total[1] represents the total hours worked on Tuesdays, and so on, but somebody reading our output might not know. So it would be helpful to label the output with some information about which day each total goes with. We can do so by defining our own array of String literals:

String[] dayNames = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; Using this array, we can improve our for loop to print out the name of each day along with its total. We can also call the sum method to write out the overall total hours worked after the for loop.

public static void print(int[] total) { String[] dayNames = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; for (int i = 0; i < DAYS; i++) { System.out.println(dayNames[i] + " hours = " + total[i]); } System.out.println("Total hours = " + sum(total)); }

7.6.5 The Complete Program

Putting all the pieces together, we end up with the following complete program.

        // This program reads an input file with information about hours worked and
        // produces a list of total hours worked each week and total hours worked for
        // each day of the week.  Each line of the input file should contain
        // information for one week and should have up to 7 integers representing how
        // many hours were worked each day (Monday first, then Tuesday, through
        // Sunday).
        
        import java.io.*;
        import java.util.*;
        
        public class Hours {
            public static final int DAYS = 7;  // # of days in a week
            
            public static void main(String[] args) throws FileNotFoundException {
                Scanner input = new Scanner(new File("hours.txt"));
                processFile(input);
            }
            
            // processes an input file of data about hours worked by an employee
            public static void processFile(Scanner input) {
                int[] total = new int[DAYS];
                while (input.hasNextLine()) {
                    String text = input.nextLine();
                    Scanner data = new Scanner(text);
                    int[] next = transferFrom(data);
                    System.out.println("Total hours = " + sum(next));
                    addTo(total, next);
                }
                System.out.println();
                print(total);
            }
            
            // constructs an array of DAYS integers initialized to 0 and transfers
            // data from the given Scanner into the array in order; assumes the
            // Scanner has no more than DAYS integers
            public static int[] transferFrom(Scanner data) {
                int[] result = new int[DAYS];
                int i = 0;
                while (data.hasNextInt()) {
                    result[i] = data.nextInt();
                    i++;
                }
                return result;
            }
            
            // returns the sum of the integers in the given array
            public static int sum(int[] numbers) {
                int sum = 0;
                for (int i = 0; i < numbers.length; i++) {
                    sum += numbers[i];
                }
                return sum;
            }
            
            // adds the values in next to their corresponding entries in total 
            public static void addTo(int[] total, int[] next) {
                for (int i = 0; i < DAYS; i++) {
                    total[i] += next[i];
                }
            }
            
            // prints information about the totals including total hours for each
            // day of the week and total hours overall
            public static void print(int[] total) {
                String[] dayNames = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
                for (int i = 0; i < DAYS; i++) {
                    System.out.println(dayNames[i] + " hours = " + total[i]);
                }
                System.out.println("Total hours = " + sum(total));
            }
        }
When executed on the sample input file:

        8 8 8 8 8
        8 4 8 4 8 4 4
        8 4 8 4 8
        3 0 0 8 6 4 4
        8 8
        0 0 8 8
        8 8 4 8 4
It produces the following output:

        Total hours = 40
        Total hours = 40
        Total hours = 32
        Total hours = 25
        Total hours = 16
        Total hours = 16
        Total hours = 32
        
        Mon hours = 43
        Tue hours = 32
        Wed hours = 36
        Thu hours = 40
        Fri hours = 34
        Sat hours = 8
        Sun hours = 8
        Total hours = 201

7.7 Arrays in the Java class libraries

You will find arrays appearing throughout the Java class libraries. In fact, there is a special class called Arrays that has a collection of utility programs for manipulating arrays. One particularly handy method in the Arrays class is the method sort. For example, given the following variable declarations:

int[] numbers = {13, 23, 480, -18, 75}; String[] words = {"hello", "how", "are", "you", "feeling", "today"}; We could make the following calls: Arrays.sort(numbers); Arrays.sort(words); If you then examine the values of the arrays, you will see that the numbers appear in numerically increasing order and the words appear in alphabetically increasing order. Below is a complete program that demonstrates this. The Arrays class is part of the java.util package, so you will see that the program includes an import from java.util.

        import java.util.*;
        
        public class SortingExample {
            public static void main(String[] args) {
                int[] numbers = {13, 23, 480, -18, 75};
                String[] words = {"hello", "how", "are", "you", "feeling", "today"};
                
                System.out.println("Before sorting:");
                display(numbers, words);
                
                Arrays.sort(numbers);
                Arrays.sort(words);
                
                System.out.println("After sorting:");
                display(numbers, words);
            }
            
            public static void display(int[] numbers, String[] words) {
                for (int i = 0; i < numbers.length; i++) {
                    System.out.print(numbers[i] + " ");
                }
                System.out.println();
                
                for (int i = 0; i < words.length; i++) {
                    System.out.print(words[i] + " ");
                }
                System.out.println();
                
                System.out.println();
            }
        }
The program produces the following output:
        Before sorting:
        13 23 480 -18 75 
        hello how are you feeling today 
        
        After sorting:
        -18 13 23 75 480 
        are feeling hello how today you 
Arrays appear in many other places in the Java class libraries. For example, the String class has a method called toCharArray that will construct an array of characters that stores the individual characters of the array. There is a corresponding constructor for the String class that allows you to construct a String from a character array. As an example, the code below converts a word into a corresponding character array, then sorts the characters in the array, then recombines the characters to make a string:

public static String sorted(String s) { char[] letters = s.toCharArray(); Arrays.sort(letters); String result = new String(letters); return result; } This might seem like a strange method to define, but it can be very helpful for solving word puzzles known as anagrams. An anagram of a word is a word that has the same letters but in a different order. For example, the following words are all anagrams of each other: pears, spear, pares, reaps, spare. Using the method above, all of these words would be converted to "aeprs". So to solve an anagram, you can simply go through a dictionary, converting every word to its sorted form and see if it matches the sorted form of the anagram you are working with.

7.8 Arrays of Objects

All of the arrays we have looked at so far have stored primitive values like simple integer values, but as section 7.2 pointed out, you can have arrays of any Java type. Arrays of objects behave slightly differently because of the fact that objects are stored as references rather than as data values.

Consider, for example, the following statement:

Point[] points = new Point[3]; This declares a variable called points that refers to an array of length 3 that stores references to Point objects. The call on new constructs the array, but it doesn't construct any actual Point objects. Instead it constructs an array of length 3 each element of which can store a reference to a Point. When Java constructs the array, it initializes these array elements to a special value known as "null" that indicates that the variable does not currently point to any object.

                         [0]     [1]     [2]
            +---+     +-------+-------+-------+
     points | +-+---> |   /   |   /   |   /   |
            +---+     +-------+-------+-------+
If we want to have some actual Point objects, they have to be constructed separately with calls on new, as in:

Point[] points = new Point[3]; points[0] = new Point(3, 7); points[1] = new Point(4, 5); points[2] = new Point(6, 2); After these lines of code execute, we would have individual Point objects that the various array elements refer to:

                                [0]     [1]     [2]
                   +---+     +-------+-------+-------+
            points | +-+---> |   |   |   |   |   |   |
                   +---+     +---+---+---+---+---+---+
                                 |       |       |
                +----------------+       |       +------------------+
                |                        |                          |
                V                        V                          V
    +--------------------+    +--------------------+    +--------------------+
    |   +----+    +----+ |    |   +----+    +----+ |    |   +----+    +----+ |
    | x |  3 |  y |  7 | |    | x |  4 |  y |  5 | |    | x |  6 |  y |  2 | |
    |   +----+    +----+ |    |   +----+    +----+ |    |   +----+    +----+ |
    +--------------------+    +--------------------+    +--------------------+
Notice that we need four different calls on new because there are four objects to be constructed: the array itself and the three individual Point objects. We could also use the curly brace notation for initializing the array in which case we don't need the explicit call on new to construct the array itself:

Point[] points = {new Point(3, 7), new Point(4, 5), new Point(6, 2)}; We have seen a reference an array of objects ever since we wrote the "hello world" program of chapter 1. Whenever we define a main method, we are required to include as its parameter "String[] args", which is an array of String objects. This array is initialized by Java itself if the user provides what are known as "command line arguments" when Java is invoked. For example, normally a Java class called DoSomething would be started from the command interface by a command like this:

        java DoSomething
The user has the option to type extra arguments, as in:

        java DoSomething temperature.dat temperature.out
In this case the user has specified two extra arguments that are file names that the program should use (e.g., the names of an input and output file). If the user types these extra arguments when starting up Java, then the "String[] args" parameter to main will be initialized to an array of length 2 storing these two strings:


                      [0]     [1]
         +---+     +-------+-------+
    args | +-+---> |   |   |   |   |
         +---+     +---+---+---+---+
                       |       |
              +--------+       +-------+
              |                        |
              V                        V
    +-------------------+    +-------------------+
    | "temperature.dat" |    | "temperature.out" |
    +-------------------+    +-------------------+

7.9 ArrayLists

Arrays in general and arrays of objects in particular are so useful that Java has a special class called an ArrayList that takes care of a lot of the low-level details for you. As its name implies, an ArrayList uses an array as its underlying data structure. It can be used to store variable length data which means that, as described in section 7.3, each ArrayList object keeps track of an array along with its current length.

You build up an ArrayList by constructing it and adding values to it:

ArrayList<String> list = new ArrayList<String>(); list.add("hello"); list.add("world"); list.add("this is fun"); This code constructs an empty ArrayList and then adds three Strings to the list. There is something unusual in this declaration that we have not seen before. The ArrayList class is what is known as a "generic" class. We would describe it as ArrayList<E> where E is short for "Element type". The idea is that there can be many different kinds of ArrayList objects. If we want an ArrayList that stores String objects, we make an ArrayList<String>. If we want an ArrayList for storing Point objects, we make an ArrayList<Point>. If we want an ArrayList of Color objects, we make an ArrayList<Color>.

Generic Class

A class such as ArrayList<E> that takes a type parameter to indicate what kind of values will be used.

For an ArrayList, the <E> that comes after its name is actually part of the type. This makes for a somewhat confusing line of code for constructing the object:

ArrayList<String> list = new ArrayList<String>(); ~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~ type type But if you think in terms of the type being ArrayList<String>, then really this is equivalent to the following line of code for a class called "T":

        T list = new T();
ArrayLists can be printed with a simple println, as in:

System.out.println(list); or:

System.out.println("list = " + list); If we execute this println after executing the code above that adds three Strings to the list, we get the following output:

        list = [hello, world, this is fun]
The ArrayList class also provides methods for adding values in the middle of the list or removing values from the list. These operations preserve the order of other list elements, shifting values left or right as appropriate. To indicate positions within the list, we use the Java convention of indexing starting at 0 for the first value, 1 for the second value and so on. For example, given the list above, consider the effect of inserting a value at index 1:

list.add(1, "middle value"); System.out.println("now list = " + list); The following output is produced:

        now list = [hello, middle value, world, this is fun]
So now the list has a total of four values. Consider what happens if we now remove the value at position 0 and then remove the value at position 1:

list.remove(0); list.remove(1); System.out.println("and now list = " + list); The output is as follows:

        and now list = [middle value, this is fun]
This result is a little surprising. We asked the list to remove the value at position 0 and then to remove the value at position 1. You might imagine that this would get rid of the Strings "hello" and "middle value" since they were at positions 0 and 1, respectively, before this code was executed. But you have to remember that an ArrayList is a dynamic structure where values are moving around and shifting into new positions. The first call on remove does remove the String "hello" because it's the value currently in position 0. But once that value is removed, everything else shifts over. So the String "middle value" moves to the front (to position 0) and the String "world" shifts into position 1. So when the second call on remove is performed, Java removes "world" from the list because it is the value that is in position 1 at that point in time.

As another example, consider the following code that creates an ArrayList and stores several words in it:


ArrayList<String> words = new ArrayList<String>(); words.add("four"); words.add("score"); words.add("and"); words.add("seven"); words.add("years"); words.add("ago"); System.out.println("words = " + words); This code produces the following output:

        words = [four, score, and, seven, years, ago]
Suppose that we want to insert a String containing a single asterisk in front of each word ("*"). We expect to double the length of the String because each of these words will have an asterisk in front of it if our code works properly. Here is a first attempt that makes sense intuitively:

for (int i = 0; i < words.size(); i++) { words.add(i, "*"); } System.out.println("after loop words = " + words); Notice that to get the number of elements in the ArrayList we call a method "size". This is an example of an unfortunate inconsistency in the Java class libraries. We have seen a total of three different conventions now for the same kind of task. Suppose, for example, that you have a variable called x. Then:

It would have been much simpler if the folks at Sun had decided on a single convention and used it consistently, but unfortunately they didn't and so we just have to remember when to use which convention.

So let's get back to the for loop. It has the usual array-style for loop with an index variable i that starts at 0 and goes up by one each time. In this case it is inserting an asterisk at position i each time through the loop. The problem is that the loop never terminates. If you're patient enough you will find that it actually does terminate but only after using up all available memory.

The problem is once again the fact that the ArrayList is a dynamic structure where things move around to new positions. Let's think about this carefully to see what is going on. Initially we have this list with the String "four" in position 0:

        [four, score, and, seven, years, ago]
The first time through the loop, we insert an asterisk at position 0. This shifts the String "four" one to the right, so that it is now in position 1.

        [*, four, score, and, seven, years, ago]
Then we come around the for loop and increment i to be 1. And we insert an asterisk at position 1. But notice that the word "four" is currently at position 1, which means that this second asterisk also goes in front of the word "four", shifting it into position 2:

        [*, *, four, score, and, seven, years, ago]
We go around the loop again, incrementing i to be 2 and once again inserting an asterisk at that position, which is once again in front of the word "four":

        [*, *, *, four, score, and, seven, years, ago]
This continues indefinitely because we keep inserting asterisks in front of the first word in the list. The for loop test compares i to the size of the list, but because the list is growing, the size keeps going up. So this process continues until we exhaust all available memory.

To fix this loop, we have to realize that inserting an asterisk at position i is going to shift everything one to the right. So on the next iteration of the loop, we will want to deal with the position two to the right, not one to the right. So we can fix the loop simply by changing the update part of the for loop to add 2 to i instead of adding 1 to i:

for (int i = 0; i < words.size(); i += 2) { words.add(i, "*"); } System.out.println("after loop words = " + words); When we execute this version of the code, we get the following output:

    after loop words = [*, four, *, score, *, and, *, seven, *, years, *, ago]
The ArrayList has a method called "get" that allows you to get a value at a specific index. The return type of get will match whatever type you've used for the generic. For example, if you asked for an ArrayList<String>, then Java will return a String when you call get. Let's look at an example of this. Suppose you initialize an ArrayList as follows:

ArrayList<String> names = new ArrayList<String>(); names.add("Erica Kane"); names.add("Adam Chandler"); names.add("Maria Santos"); We can add up the lengths of these names using a loop:

        int sum = 0;
        for (int i = 0; i < names.size(); i++) {
            String s = names.get(i);
            sum += s.length();
        }
        System.out.println("Total of name lengths = " + sum);
The ArrayList class is part of the java.util package, so to include it in a program, you would have to include an import declaration.

Below is a table of some of the most common ArrayList operations. They are defined in terms of the generic type "E". A more complete list can be found in the online Java documentation.

Useful Methods of ArrayList Objects
Method Description Example
add(E value) adds value at the end of the list list.add("end");
add(int index, E value) adds value at the given index, shifting subsequent values right list.add(1, "middle");
clear() makes the list empty list.clear();
get(int index) gets the value at the given index (return type is E) list.get(1)
indexOf(E value) returns the index of the first occurrence of the given value in the list (-1 if not found) list.indexOf("hello")
remove(int index) removes the value at the given index, shifting subsequent values left list.remove(1);
set(int index, E value) replaces the value at given index with given value list.set(2, "hello");
size() returns the current number of elements in the list list.size()

 

7.10 Chapter Summary

7.11 Self-Check Exercises

  1. What expression should be used to access the first element of an array of integers named numbers? What expression should be used to access the last element of numbers, assuming it contains 10 elements? What expression can be used to access its last element, regardless of its length?
  2. Write code that creates an array of integers named data of size 5 with the following contents:
                      [0]     [1]     [2]     [3]     [4]
         +---+     +-------+-------+-------+-------+-------+
    data | +-+---> |   27  |   51  |   33  |   -1  |  101  |
         +---+     +-------+-------+-------+-------+-------+
    

    Try writing both the long version (initializing each element one at a time) and short version (initializing the entire array at once).

  3. Write code that stores all odd numbers between -6 and 38 into an array using a loop. Make the array's size exactly large enough to store the numbers.

    Try generalizing your code so that it would work for any minimum and maximum value, not just -6 and 38.

  4. What are the values of the elements in the array numbers after the following code? int[] numbers = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100}; for (int i = 0; i < 9; i++) { numbers[i] = numbers[i + 1]; }
  5. What are the values of the elements in the array numbers after the following code? int[] numbers = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100}; for (int i = 1; i < 10; i++) { numbers[i] = numbers[i - 1]; }
  6. Write code that prints each element of an array of 5 integers named data as follows:
    element [0] is 14
    element [1] is 5
    element [2] is 27
    element [3] is -3
    element [4] is 2598
    

    For added challenge, generalize your code so that it would work on an array of any size.

  7. Consider the following method mystery: public static void mystery(int[] a, int[] b) { for (int i = 0; i < a.length; i++) { a[i] += b[b.length - 1 - i]; } } What are the values of the elements in array a1 after the following code executes? int[] a1 = {1, 3, 5, 7, 9}; int[] a2 = {1, 4, 9, 16, 25}; mystery(a1, a2);
  8. Consider the following method mystery: public static void mystery2(int[] a, int[] b) { for (int i = 0; i < a.length; i++) { a[i] = a[2 * i % a.length] - b[3 * i % b.length]; } } What are the values of the elements in array a1 after the following code executes? int[] a1 = {2, 4, 6, 8, 10, 12, 14, 16}; int[] a2 = {1, 1, 2, 3, 5, 8, 13, 21}; mystery2(a1, a2);
  9. Write a method printAll that accepts an array of integers as its argument and prints the array's contents on one line. Assume that the array variable is named data and contains the following contents:
                      [0]     [1]     [2]     [3]     [4]     [5]     [6]
         +---+     +-------+-------+-------+-------+-------+-------+-------+
    data | +-+---> |   74  |   85  |  102  |   99  |  101  |   56  |   84  |
         +---+     +-------+-------+-------+-------+-------+-------+-------+
    

    When run on this array, your method should produce the following output:

    {74, 85, 102, 99, 101, 56, 84}
    
  10. Write a method swap that accepts an array of integers and two integer indexes as its arguments, and swaps the elements at those indexes in the array. For example, consider the following code: int[] numbers = {13, 27, 92, -6, 45}; swap(numbers, 0, 3); swap(numbers, 2, 3);

    After the preceding code, the array should contain the following contents:

    {-6, 27, 13, 92, 45}
    

    You may assume that the indexes passed to your swap method are within the bounds of the array. You may wish to use the printAll method from a previous exercise to print the array's contents to check your answer.

  11. Write a method reverse that accepts an array of integers as its argument and reverses the order of the elements in that array. For example, if the array {13, 27, 92, -6, 45} is passed to your reverse method, afterward its contents should be {45, -6, 92, 27, 13}. Your method should work on an array of any length. Note that you may have to deal with arrays of even or odd size! HINT: You may wish to use a solution to an earlier exercise to help you solve this problem.

    Also try writing a variation of this method that only accepts one array as a parameter and returns the reversed array. Inside the method, create a second array of numbers to hold the reversed contents, and return it.

  12. Write a method copyAll that accepts two arrays of integers as its arguments and copies the element values from the first array into the second array. For example, consider the following code: int[] array1 = {2, 1, 4, 3, 6, 5}; int[] array2 = new int[6]; copyAll(array1, array2);

    After your copyAll method executes, the array array2 should have the contents {2, 1, 4, 3, 6, 5} just like array1. You may assume that the second array is always at least as large in length as the first array.

    Also try writing a variation of this method that only accepts one array as a parameter and returns the copied array. Inside the method, create a second array of numbers to hold the copy and return it.

  13. What is the output of the following program?
            public class Confusing {
                public static void main(String[] args) {
                    int[] numbers = {3, 7, 1, 0, 25, 4, 18, -1, 5};
                    mystery(numbers, 3, 1);
                    mystery(numbers, 5, 6);
                    mystery(numbers, 8, 4);
                    
                    // print the array contents
                    for (int i = 0; i < numbers.length; i++) {
                        System.out.print(numbers[i] + " ");
                    }
                    System.out.println();
                }
                
                public static void mystery(int[] data, int x, int y) {
                    data[data[x]] = data[y];
                    data[y] = x;
                }
            }
  14. Write a method equal that accepts two arrays of integers as its arguments and returns a boolean value indicating whether the two arrays contain the same elements. For example, consider the following code: int[] a1 = {1, 4, 9, 16, 25}; int[] a2 = {-1, 2, -3, 4, -5}; int[] a3 = {1, 4, 9, 16, 25}; int[] a4 = {0, 0, 0}; System.out.println("a1 equals a2? " + equal(a1, a2)); System.out.println("a1 equals a3? " + equal(a1, a3)); System.out.println("a2 equals a3? " + equal(a2, a3)); System.out.println("a2 equals a2? " + equal(a2, a2)); System.out.println("a1 equals a4? " + equal(a1, a4)); System.out.println("a4 equals a1? " + equal(a4, a1));

    The output from the preceding code should be:

    a1 equals a2? false
    a1 equals a3? true
    a2 equals a3? false
    a2 equals a2? true
    a1 equals a4? false
    a4 equals a1? false
    

    Note that arrays of different lengths cannot be equal, so the method should return false in such cases.

  15. Write a method isPalindrome that accepts an array of integers as its argument and returns a boolean indicating whether that array's elements are the same forwards as backwards. In other words, the array {1, 8, 15, 7, 15, 8, 1} is a palindrome while {6, 2, 4, 3, 2, 6} is not.

    Hint: You may be able to use solutions to previous exercises to help you write this method.

  16. Write a method copyRange that takes two arrays a1 and a2, two starting indexes i1 and i2, and a length l, and copies the first l elements of a1 starting at index [i1] into array a2 starting at index i2. Assume that the arguments' values are valid, that the arrays are large enough to hold the data, and so on.
  17. Write a static method named sumAll that accepts an array of integers as its argument and returns the sum of all integers in the array. For example, if the array passed contains the values {74, 85, 102, 99, 101, 56, 84}, your method should return 601.
  18. Write a static method named average that returns the average (arithmetic mean) of all elements in an array of integers as a real number (double). For example, if the array passed contains the values {1, -2, 4, -4, 9, -6, 16, -8, 25, -10}, your method should return 2.5.
  19. Write a static method named mode that returns the most frequently occurring element of an array of integers. Assume that the array has at least one element and that every element in the array has a value between 0 and 100 inclusive. Break ties by choosing the lower value. For example, if the array passed contains the values {}, your method should return .

    Hint: You may wish to look at the tally program to get an idea how to solve this problem. Can you write a version of this method that does not rely on the elements being between 0 and 100?

  20. Write a static method named stdev that returns the standard deviation of an array of integers. Standard deviation is computed by taking the square root of the sum of the squares of the differences between each element and the mean, divided by one less than the number of elements. (It's just that simple!) More concisely and mathematically, the standard deviation of an array a is written as follows:

    For example, if the array passed contains the values {1, -2, 4, -4, 9, -6, 16, -8, 25, -10}, your method should return approximately 11.237.

  21. Write a method named kthLargest that accepts an integer k and an array a as its parameters and returns the element such that k elements have greater or equal value. If k = 0, return the largest element; if k = 1, return the 2nd largest element, and so on. For example, if the array passed contains the values {74, 85, 102, 99, 101, 56, 84} and the integer k passed is 2, your method should return 99, because there are 2 values at least as large as 99 (101 and 102). Assume that 0 ≤ k < a.length.

    Hint: consider sorting the array (or sorting a copy of the array) first.

  22. Write a method median that accepts an array of integers as its argument and returns the median of the numbers in the array. The median is the number such that if you arranged the elements in order, it would appear in the middle. Assume that the array is of odd size (so that one sole element constitutes the median) and that the numbers in the array are between 0 and 99 inclusive.

    For example, consider the following code:

    int[] data = {5, 2, 4, 17, 55, 4, 3, 26, 18, 2, 17}; System.out.println("Median is " + median(data)); int[] data2 = {42, 37, 1, 97, 1, 2, 7, 42, 3, 25, 89, 15, 10, 29, 27}; System.out.println("Median is " + median(data2)); The output should be:
    Median is 5
    Median is 25
    

    Hint: This is tricky! You may wish to look at the Tally program from earlier in this chapter. Tally the occurrences of each number, then examine the tallies and stop once you have seen half the elements tallied.

    For added challenge, try to write a version that works regardless of the elements' values. Next, make your new version also work for arrays of even size. The median of such an array is the average of the middle two elements.

  23. Use your median method from the last exercise to write a program that reads a file full of integers and prints the median of those integers.
  24. Write a static method named wordLengths that accepts a String representing a file name as its argument. Your method should open the given file and count the number of letters in each token in the file, and output a result diagram of how many words contained each number of letters. For example, if the file contains the following text:
            Before sorting:
            13 23 480 -18 75 
            hello how are you feeling today 
            
            After sorting:
            -18 13 23 75 480 
            are feeling hello how today you 

    Your method should produce the following output to the console:

    1: 0
    2: 6    ******
    3: 10   **********
    4: 0
    5: 5    *****
    6: 1    *
    7: 2    **
    8: 2    **
    

    Assume that no token in the file is more than 80 characters in length.

  25. Write a static method named areAnagrams that accepts two Strings as its parameters and returns whether the two strings are anagrams of each other. Anagrams are strings that contain the same letters, regardless of order, such as "elvis" and "lives" or "dictionary" and "indicatory". Make your method case-insensitive. Assume that the two Strings are single words with no spaces or punctuation.

    For added challenge, make your method work for multi-word strings.

  26. Write a static method named maxLength that takes an ArrayList of Strings as a parameter and that returns the length of the longest String in the list. If your method is passed an empty ArrayList, it should return 0.
  27. Write a static method named removeEvenLength that takes an ArrayList of Strings as a parameter and that removes all of the Strings of even length from the list.
  28. Write a static method named stutter that takes an ArrayList of Strings as a parameter and that replaces every String with two of that String. For example, if the list stores the values ("how", "are", "you?") before the method is called, it should store the values ("how", "how", "are", "are", "you?", "you?") after the method finishes executing.
  29. Write a static method named removeDuplicates that takes as a parameter a sorted ArrayList of Strings and that eliminates any duplicates from the list. For example, suppose that an ArrayList called "list" contains the following values:
            [be, be, is, not, or, question, that, the, to, to]

    After calling removeDuplicates(list), the list should store the following values:

            [be, is, not, or, question, that, the, to]

    Because the values will be sorted, all of the duplicates will be grouped together. You may assume that the ArrayList contains only String values, but it might be empty.

7.12 Programming Problems

  1. Java's type int has a limit on how large of an integer it can store. This limit can be circumvented by representing an integer as an array of digits. Write an interactive program that adds two integers of up to 50 digits each.

  2. Personal mailing labels can prove quite useful. Write a program that reads a five-line address from an input file and produces an output file with the address repeated 50 times in three columns.

  3. Write a program that reads an input file of numbers and reports the average of the numbers as well as how far each number is from the average.

  4. Write a program that plays a variation of the game of Mastermind with a user. For example, the program can use pseudorandom numbers to generate a 4-digit number. The user should be allowed to make guesses until the user gets the number correct. Clues should be given to the user indicating how many digits of the guess are correct and in the correct place, and how many are correct but in the wrong place.


Stuart Reges
Marty Stepp
Last modified: Tue Jan 3 00:38:21 PST 2006

Chapter 8
Defining Classes

Copyright © 2005 by Stuart Reges and Marty Stepp

8.1 Introduction

Now that we have spent time mastering the basics of procedural style programming in Java, we are finally ready to explore what Java was designed for: object-oriented programming. This chapter introduces the basic terminology we use when talking about objects and shows you how to declare your own classes to create your own objects.

8.2 History and terminology

Most of our focus so far has been on procedural decomposition, breaking a complex task into smaller subtasks. This is the oldest style of programming and even in a language like Java we still use procedural techniques. But Java provides a different approach to programming that we call object-based programming or object-oriented programming.

Object-Oriented Programming (OOP)

Reasoning about a program as a set of objects rather than as a set of actions.

To understand the basic idea consider the history of how users interact with a computer. If you went back to 1983 you'd find that the IBM PC and its "clones" were the dominant machines and most people were running an operating system called DOS. DOS uses what we call a "command line interface" in which the user types commands at a prompt. The console window we have been using is a similar interface. To delete a file in DOS, for example, you would give the command "del" (short for "delete") followed by the file name, as in:

        del data.txt

This interface can be described in simple terms as "verb noun". In fact, if you look at a DOS manual, you will find that it is full of verbs. This closely parallels the procedural approach to programming. When we want to accomplish some task, we issue a command (the verb) and then mention the object of the action (the noun, the thing we want to affect).

In 1984 Apple Computer released a new computer called a Macintosh that had a different operating system that used what we call a Graphical User Interface or GUI. The GUI interface uses a graphical "desktop" metaphor that has become so well known that people almost forget that it is a metaphor.

Graphical User Interface (GUI)

An interface that uses sophisticated graphics (icons) and pointing devices like a mouse to allow users to work within a metaphor of a desktop with objects on it.

The Macintosh was not the first computer to use the desktop metaphor and a GUI, but it was the first such computer that became a major commercial success. Later Microsoft brought this functionality to IBM PCs with its Windows operating system.

So how do you delete a file on a Macintosh or on a Windows machine? You locate the icon for the file and click on it. Then you have several options. You can drag it to the garbage/recycling bin. Or you can give a "delete" command. Either way you start with the thing you want to delete and then give the command you want to perform. This is a reversal of the fundamental paradigm. In DOS it was "verb noun" but with a GUI it's "noun verb". This different way of viewing the world is the core of object-oriented programming.

Most modern programs use a graphical user interface because we have learned that people find it more natural to work this way. We are used to pointing at things, picking up things, grabbing things. Starting with the object is very natural for us. This approach has also proven to be a helpful way to structure our programs, by dividing our programs up into different objects that each can do certain tasks rather than dividing up a task into subtasks.

Object-oriented programming involves a particular view of programming that has its own terminology. Let's explore that terminology with non-programming examples first. We've been using the word "object" for a while without giving a very good definition of the word. Below is a definition that is often used by object-oriented programmers.

Object

An object encapsulates state and behavior.

To understand this definition, we have to understand the terms "state" and "behavior" and the concept of "encapsulation". These are some of the most fundamental concepts in object-oriented programming. To understand them better, let's use a non-programming example. Consider the class of objects we call radios.

What are the different states a radio can be in? It can be turned on or turned off. It can be tuned to one of many different stations. And it can be set to different volumes. Any given radio has to "know" what state it is in, which means that it has to keep track of this information internally. We call the collection of such internal values the state of an object.

State

A set of values (internal data) stored in an object.

What are the behaviors of a radio? The most obvious one is that it produces sound when it is turned on and the volume is turned up. But it has other behaviors that involve the manipulation of its internal state. We can turn a radio on or off. We can change the station. We can change the volume. We can see what station the radio is set to right now. We call the collection of these operations the behavior of an object.

Behavior

A set of actions an object can perform, often reporting or modifying its internal state.

To understand the notion of encapsulation, it is useful to consider an important property that radios have. Almost everyone knows how to use a radio, but only a few of us know how to actually build a radio. It is a benefit of the radio's design that we don't need to learn those kinds of details. This is an example of an important computer science concept known as abstraction.

Abstraction

Reasoning about the essential properties of something while ignoring its unimportant details.

We all understand the essential properties of a radio (what it does), but only a few of us understand the internal details of the radio (how it does it). This leads to an important dichotomy of an external view of an object versus an internal view of an object. From the outside, we just see behavior. From the inside, we see internal state that is used to accomplish that behavior.

The external view of an object is most important to the users of the object, which are known as clients.

Client (or client code)

Code that uses an object.

We are all clients of radios because we use them. As a client, we don't need to know how they work. We just need to know what they do. In fact, radios and other electronic devices have a case or chassis that houses all of the electronics so that we don't even see them from the outside. Instead, we have a set of dials and buttons and displays that allow us to manipulate the radio without having to deal with all of the circuitry that makes it work.

When applied to programming, this principle is known as encapsulation.

Encapsulation

Hiding the implementation details of an object from the clients of an object.

When objects are properly encapsulated, the clients of an object don't need to know anything about the internal workings of the object. Only the implementor of an object needs to know about those details.

8.3 Objects and Programming

To create a new type of objects in Java, we must create a class for that type and add code to the class, telling how to do the following things:

Once we have written the appropriate code, the class can be asked to create objects of its type. We can then use those objects in our programs. We can think of a class as a factory with blueprints telling how to do the previously mentioned tasks.
               +--------------------------------------+
               |             Radio Factory            |
               |                                      |
               | state:                               |
               |   # of radios made                   |
               |                                      |
               | behavior:                            |
               |   directions on how to build a radio |
               +--------------------------------------+
                                  |
                                  | builds
                                  |
          +-----------------------+-------------------------+
          |                       |                         |
          v                       v                         v
+-------------------+    +-------------------+    +-------------------+
|  Radio #1         |    |  Radio #2         |    |  Radio #3         |
|                   |    |                   |    |                   |
| state:            |    | state:            |    | state:            |
|   station         |    |   station         |    |   station         |
|   volume          |    |   volume          |    |   volume          |
|                   |    |                   |    |                   |
| behavior:         |    | behavior:         |    | behavior:         |
|   power on/off    |    |   power on/off    |    |   power on/off    |
|   change station  |    |   change station  |    |   change station  |
|   adjust volume   |    |   adjust volume   |    |   adjust volume   |
+-------------------+    +-------------------+    +-------------------+

If we wanted to create actual Java objects to represent radios, we'd write a Radio class that specified the state and behavior each Radio object should have. That state and behavior would become a part of every Radio object created. Here is what the class and some of its objects might look like:
               +-----------------------------------------+
               |             class Radio                 |
               |                                         |
               | static int ourNumRadios                 |
               |                                         |
               | public Radio(double station, int volume)|
               +-----------------------------------------+
                                   |
                                   | constructs
                                   |
             +---------------------+---------------------+
             |                     |                     |
             v                     v                     v
  +-------------------+  +-------------------+  +-------------------+
  |  Radio object #1  |  |  Radio object #2  |  |  Radio object #3  |
  |                   |  |                   |  |                   |
  | double station    |  | double station    |  | double station    |
  | int volume        |  | int volume        |  | int volume        |
  |                   |  |                   |  |                   |
  | powerOn()         |  | powerOn()         |  | powerOn()         |
  | powerOff()        |  | powerOff()        |  | powerOff()        |
  | setStation(value) |  | setStation(value) |  | setStation(value) |
  | setVolume(value)  |  | setVolume(value)  |  | setVolume(value)  |
  +-------------------+  +-------------------+  +-------------------+

8.4 A Programming Example: Point

In Chapter 3 we saw a Point type to represent (x, y) points in 2D space. Suppose we wanted to write our own version of the Point type. It would actually be possible to do a minimal version of it in just four lines of code.

8.4.1 Point class with state (data fields)

Here is an initial Point class that stores an (x, y) position:

        // A Point object represents an ordered pair of 2D coordinates (x, y).
        public class Point {
            int x;   // x-coordinate
            int y;   // y-coordinate
        }

This first version of the Point class is grossly incomplete, but it does give us a minimal blueprint of the state of each Point object: an int named x, and an int named y. With this version of the class, it's now legal for us to write code like the following:

        public class UsePoint {
            public static void main(String[] args) {
                // create two Point objects
                Point p1 = new Point();
                p1.x = 5;
                p1.y = -2;
        
                Point p2 = new Point();
                p2.x = -4;
                p2.y = 3;
        
                // print each point
                System.out.println("(" + p1.x + ", " + p1.y + ")");
                System.out.println("(" + p2.x + ", " + p2.y + ")");
            }
        }

The declarations of the variables in the Point class specify what variables should exist inside each Point object. Such variables are called data fields or instance variables. Together, the set of an object's fields and their values make up the object's state.

Data Field

A variable inside an object that makes up part of its internal state.

Currently, when one of our Point objects is initially created, its fields receive a default value depending on their type. Numeric types receive a 0 value, boolean fields receive a false value, and object types receive a null value. So a new Point object always begins at the origin of (0, 0) until we change the values of its data fields.

The previous code created two Point objects, each referred to by a Point variable, and gave them the following internal states:
               +--------------------+                  +--------------------+
       +---+   |   +----+    +----+ |          +---+   |   +----+    +----+ |
    p1 | +-+-->| x |  5 |  y | -2 | |       p2 | +-+-->| x | -4 |  y |  3 | |
       +---+   |   +----+    +----+ |          +---+   |   +----+    +----+ |
               +--------------------+                  +--------------------+

This minimal Point class works and lets us do a few things, but it has several problems. It's too clunky to use these Point objects, and they aren't encapsulated. We haven't specified any behavior for Point objects or how to construct them--we've only specified their state. An object that only contains state and no behavior is sometimes called a structure or a record. In the next few sections, we'll grow our Point type from a clunky structure into a proper Java class of objects.

8.4.2 Point class with behavior (methods and constructor)

We've written a Point class that holds object state in data fields. But so far the Point objects have no behavior. We'll now add behavior to our Point objects by writing methods inside the Point class. We'll also learn how to easily initialize Point objects' state with a special method called a constructor.

Mutators and the translate method

In Chapter 3, you saw that the Point type in Java has a method named translate that takes two arguments (a delta-x and delta-y) that adjusts the Point's coordinates by those amounts.

For example, the following code translates a Point object's position from (3, 8) to (5, 7):

        public static void main(String[] args) {
            Point p = new Point();
            p.x = 3;
            p.y = 8;
            p.translate(2, -1);
            System.out.println("(" + p.x + ", " + p.y + ")");
        }

We can add this same behavior to our Point type by writing our own translate method. Methods implement the behavior of an object. You have already called methods on several types of Java objects.

The syntax for writing objects' methods is similar to the static methods you have already seen, except that the static keyword is absent. Objects' methods can accept parameters and return values, just like static methods.

Here's a version of the Point class with a translate method. Java style guidelines suggest to declare the fields at the top of the class, with the methods below. But in general it is legal for a class's contents to appear in any order.

        // A Point object represents an ordered pair of 2D coordinates (x, y).
        public class Point {
            int x;   // x-coordinate
            int y;   // y-coordinate
        
            // Shifts the position of this Point object by the given amount
            // in each direction.
            public void translate(int dx, int dy) {
                this.x += dx;
                this.y += dy;
            }
        }

This method accepts the delta-x (dx) and delta-y (dy) as arguments, then modifies the object's data fields to store the new information. The dx and dy are added to the x and y for this Point.

If each object has its own copy of each field, how does the code know which object's x or y data field to set? Objects' constructors and methods have a knowledge of the object they are operating upon. In any method of an object, there is a special variable named this that refers to the object itself that is being used. We can use the this reference like other variables, in this case to set that object's data field state in the translate method.
        Point p1 = new Point();
        p1.x = 8;
        p1.y = 3;

Two ways that a data field's value can be modified:

1. Outside the class,
by directly accessing the fields:

p1.x += 2;
p1.y += -1;
 

2. Inside the class,
by calling a method on the object:

p1.translate(2, -1);
         \
          \
           +-----------------------------------------+
           | public void translate(int dx, int dy) { |
           |     this.x += dx;                       |
           |     this.y += dy;                       |
           | }                                       |
           +-----------------------------------------+

Inside the above code, the this reference refers to p1, because that is the object on which we called the translate method. If we called translate on some other Point variable p2, this would refer to p2 during that call. The this keyword reflects the key difference between static methods and non-static methods: non-static methods operate on a particular object.

It isn't mandatory to use the this keyword when referring to an object's data fields. If the field name is written by itself, Java assumes we mean the data field of the current object. Here is an alternative version of the translate method without the use of the this keyword:

        // A version of translate that does not use the this keyword.
        public void translate(int dx, int dy) {
            x += dx;   // same as  this.x += dx;
            y += dy;   // same as  this.y += dy;
        }

The authors feel that using the this keyword consistently is more clear, so we will continue to use it in the rest of this chapter.

The following client code is an example usage of our new translate method. The output of the code is (5, 7), as expected.

        public class UsePoint {
            public static void main(String[] args) {
                Point p = new Point();
                p.x = 3;
                p.y = 8;
                p.translate(2, -1);
                System.out.println("(" + p.x + ", " + p.y + ")");
            }
        }

Methods like translate are useful because they increase the amount of abstraction in our class. A cynic might question the importance of having a translate method, since we could have just manually adjusted the x and y values of our Point from the client code. The benefit of translate is that the client code is shorter and easier to read. Translating points to new locations is a common operation to perform. Even though we could have already performed this functionality, it was manual and tedious. By adding the translate method, we have given a clean abstraction for moving the position of a point.

The general syntax for a method of an object is as follows. Notice that it is the same as the general syntax of static methods, but without the static keyword.

public <type> <name>(<type> <name>, ..., <type> <name>) { <statement(s)>; }

Our translate method is an example of a classification of methods called mutators. A mutator is an object's method that changes the state of that object in some way. Generally this means that a mutator assigns a new value to one of the object's data fields. For a radio, the mutators would be switches and knobs that turned the radio on and off or that changed the station or that changed the volume. You have already used several mutators, such as the setLocation on Java's Point objects or the nextLine method on Scanner objects.

Mutator

A method of an object that modifies its internal state.

There are some conventions about how to write mutators. Many mutator methods' names begin with "set", such as setID or setTitle. Usually mutator methods have a void return type. Mutators often accept arguments that specify the new state of the object, or the amount by which to modify its current state. When we discuss encapsulation later in this chapter, we'll see that it is a good general practice to prevent modification of data fields except in the ways necessary for the object to be useful.

Point class with constructor

An annoying problem in the previous code is that it takes three lines to create and initialize the state of one Point object. You may recall that in Chapter 3, we were able to construct Java Point objects like this:

        Point p = new Point(10, 27);

Such a statement wouldn't be legal for our minimal Point class, because we haven't written any code specifying how to construct a Point from two ints. We can do so by adding a special method to our Point class called a constructor.

Constructor

A method that creates a new object and initializes its state.

A constructor looks like a method that has the same name as the class, but with no return type specified. A constructor may take parameters that help to initialize the object's state.

Here is a version of our Point class with a constructor:

        // A Point object represents an ordered pair of 2D coordinates (x, y).
        public class Point {
            int x;   // x-coordinate
            int y;   // y-coordinate
        
            // Constructs a Point object with the given x and y coordinates.
            public Point(int x, int y) {
                this.x = x;
                this.y = y;
            }
            
            // Shifts the position of this Point object by the given amount
            // in each direction.
            public void translate(int dx, int dy) {
                this.x += dx;
                this.y += dy;
            }
        }
Our Point constructor accepts parameters specifying the x and y coordinates for the Point to be created. It saves those values into the object's x and y data fields.

After adding the constructor, our sample UsePoint code becomes shorter and simpler:

        public static void main(String[] args) {
            // create two Point objects
            Point p1 = new Point(5, -2);
            Point p2 = new Point(-4, 3);
        
            // print each point
            System.out.println("(" + p1.x + ", " + p1.y + ")");
            System.out.println("(" + p2.x + ", " + p2.y + ")");
        }

Notice that the names of the constructor arguments are the same as the names of a Point object's data fields. This is not required, but it can be a good practice to make it clear how each constructor argument is used.

Notice that the this keyword appears again here in the constructor. In the last section, we talked about how the this can be omitted in methods. However, if we omit this in this case, we'd have to change the names of the arguments to avoid ambiguity, because they have the same names as the data fields: x and y.

To illustrate this point: The following code is incorrect. It does not store the arguments' values into the Point object's data fields. It essentially does nothing--it just reassigns

        // This code is incorrect!
        public Point(int x, int y) {
            x = x;   // incorrect; does not set this.x value!
            y = y;   // incorrect; does not set this.y value!
        }

Here is a correct version of the Point constructor without the use of the this keyword. This version works because the parameter names aren't the same as the field names, so there is no conflict.

        // correct, but perhaps unclear
        public Point(int initialX, int initialY) {
            x = initialX;
            y = initialY;
        }

Once again, we feel that using the this keyword consistently is more clear, so we recommend that usage.

The general syntax for constructors is the following:

public <type> (<type> <name>, ..., <type> <name>) { <statement(s)>; }

When a class doesn't have a constructor, like our Point class before this section, Java automatically supplies an empty default constructor that accepts no arguments. That is why it was previously legal to construct a new Point().

However, when we write a constructor of our own, Java doesn't supply the default empty constructor. So it would now be illegal to construct Point objects without passing in an x and y coordinate. In a later section of this chapter, we'll write additional code to restore this ability.

        Point p1 = new Point();  // this will not compile now

Accessor methods and the toString method

Java's Point type (like most predefined types in Java) had a method named toString that we could call to return the state of a Point object as a String. We saw in Chapter 3 that we could write code like the following:

        // using Java's Point type
        Point p = new Point(3, 8);
        System.out.println("p = " + p.toString());

Recall that the toString() was optional in many cases, and the above code could have been written as:

        Point p = new Point(3, 8);
        System.out.println("p = " + p);

Either version of the previous code (when run on Java's Point objects, not our own) produces the output:

        p = java.awt.Point[x=3,y=8]

Our own Point type doesn't have this functionality. When we try to print one of our Point objects to the console, we get a strange result. First of all, it might seem odd that the code compiles at all. Despite the fact that we haven't defined any toString behavior for our Point type, it is legal to call toString() on one of our Point objects. Secondly, the output this toString() call produces is bizarre. Here's the output of the same code shown previously, when run using our own Point class:

        p = Point@119c082

The reason the code compiles, despite our not having written any toString method, is that there are certain methods common to every Java object. The toString method is one of these; others include equals and getClass. When defining a new type of objects, Java gives your objects a default version of each of these methods. If you like, you can replace this default version with one of your own.

In the case of toString, the default version of the method prints the type of the object, an @ sign, and a hexadecimal (base-16) number. This isn't a very useful message to represent a Point object, so we'll write our own toString method that builds and returns a String representing the state of the Point object. The toString method requires no parameters and has a return type of String.

        public String toString() {
            return "(" + this.x + ", " + this.y + ")";
        }

With our toString method written, the same code sample shown previously now produces the following output:

        p = (3, 8)

We didn't write our toString code to match Java's Point type's toString code, instead opting for a shorter version. Sun's Java guidelines recommend writing a toString method in every class you write.

The toString method further improves the readability of the client code we've been showing previously that creates and displays two Point objects. Here's a new version of that client code:

        // create two Point objects
        Point p1 = new Point(5, -2);
        Point p2 = new Point(-4, 3);

        // print each point
        System.out.println(p1);
        System.out.println(p2);

The general syntax for a toString method is the following. The statements of the method should build and return the appropriate String result.

public String toString() { <statement(s)>; }

The toString method is an example of a classification of methods called accessors. An accessor is an object's method that gives access to, or provides a representation of, the state of that object in some way. For example, a radio object might provide access to its current station setting or current volume. Examples of accessor methods you have seen include the length and substring methods of String objects, or the getX and getY methods on Java's Point objects.

Note that accessors are not used to change the state of the object--they only allow you to view it. We can't change the Point object's coordinates by a call of toString, for example.

Accessor

A method of an object that allows access to view its internal state.

There are some conventions about how to write accessors. They usually don't need to accept any arguments, and they also usually have a non-void return type to allow them to return the appropriate information. Many accessors' names begin with the prefix "get", such as getBalance or getX. In the case of toString, though, that isn't the case.

8.4.3 Encapsulated Point class

Suppose we'd like to constrain the x and y coordinates of all Point objects so that they are always non-negative. We can set up appropriate code in our constructor and methods to test for values less than 0, and either adjust them to 0, or throw an IllegalArgumentException.

        public Point(int x, int y) {
            if (x < 0 || y < 0) {
                throw new IllegalArgumentException();
            }

            this.x = x;
            this.y = y;
        }

        public void translate(int dx, int dy) {
            if (this.x + dx < 0 || this.y + dy < 0) {
                throw new IllegalArgumentException();
            }

            this.x += dx;
            this.y += dy;
        }

However, client code outside the Point class could still make the Point's location become negative, by setting the x or y data fields' values directly. Here's an example of such a malicious piece of client code:

        public class UsePoint {
            public static void main(String[] args) {
                Point p = new Point(5, 4);
                p.x = -7;
                p.y = -3;
                
                System.out.println("Haha, I set it to (" + p.x + ", " + p.y + ")");
            }
        }

The weakness here is that our Point class is not encapsulated. Java objects that are encapsulated do not allow direct access to their fields. Encapsulation would prevent the UsePoint class from directly referring to or modifying a Point object's x or y data field values. That way, we could better enforce our new constraint that x and y cannot be less than 0.

To encapsulate data fields on an object, we must declare them to be private. The data fields of our Point type would be declared as:

        private int x;
        private int y;

In fact, the preferred general syntax for data fields is the following:

private <type> <name>;

Fields can be declared with an initial value, such as:

private <type> <name> = <value>;

Declaring data fields private encapsulates the state of the object, in the same way that the radio chassis protects the user from seeing the wires and circuitry inside the radio. Private data fields are visible to all of the code inside the Point class (inside the Point.java file), but not anywhere else.

However, once we encapsulate the data fields of Point objects, we can no longer externally refer to them in our code. This means that the following line would not compile successfully:

        System.out.println("p1 has an x coord of: " + p1.x);

An error message such as this one is produced by the compiler:

        C:\book\ch8\Example.java:8: x has private access in Point
                System.out.println("p1 has an x coord of: " + p1.x);

To preserve the functionality of our program, we need to provide access to the values of the Point object's data fields. We will do this by adding some new accessor methods to the Point class.

If the value of an object's data field might be useful externally, it is common to write an accessor to return that value. This does not break the encapsulation of the object, because the accessor does not allow the outside code to change the data field's value, only to examine it. In other words, accessor methods allow a person "read only" access to the state of the object. We could add additional accessor methods to our Point objects to return the values of its data fields x and y.

        public int getX() {
            return this.x;
        }
        public int getY() {
            return this.y;
        }

Some programmers even prefer to use the appropriate accessor when using a data field's value in an expression. If we followed this convention in the Point class, the toString method would look like this:

        public String toString() {
            return "(" + this.getX() + ", " + this.getY() + ")";
        }

An added benefit of encapsulating our Point objects with private data fields and getX/getY methods is that we could later change the internal structure of our Point class, and the client code would not have to be modified. For example, sometimes it is useful to express 2D points in polar coordinates, using a radius r and angle theta from the origin. In this representation, a Point's x and y coordinates are not stored directly, but can be computed as (r * cos(theta), r * sin(theta)). In theory, we could modify our encapsulated Point to use an (r, theta) representation internally, then modify the getX and getY methods to compute and return the appropriate values.

Now that our Point class is encapsulated, one drawback is that it's now not easy for the UsePoint class (or any other client code) to set a Point to a new location. We'd have to translate it to the new location using our translate method, which would require some math and would be cumbersome if we had to do it many times. For convenience, we'll add a new mutator to our encapsulated Point class called setLocation that sets both the x and y data fields on the object to new values.

        public void setLocation(int x, int y) {
            if (x < 0 || y < 0) {
                throw new IllegalArgumentException();
            }
            
            this.x = x;
            this.y = y;
        }

The setLocation method gives us some nice opportunities to reduce redundancy. For one, its body is the same as the body of our constructor, so we can modify our constructor to call setLocation:

        public Point(int x, int y) {
            this.setLocation(x, y);
        }

Also, translating a Point can be thought of as setting its location, so we could have the translate method call the setLocation method.

        public void translate(int dx, int dy) {
            this.setLocation(this.x + dx, this.y + dy);
        }

Channeling all code that sets the Point's position through the setLocation method proves useful because we only have to check for the x and y being less than 0 once.

8.5 More Object Features

In the last section, we developed a Point type. In this section, we'll add more functionality to our Point to make it easier and more convenient to use in client code.

8.5.1 Multiple Constructors with the this Keyword

Presently our Point class has a constructor that requires two arguments: the Point's initial x and y coordinates.

        public Point(int x, int y) {
            this.setLocation(x, y);
        }

Before we wrote our constructor, it was legal to create a Point object that represented the origin, (0, 0), by passing no arguments when constructing it. This isn't legal any more, now that we added our two-argument constructor. However, we can make it legal again by adding a second constructor to our Point class.

A Java class can have more than one constructor, as long as each constructor has a different signature (i.e., accepts either a different number of parameters or different parameter types). In other words, we can add a second constructor to our Point class as long as it doesn't have two int arguments as its parameters. This is an example of overloading.

Our new, parameterless constructor might look like this:

        // Constructs a Point object at the origin, (0, 0).
        public Point() {
            this.x = 0;
            this.y = 0;
        }
Now it would be possible to construct Points in two ways:

        Point p1 = new Point(5, -2);   // (5, -2)
        Point p2 = new Point();        // (0, 0)

But by adding a second constructor, we have again introduced a bit of redundancy into our class. Both constructors perform similar actions; the only difference is exactly what initial value the x and y data fields receive. A useful observation here is that the new constructor can be expressed in terms of the old constructor.

        Point p2 = new Point();
has the same effect as:
        Point p2 = new Point(0, 0);
We can express this in our Point class by having the new no-argument constructor call the old two-parameter constructor. The syntax for one constructor to call another is a bit strange: in its body we write the keyword this, followed by the arguments to pass to the other constructor in parentheses. In our case, we want to pass argument values of 0 and 0 to initialize each field.

        public Point() {
            this(0, 0);
        }

The odd this(arguments) syntax causes the parameterless constructor to utilize the behavior of the other constructor. The following diagram represents this behavior:

        public Point() {
            this(0, 0);
        }    \
              \                 0      0
             +-\----------------------------+
             | public Point(int x, int y) { |
             |     this.setLocation(x, y);  |
             | }    \                       |
             +-------\----------------------+
                      \                            0      0
                     +-\---------------------------------------+
                     | public void setLocation(int x, int y) { |
                     |     this.x = x;                         |
                     |     this.y = y;                         |
                     | }                                       |
                     +-----------------------------------------+

The general syntax for one constructor to call another is the following:

this(<expression>, <expression>, ..., <expression>);

This is really just the normal syntax for a method call, except for the fact that we use the special "this" keyword where we would normally put the name of a method.

8.5.2 The equals method

In many of the existing Java types you have seen, there was an equals method that compared two objects of the same type. We saw that the == operator did not behave as expected when used on objects. Here is some example code using Java's Point type that illustrates this. Surprisingly, the output produced is false, because == compares the references to the objects, rather than the internal state of each object.

        Point p1 = new Point(6, 11);
        Point p2 = new Point(6, 11);
        System.out.println(p1 == p2);        // false

The solution seen before was to use the equals method to compare objects. Modifying the last line of the preceding code to the following results in the expected output of true.

        // with Java's Point type, this works as we expect.
        System.out.println(p1.equals(p2));   // true

Our own Point type would still see an output of false even for the modified code, because we didn't define any equals method. Like toString, equals is a method that can be called on every object in Java, even if that object's class doesn't define an equals method. The default behavior of equals is to behave the same way as the == operator; that is, to compare the references of the two objects.

We can define an equals method in our Point class. The equals method must accept an Object as its argument and return a boolean value indicating whether the objects have the same state. For our Point objects, having the same state means that the objects have the same x and y values in their data fields.

The equals method is odd in that it takes an Object as its argument, not a Point. This is required because the equals method must be able to work for any kind of Java object. We want to treat the argument passed as a Point object, so we must cast it from type Object to type Point.

A well-written equals method should work no matter what parameter value is passed, including null values or objects of another class. The easiest way to make sure that the object we're comparing against is the same type as this object is to call the getClass method on each and compare them. The getClass method returns an object representing an object's class. We won't talk about it in any detail here, except to use it as part of the general contract of the equals method.

The complete equals method code appears in our complete Point class in the next section. Notice that we can refer to the other.x and other.y fields because Point data fields are accessible anywhere inside the Point class, even those of other Point objects.

8.5.3 Final version of Point class

Here is the complete Point class after all of the changes in this section:

        // A Point object represents an ordered pair of 2D coordinates (x, y).
        public class Point {
            private int x;   // x-coordinate
            private int y;   // y-coordinate
        
            // Constructs a Point object at the origin, (0, 0).
            public Point() {
                this(0, 0);
            }
            
            // Constructs a Point object with the given x and y coordinates.
            public Point(int x, int y) {
                this.setLocation(x, y);
            }
            
            // Returns the x-coordinate of this Point.
            public int getX() {
                return this.x;
            }
            
            // Returns the y-coordinate of this Point.
            public int getY() {
                return this.y;
            }
            
            // Returns whether the two Points have the same x/y coordinates.
            public boolean equals(Object o) {
                if (o == null || this.getClass() != o.getClass()) {
                    return false;
                }
                
                Point other = (Point) o;
                return this.x == other.x && this.y == other.y;
            }
            
            // Sets this Point's x/y coordinates to the given values.
            public void setLocation(int x, int y) {
                if (x < 0 || y < 0) {
                    throw new IllegalArgumentException();
                }
                
                this.x = x;
                this.y = y;
            }
            
            // Returns a String representation of this Point object.
            public String toString() {
                return "(" + this.x + ", " + this.y + ")";
            }
            
            // Shifts the position of this Point object by the given amount
            // in each direction.
            public void translate(int dx, int dy) {
                this.setLocation(this.x + dx, this.y + dy);
            }
        }

8.6 Case Study: Stock Class

In this section, we'll create another new class of objects called Stock, for a program that manages a portfolio of shares of stock that the user has purchased. The user can add a new stock to his/her portfolio or can record purchases of shares of a stock at a certain price. This information can be used to report the investor's profit or loss on that stock. The interaction with the program might look like this:
        Type a stock symbol: AMZN
        How many purchases did you make? 2
        
        How many shares did you buy? 50
        What price per share did you pay? 35.06
        How many shares did you buy? 25
        What price per share did you pay? 38.52
        
        AMZN ( 75 shares, $ 2716.00 total cost )
        
        What is today's price per share for this stock? 37.29
        Your net profit/loss: $80.75

This program has several pieces of data that are related to each other:

The program also has several related behaviors:

The stock program could be written using primitive data such as doubles and Strings, but it would be cumbersome to keep track of all the data variables, especially if we used multiple stocks, because each would need a copy of those variables.

The program would likely be shorter and simpler if Java had Stock objects to represent all the shares purchased of a given stock and their purchase price. The Stock object would remember the shares and total price and could report the profit or loss based on the current daily stock price.

If there were such a thing as a Stock object, we could use Stock objects to write this program. The code might look something like this:

        public class StockManager {
            public static void main(String[] args) {
                Scanner console = new Scanner(System.in);
                System.out.print("Type a stock symbol: ");
                String symbol = console.next();
        
                // Create a Stock object to represent my purchases of this stock
                Stock currentStock = new Stock(symbol);
        
                System.out.print("How many purchases did you make? ");
                int numPurchases = console.nextInt();
                System.out.println();
                
                // Ask about each purchase
                for (int i = 0; i < numPurchases; i++) {
                    System.out.print("How many shares did you buy? ");
                    int numShares = console.nextInt();
                    System.out.print("What price per share did you pay? ");
                    double pricePerShare = console.nextDouble();
        
                    // Ask the Stock object to store this purchase
                    currentStock.purchase(numShares, pricePerShare);
                }
        
                // Show the overall state of Stock purchases made
                System.out.println();
                System.out.println(currentStock);
                System.out.println();
        
                // Use the Stock object to compute how much money I made / lost
                System.out.print("What is today's price per share for this stock? ");
                double currentPrice = console.nextDouble();
                double profitLoss = currentStock.getProfit(currentPrice);
        
                System.out.println("Your net profit/loss: $" + profitLoss);
            }
        }
Unfortunately, Java does not come with a Stock type. But we can add the Stock type to our program by writing it ourselves. To do this, we'll write a Stock class, in which we'll specify what data is stored in a Stock object and what behavior each Stock object will have. The StockManager program above will interact with the Stock class when it runs.

When we define new types of objects, we start to have programs that occupy more than one file. The StockManager program itself will reside in the file StockManager.java, and the new Stock class we'll write will go into a separate file Stock.java. When we compile and run StockManager.java, it will also depend on Stock.java to run properly. We'll keep these two files in the same folder so that the Java compiler can find them and compile them together.

To design our Stock type, let's begin by thinking about what data fields it would need. First of all, the Stock is constructed with a symbol, so we'll need a field for that. Also, for a Stock object to keep track of several purchases as in the log of execution shown previously, it must have data fields storing the total number of shares purchased and the total price paid for all of those shares.

8.6.1 Stock fields and constructor

Here is a possible list of the fields our Stock objects will need:

        private String symbol;      // stock symbol, such as "YHOO"
        private int numShares;      // total number of shares purchased
        private double totalCost;   // total cost for all shares purchased
A Stock object with symbol of "AMZN" and 50 total shares purchased at a total cost of $250.40 would look like this:

    +---------------------------------------+
    | numShares     totalCost      symbol   |      String
    |   +----+      +-------+      +----+   |    +--------+
    |   | 50 |      | 250.4 |      | ---+---+--->| "AMZN" |
    |   +----+      +-------+      +----+   |    +--------+
    +---------------------------------------+

The symbol field has an arrow to an external box because symbol is a reference to a String object. This is an example of how objects' data fields can be references to other objects.

Many Stock objects can be created, and each will have its own copy of the data fields. It would be possible to create a second Stock object with symbol "INTC" and 25 shares purchased at a total cost of $175.60. Its state would look like this:
    +---------------------------------------+
    | numShares     totalCost      symbol   |      String
    |   +----+      +-------+      +----+   |    +--------+
    |   | 25 |      | 175.6 |      | ---+---+--->| "INTC" |
    |   +----+      +-------+      +----+   |    +--------+
    +---------------------------------------+

Our Stock constructor should accept a parameter specifying the symbol for this Stock. We will save the symbol value into the object's symbol data field. The constructor doesn't need to accept arguments for the numShares or totalCost data fields -- we'll just assume those should be initialized to 0.

        // Creates a new Stock with the given symbol and no shares purchased.
        public Stock(String symbol) {
            this.symbol = symbol;
            this.numShares = 0;
            this.totalCost = 0.00;
        }

When a constructor takes an object as an argument (such as the String symbol), it might make sense to check that argument's value to make sure it isn't null. One possible way to handle this case would be to throw an exception if a null symbol is passed when creating a Stock object. The following lines could be inserted at the start of the Stock's constructor:

        if (symbol == null) {
            throw new NullPointerException("null symbol");
        }

8.6.2 Stock methods

The StockManager client code recorded purchases of stock by calling the following method on a Stock object:

        currentStock.purchase(numShares, pricePerShare);

We can write a mutator method called purchase that accepts a number of shares and a price per share as arguments, and modifies the Stock object's state to record that purchase. Recording the purchase consists of adding the number of shares to our total, and adding the price paid for these shares (number of shares * price per share) to our total cost.

        // Records a purchase of the given number of shares of this stock
        // at the given price per share.
        public void purchase(int numShares, double pricePerShare) {
            this.numShares += numShares;
            this.totalCost += (numShares * pricePerShare);
        }

As with the constructor, it might make sense here to check the arguments passed in to make sure they are valid. In this case a valid number of shares and price per share would be ones that are non-negative. The following lines could be inserted at the start of our purchase method to perform this test:

if (numShares < 0 || pricePerShare < 0) { throw new IllegalArgumentException("negative shares or price"); }

After recording all the purchases, the StockManager prints the Stock object, which shows the symbol, total shares, and cost. This must mean that the Stock has a toString method. We'll leave implementing this as an exercise for you to complete.

At the end of the StockManager code, the profit or loss earned on the stock is printed by calling a getProfit method on the Stock object, passing in a current share price.

        double profitLoss = currentStock.getProfit(currentPrice);
        System.out.println("Your net profit/loss: $" + profitLoss);

The getProfit method computes how much our shares are worth today (the total number of shares purchased * the current price per share), and subtracts how much we paid for the shares (our total cost), and returns the result as our profit or loss.

        // Returns the total profit or loss earned on the shares of this stock,
        // based on the given price per share.
        public double getProfit(double currentPrice) {
            double marketValue = this.numShares * currentPrice;
            return marketValue - this.totalCost;
        }

As with the other methods, we probably shouldn't allow a negative current price per share, so the following code could be placed at the start of the getProfit method:

if (currentPrice < 0.0) { throw new IllegalArgumentException("negative current price"); }

8.6.3 Final version of Stock class

After all data fields, constructor, and methods of our Stock class are written, the class would look like this. The toString method is still missing and left for you to implement.

        // A Stock object represents purchases of shares of a particular stock.
        public class Stock {
            private String symbol;        // stock symbol, such as "YHOO"
            private int numShares;        // total number of shares purchased
            private double totalCost;     // total cost for all shares purchased
            
            // Creates a new Stock with the given symbol and no shares purchased.
            // Precondition: symbol != null
            public Stock(String symbol) {
                if (symbol == null) {
                    throw new IllegalArgumentException("null symbol");
                }
        
                this.symbol = symbol;
                this.numShares = 0;
                this.totalCost = 0.00;
            }
        
            // Returns the total profit or loss earned on the shares of this stock,
            // based on the given price per share.
            // Precondition: currentPrice >= 0.0
            public double getProfit(double currentPrice) {
                if (currentPrice < 0.0) {
                    throw new IllegalArgumentException("negative current price");
                }
        
                double marketValue = this.numShares * currentPrice;
                return marketValue - this.totalCost;
            }
        
            // Records a purchase of the given number of shares of this stock 
            // at the given price per share.
            // Precondition: numShares >= 0 && pricePerShare >= 0.00
            public void purchase(int numShares, double pricePerShare) {
                if (numShares < 0 || pricePerShare < 0.0) {
                    throw new IllegalArgumentException("negative shares or price");
                }
        
                this.numShares += numShares;
                this.totalCost += numShares * pricePerShare;
            }
        }

 


8.7 Chapter Summary

 

8.8 Self-Check Exercises

  1. Add a method named public double getDistance(Point other) to the Point class that returns the distance between the current Point object and the given other Point object. The distance between two points is equal to the square root of the squares of the differences of x and y coordinates. In other words, the distance between two points (x1, y1) and (x2, y2) can be expressed as the square root of (x2 - x1)2 + (y2 - y1)2 . Two points with the same (x, y) coordinates should return a distance of 0.0.
  2. Add the following method to your Point class:

    public double getDistanceFromOrigin(Point other)
    Returns the distance between the current Point object and the origin at (0, 0).

  3. Add the following method to your Point class:

    public int getManhattanDistance(Point other)
    Returns the "Manhattan distance" between the current Point object and the given other Point object. The Manhattan distance refers to how far apart two places are if the person can only travel straight horizontally or vertically, as though driving on the streets of Manhattan. In our case, the Manhattan distance is the sum of the absolute values of the differences in their coordinates; in other words, the difference in x plus the difference in y between the points.

  4. Write a modified version of the toString method on the Point class, such that its toString output matches the format of the toString from Java's Point objects. Here is an example:
            java.awt.Point[x=3,y=8]
  5. Add the following method to your Point class:

    public double getSlope(Point other)
    Returns the slope of the line drawn between this Point and the given other Point. Use the formula (y2 - y1) / (x2 - x1) to determine the slope between two points (x1, y1) and (x2, y2). Note that this formula fails for points with identical x coordinates, so throw an IllegalArgumentException in this case.

  6. Add the following method to your Point class:

    public boolean isColinear(Point p1, Point p2)
    Returns whether this Point is colinear with the given two other points. Points are colinear if a straight line can be drawn that connects them. Two basic examples are three points that have the same x or y coordinate. The more general case can be determined by calculating the slope of the line between each pair of points and seeing that this slope is the same for all pairs of points. Use the formula (y2 - y1) / (x2 - x1) to determine the slope between two points (x1, y1) and (x2, y2). (Note that this formula fails for points with identical x coordinates so this will have to be special-cased in your code.)

    Since Java's double type is imprecise, round all slope values to a reasonable accuracy such as 4 digits past the decimal point before you compare them.

  7. Add the following static method to your Point class:

    public static Point parsePoint(String str)
    Examines the given String and converts it into an appropriate Point object, which is returned. For example, Point.parsePoint("(-2, 3)") should return a new Point with x value of -2 and y value of 3. It should always be the case for any Point p that Point.parsePoint(p.toString()).equals(p) .

    If you have rewritten your toString method to match that of Java's Point class, make your parsePoint method use that format. For example, Point.parsePoint("java.awt.Point[x=-2, y=3]") should return a new Point with x value of -2 and y value of 3.

  8. Add the following methods to the Stock class provided:

    public String getSymbol()
    Returns this Stock's symbol value, such as "AMZN".

    public int getShares()
    Returns this Stock's number of shares value.

    public double getTotalCost()
    Returns this Stock's total cost value.

  9. Add the following method to the Stock class provided:

    public void clear()
    Resets this Stock's number of shares purchased and total cost to 0.

  10. Add the following method to the Stock class provided:

    public String toString()
    Returns a String representation of this Stock object, such as "AMZN ( 75 shares, $ 2716.00 total cost )".

    Note that you should properly format the amount of money so that it has the correct number of digits after the decimal point. 2716.00 is the expected value, not 2716.0.

  11. Add the following method to the Stock class provided:

    public boolean equals(Object o)
    Returns true if the given object o is a Stock object with the same symbol, number of shares purchased, and total cost as this one.

  12. Write a class named Line that represents a line segment between two Points. Your Line objects should have the following methods:

    public Line(Point p1, Point p2)
    Constructs a new Line that contains the given two Points.

    public Point getP1()
    Returns this Line's first endpoint.

    public Point getP2()
    Returns this Line's second endpoint.

    public String toString()
    Returns a String representation of this Line, such as "[(-2, 3), (4, 7)]".

  13. Add the following method to your Line class:

    public double getSlope()
    Returns the slope of this Line. The slope of a line between points (x1, y1) and (x2, y2) is equal to (y2 - y1) / (x2 - x1). If x2 == x1, the denominator is zero and the slope is undefined, so you may throw an Exception in this case.

  14. Add the following constructor to your Line class:

    public Line(int x1, int y1, int x2, int y2)
    Constructs a new Line that contains the given two Points.

  15. Add the following method to your Line class:

    public boolean equals(Object o)
    Returns whether the given other object o is a Line with the same endpoints as this Line.

  16. Add the following method to your Line class:

    public boolean isColinear(Point p)
    Returns true if the given point is colinear with the points of this Line; that is, if this Line stretched infinitely, would it hit the given Point? Points are colinear if a straight line can be drawn that connects them. Two basic examples are three points that have the same x or y coordinate. The more general case can be determined by calculating the slope of the line between each pair of points and seeing that this slope is the same for all pairs of points. Use the formula (y2 - y1) / (x2 - x1) to determine the slope between two points (x1, y1) and (x2, y2). (Note that this formula fails for points with identical x coordinates so this will have to be special-cased in your code.)

    Since Java's double type is imprecise, round all slope values to a reasonable accuracy such as 4 digits past the decimal point before you compare them.

  17. Write a class named Rectangle that represents a rectangular 2-dimensional region. Your Rectangle objects should have the following methods: public Rectangle(int x, int y, int width, int height)
    Constructs a new Rectangle whose top-left corner is specified by the given coordinates and with the given width and height. Throw an IllegalArgumentException on a negative width or height.

    public int getHeight()
    Returns this Rectangle's height.

    public int getWidth()
    Returns this Rectangle's width.

    public int getX()
    Returns this Rectangle's x coordinate.

    public int getY()
    Returns this Rectangle's y coordinate.

    public String toString()
    Returns a String representation of this Rectangle, such as "Rectangle[x=1,y=2,width=3,height=4]".

  18. Add the following method to your Rectangle class:

    public boolean equals(Object o)
    Returns whether the given other object o is a Rectangle with the same x/y coordinates, width, and height as this Rectangle.

  19. Add the following constructor to your Rectangle class:

    public Rectangle(Point p, int width, int height)
    Constructs a new Rectangle whose top-left corner is specified by the given Point and with the given width and height.

  20. Add the following methods to your Rectangle class:

    public boolean contains(int x, int y)
    public boolean contains(Point p)

    Returns whether the given Point or coordinates lie inside the bounds of this Rectangle.

  21. Add the following method to your Rectangle class:

    public Rectangle union(Rectangle rect)
    Returns a new Rectangle that represents the area occupied by the tightest bounding box that contains both this rectangle and the given other rectangle.

  22. Add the following method to your Rectangle class:

    public Rectangle intersection(Rectangle rect)
    Returns a new Rectangle that represents the largest rectanglar region completely contained within both this rectangle and the given other rectangle. If the rectangles do not intersect at all, returns a rectangle whose width and height are both 0.

8.9 Programming Problems

  1. Write a class named RationalNumber that represents a fraction with an integer numerator and denominator. A RationalNumber object should have the following methods:

    public RationalNumber(int numerator, int denominator)
    Constructs a new rational number to represent the ratio (numerator/denominator). The denominator cannot be 0, so throw an IllegalArgumentException if 0 is passed.

    public RationalNumber()
    Constructs a new rational number to represent the ratio (0/1).

    public boolean equals(Object o)
    Returns whether the given object o is a RationalNumber object with equal value to this one. Two rational numbers need not have identical numerator and denominator to be considered equal; ratios such as (3/5) and (6/10) have equal value, so they too should be considered equal.

    public int getDenominator()
    Returns this rational number's denominator value; for example, if the ratio is 3/5, returns 5.

    public int getNumerator()
    Returns this rational number's numerator value; for example, if the ratio is 3/5, returns 3.

    public String toString()
    Returns a String representation of this rational number, such as "3/5".

  2. Write a class named Date that represents a date consisting of a year, month, and day. A Date object should have the following methods:

    public Date(int year, int month, int day)
    Constructs a new Date to represent the given date.

    public void addDays(int days)
    Moves this Date object forward in time by the given number of days.

    public void addWeeks(int weeks)
    Moves this Date object forward in time by the given number of 7-day weeks.

    public int daysTo(Date other)
    Returns the number of days that this Date must be adjusted to make it equal to the given other Date.

    public boolean equals(Object o)
    Returns whether two Date objects represent the same calendar date.

    public int getDay()
    Returns this Date's day value; for example, for the date 2006/07/22, returns 22.

    public int getMonth()
    Returns this Date's month value; for example, for the date 2006/07/22, returns 7.

    public int getYear()
    Returns this Date's year value; for example, for the date 2006/07/22, returns 2006.

    public String toString()
    Returns a String representation of this Date in Year/Month/Day order, such as "2006/07/22".

  3. Write a class named GroceryList that represents a person's list of items to buy from the market, and another class named GroceryItemOrder that represents a request to purchase a particular item in a given quantity (example: 4 boxes of cookies).

    The GroceryList class should use an array field to store the grocery items, as well as keeping track of its size (number of items in the list so far). Assume that a grocery list will have no more than 10 items. A GroceryList object should have the following methods:

    public GroceryList()
    Constructs a new empty grocery list.

    public void add(GroceryItemOrder item)
    Adds the given item order to this list, if the list is not full (has fewer than 10 items).

    public double getTotalCost()
    Returns the total sum cost of all grocery item orders in this list.

    The GroceryItemOrder class should store an item quantity and price per unit. A GroceryItemOrder object should have the following methods:

    public GroceryItemOrder(String name, int quantity, double pricePerUnit)
    Constructs an item order to purchase the item with the given name, in the given quantity, which costs the given price per unit.

    public double getCost()
    Returns the total cost of this item in its given quantity. For example, 4 boxes of cookies that are 2.30 per unit have a cost of 9.20.

    public void setQuantity(int quantity)
    Sets this grocery item's quantity to be the given value.


Stuart Reges
Marty Stepp
Last modified: Tue Jan 3 12:51:10 PST 2006

Chapter 9
Inheritance, Polymorphism, Interfaces,
and Introductory Object-Oriented Design

Copyright © 2005 by Stuart Reges and Marty Stepp

9.1 Introduction

In this chapter we will explore two of the most important techniques provided by the Java language to write better structured solutions. Inheritance allows us to share code between classes to reduce redundancy, as well as letting us treat different types of objects in the same way. Interfaces allow us to treat several different types of objects the same way without sharing code.

9.2 Hierarchies of Types

Often we categorize objects in the real world starting with a general category and then becoming more specific. Let's think about human beings. What attributes does every person have? What does every person know how to do?

Let's say that each person knows his/her name, age, height and weight, and has the abilities of eating, sleeping, and talking. We could draw a diagram of a typical person who has some of these qualities. It might look something like the picture at right.

Within the broad group of all human beings, let's talk about a specific group: employees (people with jobs). What new attributes and abilities do employees have? Let's assume that every employee is a person, and draw them as a subcategory of persons, like this:

Employees add some new abilities and attributes that other persons may not have. An employee has a salary and a company where he/she works. An employee also has a new skill--the ability to do work. Employees might perform some of their tasks differently than other people. For example, perhaps an employee sleeps on the job rather than at home.

We can add more subcategories of employees. For example, some employees are lawyers, and some are college professors. Some are secretaries; and of the secretaries, some are legal secretaries. Let's assume for simplicity that no one person falls into more than one category. We have more is-a relationships: every doctor, secretary, legal secretary, and college professor is an employee (and is a person).

Each category retains the abilities of the ones above it, but may add more or redefine how it performs existing abilites. For example, let's say that the Secretary and Lawyer change how they eat, the CollegeProfessor and Lawyer change how they write their name, and the CollegeProfessor changes how he/she works.

What we've done in this section is to create a group of related categories. We could call it a type hierarchy. The analogous construct in a programming language like Java is called an inheritance hierarchy.

9.3 Programming with Inheritance

Now that we've learned how to create new types of objects in Java, we could represent each of the categories above as classes. Here are initial attempts at classes for some of the categories named above:

        public class Person {
            public void eat() {
                System.out.println("Yum!");
            }
        
            public void sleep() {
                System.out.println("Zzzzz...");
            }
        
            public void talk() {
                System.out.println("Hello.");
            }
        
            public String toString() {
                return "Person";
            }
        }
        public class Employee {
            public void eat() {
                System.out.println("Yum!");
            }
        
            public void sleep() {
                System.out.println("Zzzz... Hardly working...");
            }
        
            public void talk() {
                System.out.println("Hello.");
            }
        
            public String toString() {
                return "Person";
            }
        
            public void work() {
                System.out.println("Workin' for the man.");
            }       
        }

The two classes are quite redundant, because they contain many of the same methods with identical behavior. In particular, the eat, talk, and toString methods are identical. We would notice further redundancy if we wrote classes for the other categories such as CollegeProfessor or LegalSecretary.

Java provides a mechanism called inheritance that can help us remove redundancy between similar classes of objects. Inheritance allows the programmer to specify a parent-child relationship between two classes.

Inheritance

A parent-child relationship between two classes, where objects of the child class receive their own copy of the data fields and methods of the parent class.

The child type, or subclass, receives all of the state and behavior of its parent type, or superclass. The child can then add existing state and behavior of its own, or it can replace its inherited behavior with new behavior as needed. A Java class can have only one parent; it is not possible to extend more than one class. This is called single inheritance.

Superclass or Base Class

The parent in an inheritance relationship. A class can only have one superclass.

Subclass or Derived Class

The child in an inheritance relationship. One parent may have many children.

The general syntax to specify one class as the child (subclass) of another is the following:

public class <name> extends <name> { ... }

In our example with categories of Persons, we could rewrite the Employee class to extend the Person class.

        public class Employee extends Person {

        }

By doing this, we're saying that each Employee object is a person. So therefore, despite the fact that there's no code inside the Employee class, every Employee object automatically receives an eat, sleep, talk, and toString method from the Person class. We don't need to write these methods in the Employee class any more, because they've become inherited instead. The redundancy between the two classes has been eliminated.

We said previously that employees add an ability not seen in other persons: the ability to work. We can add this to our previously empty Employee class. It's legal for a subclass to add new behavior that wasn't present in the superclass.

        public class Employee extends Person {
            public void work() {
                System.out.println("Workin' for the man.");
            }
        }

The following testing code would work with our new Employee class:

        public static void main(String[] args) {
            Person p = new Person();
            Employee empl = new Employee();
        
            System.out.println(p);
            p.eat();
            p.sleep();
            p.talk();
            System.out.println();
            
            System.out.println(empl);
            empl.eat();
            empl.sleep();
            empl.talk();
        }

The code would produce the following output, because Employee now behaves identically to Person:

        Person
        Yum!
        Zzzzz...
        Hello.
        
        Person
        Yum!
        Zzzzz...
        Hello.

9.3.1 Overriding Methods

One flaw with our new inheritance-enhanced version of Employee is that it doesn't perform the correct sleep behavior. We said in the previous section that employees sleep on the job, not in the standard places like normal persons. Our Employee type has inherited the version of the sleep method from Person, which doesn't do what we want. It's legal for us to replace it by writing a new version of sleep in Employee, which will replace the one inherited from Person. This idea of replacing behavior from the superclass is called overriding.

Override

Implementing a new version of a method inherited from a superclass, replacing the superclass's version.

Overriding requires no special syntax; just write the method you want to replace in the subclass. Here is a new version of Employee that overrides the sleep method.

        public class Employee extends Person {
            public void sleep() {
                System.out.println("Working hard or hardly working?");
            }
            
            public void work() {
                System.out.println("Workin' for the man.");
            }
        }

After our override has been made, the output from the previous testing code changes to the following, as desired:

        Person
        Yum!
        Zzzzz...
        Hello.
        
        Person
        Yum!
        Working hard or hardly working?
        Hello.

Now that we know how to override, we could implement some of the other categories correctly, such as the Secretary:

        public class Secretary extends Employee {
            public void eat() {
                System.out.println("Eat at my desk");
            }
        }

9.3.2 Polymorphism

One very interesting and odd thing about inherited types is that there is some compatibility in what variables can refer to what objects. It is legal for a variable of a superclass type to refer to an object of one of its subclass types. For example, the following is a legal assignment statement:
        Person p2 = new Employee();

You've already seen cases where one type can store a value of another type, with primitive values. For example, an int value can be stored into a double variable. In those cases, the int value was automatically converted into a double when it was assigned.

It doesn't work the same way with inherited types as you may expect from primitive types. When the subclass object is stored into the superclass variable, no conversion occurs. The object referred to by p2 really is an Employee object, not a Person object. If we call methods on it, it will behave like an Employee object. For example, the call:

        p2.sleep();

produces the following output:

        Working hard or hardly working?

This may not seem like a very important concept, but it is one of the most crucially important ideas in object-oriented programming. The ability for a variable to refer to a subclass's object allows us to write flexible code that can interact with many types of objects in the same way. For example, we can write a method that accepts a Person as a parameter, or returns a Person, or create an array of Person objects--and in any of these cases, we can substitute an Employee, Secretary, CollegeProfessor, or other subclass object and the code will still work. Even more importantly, the code will actually behave differently depending on which type of object is used, because each subclass overrides and changes some of the behavior from the superclass.

This important ability for the same code to be used with several different types of objects is called polymorphism.

Polymorphism

The ability for the same code to be used with several different types of objects, possibly behaving differently depending on the type of objects used.

Here is an example test file that uses Person, Employee, and Secretary objects polymorphically as parameters to a method.

        public class TestEmployee3 {
            public static void main(String[] args) {
                Person p = new Person();
                Employee empl = new Employee();
                Secretary sec = new Secretary();
                printInfo(p);
                printInfo(empl);
                printInfo(sec);
            }
        
            public static void printInfo(Person pers) {
                System.out.println(pers);
                pers.eat();
                pers.sleep();
                pers.talk();
                System.out.println();
            }
        }

The program produces the following output:

        Person
        Yum!
        Zzzzz...
        Hello.
        
        Person
        Yum!
        Working hard or hardly working?
        Hello.
        
        Person
        Eat at my desk
        Working hard or hardly working?
        Hello.

To shorten the code even further, we could even store the people into an array. It's legal for an Employee, Secretary, or any other subclass of Person to reside as an element of a Person[]. The following program produces the same output as the preceding one:

        public class TestEmployee4 {
            public static void main(String[] args) {
                Person[] people = {new Person(), new Employee(), new Secretary()};
                for (int i = 0; i < people.length; i++) {
                    printInfo(people[i]);
                }
            }
        
            public static void printInfo(Person pers) {
                System.out.println(pers);
                pers.eat();
                pers.sleep();
                pers.talk();
                System.out.println();
            }
        }

9.4 Inheritance and Polymorphism Problems

One useful way to get the hang of how inheritance works is to perform exercises where we have to interpret the behavior of programs that use inheritance and polymorphism. Let's define several classes that have inheritance relationships, along with various methods in the superclass that are overridden in some of the subclasses. Then we'll declare an array of variables of the superclass type and fill it with objects of the various subclass types. When we call methods on the elements of our array, we should observe polymorphic behavior.

Assume that the following classes have been defined. Admittedly, their names and methods are nonsense, but the key is to use them to examine inheritance and polymorphism behavior.

        public class Foo {
            public void method1() {
                System.out.println("foo 1");
            }
        
            public void method2() {
                System.out.println("foo 2");
            }
        
            public String toString() {
                return "foo";
            }
        }
        public class Bar extends Foo {
            public void method2() {
                System.out.println("bar 2");
            }
        }
        public class Baz extends Foo {
            public void method1() {
                System.out.println("baz 1");
            }
        
            public String toString() {
                return "baz";
            }
        }
        public class Mumble extends Baz {
            public void method2() {
                System.out.println("mumble 2");
            }
        }

Consider the following code fragment, which uses the above classes:

        public static void main(String[] args) {
            Foo[] elements = {new Foo(), new Bar(), new Baz(), new Mumble()};
            for (int i = 0; i < elements.length; i++) {
                System.out.println(elements[i]);
                elements[i].method1();
                elements[i].method2();
                System.out.println();
            }
        }

How do we go about solving such a problem, and determining its correct output? We must determine what happens when each element is printed (when its toString method is called), and when its method1 and method2 methods are called. One way to get started is to draw a diagram like those seen in previous sections. We'll draw each type as a box, listing its methods in the box, and connecting subclasses to their superclasses by arrows. (This is a simple variation of a standard OOP diagram called a UML Class Diagram.)

Now, let's enhance our diagram by writing the methods' output next to their names. Also, let's not only write the methods defined in each class, but also the ones that the class inherits.

We'll start by filling in the methods from the Foo class with their output. When someone calls method1 on a Foo object, the resulting output is "foo 1". When someone calls method2 on a Foo object, the resulting output is "foo 2". When someone prints a Foo object with toString, the resulting output is "foo". Let's fill them in on our diagram:

Next let's look at the Bar type. Bar inherits all the behavior from Foo, except that it overrides the method2 output to say "bar 2". So that means we can fill in the Bar column of our table identically to the Foo column except that we'll replace "foo 2" with "bar 2":

Third, let's look at the Baz type. Baz inherits all the behavior from Foo, except that it overrides the method1 output to say "baz 1" and overrides the toString method to say "baz". So that means we can fill in the Baz column of our table identically to the Foo column except that we'll replace "foo 1" with "baz 1" and "foo" with "baz":

Lastly, let's look at the Mumble type. Mumble inherits all the behavior from Baz, except that it overrides the method2 output to say "mumble 2". Following our pattern, we'll get this final table:

Another way to do this is to make a table of behavior for each type, for each of its methods. List each type horizontally and each method name vertically:

method nameFooBarBazMumble
method1foo 1foo 1baz 1baz 1
method2foo 2bar 2foo 2mumble 2
toStringfoofoobazbaz

Now that we've created a diagram or table, we can figure out the output of the previous code. The array contains a Foo object, a Bar object, a Baz object, then a Mumble object. On each of these it prints the toString output, then calls method1, then method2, then a blank line. When a method gets called on an object, we can just look up the output of that method for that type on our diagram or table. We can see that the output for these methods on a Foo object (element 0) is the following:

        foo
        foo 1
        foo 2

Using the table in this way for all 4 types, we get the following complete output for the exercise:

        foo
        foo 1
        foo 2
        
        foo
        foo 1
        bar 2
        
        baz
        baz 1
        foo 2
        
        baz
        baz 1
        mumble 2

9.5 Overriding and the super Keyword

There are some details of inheritance that we haven't covered yet. The classes in the previous sections only had methods, not fields or constructors. Let's examine some more complex classes that use inheritance and learn a new keyword that helps us implement them effectively.

9.5.1 Private Data Fields and Inheritance

In the last chapter, we built a Stock class representing purchased shares of a given stock. Here's the code for that class, shortened a bit for this section, and with a getNumShares method added to return the stock owner's number of shares:

        public class Stock {
            private String symbol;        // stock symbol, such as "YHOO"
            private int numShares;        // total number of shares purchased
            private double totalCost;     // total cost for all shares purchased
        
            public Stock(String symbol) {
                this.symbol = symbol;
                this.numShares = 0;
                this.totalCost = 0.00;
            }
            
            public int getNumShares() {
                return this.numShares;
            }
        
            public double getProfit(double currentPrice) {
                double marketValue = this.numShares * currentPrice;
                return marketValue - this.totalCost;
            }
        
            public void purchase(int numShares, double pricePerShare) {
                this.numShares += numShares;
                this.totalCost += numShares * pricePerShare;
            }
        }

Now let's imagine that we want to create a type of objects for stocks that pay dividends. Dividends are payments made by a corporation to its shareholders. The amount the shareholder receives is based on the number of shares they own. Not every stock pays dividends, so we wouldn't want to modify our Stock class to add this functionality; instead, we'll create a new type of objects. We could call this type DividendStock.

Every DividendStock object will be very similar to a Stock object, but it will add certain features, so we could extend Stock to write our DividendStock class. Each DividendStock object will inherit the symbol, number of shares, and total cost data from the Stock superclass. We'll add a data field to record the amount of dividends paid.

        public class DividendStock extends Stock {
            private double dividends;   // amount of dividends paid

Using the dividends data field, we can write a method in the DividendStock that lets the shareholder receive a per-share dividend. But the following code won't compile:

        // This code does not compile!
        public void payDividend(double amountPerShare) {
            this.dividends += amountPerShare * this.numShares;
        }

You'll get a compiler error like the following:

        DividendStock.java:17: numShares has private access in Stock
                this.dividends += amountPerShare * this.numShares;
                                                   ^
        1 error

The problem is that DividendStock is trying to access the numShares data field, which is declared as private in Stock. A subclass may not refer directly to any private data fields that were declared in its superclass. Instead, we must use the accessor or mutator methods associatied with those data fields to access or change their values. Here is a corrected version of the payDividend method:

        public void payDividend(double amountPerShare) {
            this.dividends += amountPerShare * this.getNumShares();
        }
Common Syntax Error: Trying to access a private data field from a subclass

In a subclass, access or modify the superclass's data field using a method instead of referring to it directly.

9.5.2 Calling a Superclass's Constructor with super

The issue with accessing private data fields will arise again when we write a constructor for DividendStock. Constructors (unlike methods) are not inherited, so we'll have to write our own constructor for the DividendStock class. We'd like it to accept the same parameter as the Stock constructor: the symbol. We want it to have the same behavior as the Stock constructor, but additionally we want to set the dividends field to 0.00.

The following implementation for the constructor won't compile:

        // This constructor does not compile!
        public DividendStock(String symbol) {
            this.symbol = symbol;
            this.numShares = 0;
            this.totalCost = 0.00;
            this.dividends = 0.00;  // this line is the new code
        }

The compiler produces error messages like the following:

        DividendStock.java:5: cannot find symbol
        symbol  : constructor Stock()
        location: class Stock
            public DividendStock(String symbol) {
                                                ^
        DividendStock.java:6: symbol has private access in Stock
                this.symbol = symbol;
                ^
        DividendStock.java:7: numShares has private access in Stock
                this.numShares = 0;
                ^
        DividendStock.java:8: totalCost has private access in Stock
                this.totalCost = 0.00;
                ^
        4 errors

Again, the problem is that we cannot directly access the superclass's data fields, because they are private. If there were mutator methods in the Stock class named setSymbol, setNumShares, and setTotalCost, we could call them to set the data fields' values.

Even in the absence of mutator methods for these data fields, there is still a way to initialize them properly: DividendStock's constructor can call Stock's constructor. Even though DividendStock doesn't inherit the constructor, the subclass constructor itself can still call a constructor from the superclass if it needs to do so.

To call a constructor from a superclass, write the keyword super, followed by the parameter values in parentheses.

super(<parameter(s)>);

In the case of our DividendStock constructor, the code looks like this:

        public DividendStock(String symbol) {
            super(symbol);          // call Stock constructor
            this.dividends = 0.00;
        }

It's probably a good idea to write accessor methods for the other fields in the superclass, such as getSymbol and getTotalCost, in case the DividendStock subclass or other classes need to use their values.

We need to know about one more usage of the super keyword before our DividendStock can be finished, so we'll come back to it in a moment.

9.5.3 Calling Overridden Methods with super

The super keyword was used in the previous section to invoke a superclass's constructor. However, it can also be used when overriding a method, to call the superclass's version of that same method.

Let's consider the example of determining the current profit of a DividendStock. The current profit of a DividendStock is equal to the market value (number of shares times current price) minus the total cost paid for the shares, plus the amount of dividends paid. In other words, it's the profit of the regular Stock plus the dividends.

Because the profit of a DividendStock is computed differently than that of a regular Stock, we override the getProfit method in the DividendStock class to implement this new functionality. An incorrect initial attempt might look something like this:

        // First attempt; this code does not compile,
        // because it accesses fields directly.
        // Also does not take advantage of inheritance.
        public double getProfit(double currentPrice) {
            double marketValue = this.numShares * currentPrice;
            return marketValue - this.totalCost + this.dividends;
        }

The preceding code has two problems. For one, we cannot refer directly to the various data fields that were declared in Stock. We could add accessor 'get' methods for each data field to get around this. The second problem is that the code is redundant: it duplicates much of the functionality from Stock's getProfit method shown earlier. The only new part is the adding of this.dividends into the total.

To remove this redundancy, we can have DividendStock's getProfit method call Stock's getProfit method as part of its computation. However, since the two methods share the same name, we must disambiguate them by explicitly telling the compiler we wish to call Stock's version of the getProfit method. We do this using super keyword, which we also used in the previous section to call a superclass's constructor.

Here is the corrected code, which does compile and eliminates the previous redundancy:

        public double getProfit(double currentPrice) {
            return super.getProfit(currentPrice) + this.dividends;
        }

The following shorter body would also have been correct, replacing the two preceding lines in getProfit's body:

            return super.getProfit(currentPrice) + this.dividends;

The general syntax for calling an overridden method using the super keyword is:

super.<method name>(<parameter(s)>)

Here is the completed DividendStock class built in the preceding sections:

        // Represents a stock purchase that also pays dividends to the shareholder.
        public class DividendStock extends Stock {
            private double dividends;  // amount of dividends paid
        
            public DividendStock(String symbol) {
                super(symbol);          // call Stock constructor
                this.dividends = 0.00;
            }
            
            public double getProfit(double currentPrice) {
                return super.getProfit(currentPrice) + this.dividends;
            }
        
            public void payDividend(double amountPerShare) {
                this.dividends += amountPerShare * this.getNumShares();
            }
        }

9.5.4 Another Example: Point3D

Let's consider another example of extending a class where super is useful. Imagine that we'd like to write a program that deals with points in 3-dimensional space. We already have our Point class, representing a point in 2D space. We can extend Point and write a Point3D class that adds a z-coordinate.

Point3D's constructor wants to set the x and y fields, just as the Point constructor did. But Point3D adds a z field, which must also be set. Calling super(x, y) sets the first two fields, and the third is set separately.

        // A Point3D object represents an ordered triple of 2D coordinates (x, y, z).
        public class Point3D extends Point {
            private int z;
            
            public Point3D(int x, int y, int z) {
                super(x, y);
                this.z = z;
            }
            
            // Returns the z-coordinate of this Point.
            public int getZ() {
                return this.z;
            }
        }

Now let's consider the task of writing an equals method for the Point3D class. Two Point3D objects are equal if they have the same x, y, and z coordinates. The following is one working implementation of equals that is correct, but stylistically lacking.

        public boolean equals(Object o) {
            if (o == null || this.getClass() != o.getClass()) {
                return false;
            }

            Point3D other = ((Point3D) o);
            return this.getX() == other.getX() &&
                   this.getY() == other.getY() &&
                   this.getZ() == other.getZ();
        }

The problem with this code is that it repeats all of the code from Point's equals method and adds little more (only a test for equal z-coordinates). Luckily, we can have Point3D's version of equals call Point's version of the same method. However, we cannot simply write equals(o) to call Point's equals method, because we have overridden equals in Point3D, so Point3D's version would be called instead. To call the overridden equals from Point, we must use the super keyword.

Knowing this, our Point3D's equals method must only do two things: call Point's equals and make sure it returns true, and compare the z-coordinates. The following code achieves this concisely:

        public boolean equals(Object o) {
            return super.equals(o) && this.z == ((Point3D) o).z;
        }

One example where we would not need to use super is if Point3D wanted to add a translate method that accepted dx, dy, and dz as parameters. In this case, we'd like to call the superclass's version of translate to adjust the x- and y-coordinates, then adjust the z-coordinate ourselves. But our new translate method hasn't really overridden the one from Point--it has overloaded it, creating a second version of the method, with different parameters. We wouldn't need to say super.translate here; saying this.translate would suffice.

        public void translate(int dx, int dy, int dz) {
            this.translate(dx, dy);
            this.z += dz;
        }

To summarize, you use the super keyword to call methods that you have overridden in your subclass, but you do not need to use the super keyword to call methods from the subclass that have not been overridden.

9.6 Interfaces

Imagine that we are creating classes to represent many different types of shapes, such as rectangles, squares, circles, and triangles. Based on what we know about inheritance, we might be tempted to use inheritance with these shape classes, because they seem to all be subcategories of the general class of 'shapes.' All shapes have an area and perimeter, for example.

But the situation is a bit different here, because each shape computes its area and perimeter in a different way, as listed below:
shapeareaperimeter
Rectanglelw2l + 2w
Circleπr22πr
Triangle(1/2)bhside1 + side2 + side3

We'd still like to create a supertype and subtype, 'is-a' relationship here, but the code-sharing aspect isn't useful in this case. Also, it would not make sense to create a superclass named Shape, because there's no such actual shape as a 'Shape' -- every shape must be one of the types we named, or some other polygon. The existence of a 'Shape' object would be a bit like asking someone, "What kind of car do you drive?" and having them answer, "Just a car." It wouldn't make sense.

Java has a construct called an interface that can represent a common supertype between several classes, without code sharing. Think of an interface as a set of qualifications that can be imposed on a class, or more specifically, a set of methods that classes can promise to implement.

Interface

A set of methods that classes can promise to implement, allowing those classes to be treated similarly in your code.

In the case of this shape example, we could write an interface to represent the common functionality of all shapes--the ability to ask for their area and perimeter. Then the various shape classes can specify that they contain all methods of Shape (that they 'implement' the interface).

The syntax for declaring an interface is the following. We write the methods' headers, but instead of method bodies with { }, we simply put a semicolon. This signifies that we aren't going to specify how the methods are implemented, only that they must be present in any class that wants to be considered a Shape. An interface should be declared in its own .java file, with the same name as the interface's name:

public interface <name> { public <type> <name>(<type> <name>, ..., <type> <name>); public <type> <name>(<type> <name>, ..., <type> <name>); ... public <type> <name>(<type> <name>, ..., <type> <name>); }

An interface for shapes could look like this.

        public interface Shape {
            public double getArea();
            public double getPerimeter();
        }

An interface doesn't actually define a new type of objects; if we tried to create a new Shape(), it would not compile. An interface only enumerates constraints that can be placed on other types. If we want to connect a class to our interface with an 'is-a' relationship, we must specify that that class 'implements' the interface. The general syntax for implementing an interface in a class is the following:

public class <name> implements <type> { ... }

In our various shape classes, we modify their headers to indicate that they implement all of the methods in the Shape interface. The files Rectangle.java, Triangle.java, and Circle.java might begin like this:

        public class Rectangle implements Shape {
            ...

        public class Triangle implements Shape {
            ...

        public class Circle implements Shape {
            ...

After our modifications, one might draw the type hierarchy like this:

If a class claims to be a Shape but does not have a suitable getArea or getPerimeter method, it will not compile:

        public class EmptyClass implements Shape {
            // How empty I am!  I don't really implement the interface.
        }

The compiler would give errors like the following when trying to compile EmptyClass.java:

        EmptyClass.java:1: EmptyClass is not abstract and does not override abstract method getPerimeter() in Shape
        public class EmptyClass implements Shape {
               ^
        1 error

The benefit of interfaces is that we can achieve polymorphism with them. We can now create an array of Shapes, pass Shapes as parameters to methods, return Shapes from methods, and so on.

        public class UseShapes {
            public static void main(String[] args) {
                Shape[] shapes = new Shape[3];
                shapes[0] = new Rectangle(10, 20, 15, 15);
                shapes[1] = new Triangle(new Point(60, 10), new Point(60, 40), new Point(100, 10));
                shapes[2] = new Circle(new Point(400, 50), 40);
                
                for (int i = 0; i < shapes.length; i++) {
                    printShape(shapes[i]);
                }
            }
            
            public static void printShape(Shape shape) {
                System.out.print(shape);
                System.out.print(", area=" + shape.getArea());
                System.out.println(", perimeter=" + shape.getPerimeter());
            }
        }

This program produces the following output:

        Rectangle@11b86e7, area=225.0, perimeter=60.0
        Triangle@35ce36, area=600.0, perimeter=120.0
        Circle@757aef, area=5026.548245743669, perimeter=251.32741228718345

It may seem odd that we can have interface variables, arrays, and parameters when it isn't possible to construct an object of an interface type. It simply means that any object of a type that implements that interface may be used. In our case, any type that implements Shape (such as Circle, Rectangle, or Triangle) may be used.

9.7 Case Study: Designing a Hierarchy of Financial Classes

As you write larger and more complex programs, you will end up with more classes and more opportunities to use inheritance and interfaces. It is important to get practice devising sensible hierarchies of types, so that you will be able to solve large problems by breaking them down into good classes in the future.

When designing an object-oriented system, you should ask yourself the following questions:

Having good answers to the above questions, along with a good knowledge of the necessary Java syntax, is a good start toward designing an object-oriented system.

Object-Oriented Design (OOD)

Modelling a program or system as a collection of cooperating objects and individual objects, which are treated as instances of classes within class hierarchies.

Let's consider the problem of gathering a user's financial investments. We already have explored a Stock example in the last chapter. There are also stocks that pay dividends, which our Stock class didn't handle. Stocks are not the only type of financial asset that investors might have in their financial portfolios. Other investments might include mutual funds, real estate, or cash.

How should we design this system? What new types of objects should we write? Take a moment to consider it yourself. We'll discuss an example design next.

It seems likely that each type of asset deserves its own class. We have the Stock class from the last chapter. We can add classes like MutualFund and Cash. Each object of each of these types will represent a single investment of that type: for example, a MutualFund object will represent a purchase of a mutual fund, and a Cash object will represent a sum of money in the user's portfolio. Also, since some (but not all) stocks pay dividends back to the stockholder, let's give those their own type named DividendStock.

What data and behavior is necessary in each of these types of objects? Take a moment to consider it.

In terms of behavior, though each type of assets is unique, the types do have some common behavior. Each asset should be able to compute its current market value and profit, if any. These values are performed in different ways for different asset types. A stock's market value is the total number of shares purchased times the current price per share. Real estate's market value might depend on the current interest rate. Cash is always worth exactly its own amount.

In terms of data, we decided in the previous chapter that a Stock object should store the stock's symbol, the number of shares, the total cost paid for all shares, and the current price of the stock. Dividend stocks also need to store an amount of dividends paid. A MutualFund object stores the same, but mutual funds can hold partial shares. Cash only needs to store its amount.

We might update our diagram of types to reflect the preceding data and behavior:

Are the asset types related? It seems so. Perhaps we'd want to gather and store a person's portfolio of assets in an array. It would be convenient to be able to treat any asset the same way, insofar as they share similar functionality. For example, every asset has a market value, so it would be nice to compute the sum market value of all assets in an investor's portfolio array, without worrying about exactly which type of asset it is.

One hurdle is that different assets compute their market value in different ways. Stocks' values are based on the current share price, while the value of cash depends only on how much cash the person has. This implies that we should use an interface to represent the notion of an asset, and have every class previously named implement the asset interface. Our interface will demand that all assets have methods to get the market value and profit. The interface is a way of saying, "classes that want to consider themselves assets must have a getMarketValue and getProfit method." Our interface for financial assets would be saved into a file named Asset.java and might look like this:

        // This interface represents financial assets that investors hold.
        public interface Asset {
            public double getMarketValue();
            public double getProfit();
        }

We'll have our various classes 'certify' that they are Assets by making them implement the Asset interface. For example, let's look at the Cash class. We didn't write a getProfit method in our previous diagram of Cash, because cash doesn't really change value and therefore doesn't have a profit. But we can just write a getProfit for Cash that returns 0.0 to indicate no profit.

        // Represents an amount of money held by an investor.
        public class Cash implements Asset {
            private double amount;   // amount of money held
            
            public Cash(double amount) {
                this.amount = amount;
            }
            
            public double getMarketValue() {
                return this.amount;
            }
            
            // Since cash is a fixed asset, it never has any profit or loss.
            public double getProfit() {
                return 0.00;
            }
            
            public void setAmount(double amount) {
                this.amount = amount;
            }
        }

As discussed earlier in the chapter, a DividendStock is very similar to a normal Stock, only with a small amount of behavior added. Let's display DividendStock as a subclass of Stock through inheritance, so that code won't be redundant.

Our type hierarchy would now look like this:

What about the similarity between mutual funds and stocks? They both store assets based on shares, with a symbol, total cost, and current price. It wouldn't work very well to make one of them the subclass of the other, because the type of shares (integer or real number) isn't the same, and also because it's not a sensible 'is-a' relationship: stocks aren't really mutual funds and vice versa.

Let's try a different alternative by making a new common superclass named ShareAsset, representing any asset that has shares, that contains the common behavior of Stock and MutualFund. Then we can have both Stock and MutualFund extend ShareAsset, to reduce redundancy. In order to implement the parameterless getMarketValue method, we'll add the current price as a data field, along with methods to get and set its value.

Here's some potential code for the ShareAsset class:

        // Represents a general asset that has a symbol and holds shares.
        public class ShareAsset {
            private String symbol;
            private double totalCost;
            private double currentPrice;
        
            public ShareAsset(String symbol, double totalCost, double currentPrice) {
                this.symbol = symbol;
                this.totalCost = totalCost;
                this.currentPrice = currentPrice;
            }
        
            public void addCost(double cost) {
                this.totalCost += cost;
            }
        
            public double getCurrentPrice() {
                return this.currentPrice;
            }
        
            public double getTotalCost() {
                return this.totalCost;
            }
        
            public void setCurrentPrice(double currentPrice) {
                this.currentPrice = currentPrice;
            }
        }

We stole code from last chapter's Stock class, but to fit this interface, the code underwent a few changes. Our old Stock code asked for the current share price as an argument to its getProfit method. Since the getProfit method may not accept any arguments if we wish to implement the interface, we'll instead store the current share price as a data field in the ShareAsset object. We supply a setCurrentPrice mutator method that can be called to set its proper value. We also give a constructor that can initialize with any number of shares and total cost.

One last modification we made to ShareAsset was to include an addCost method, which we'll use to add a given amount to the asset's total cost. We will need this because purchases on Stocks and MutualFunds need to update the totalCost data field, but they cannot do so directly since it is private.

The Stock and MutualFund classes can now extend ShareAsset to implement the remaining functionality:

        // A Stock object represents purchases of shares of a particular stock.
        public class Stock extends ShareAsset implements Asset {
            private int numShares;
            
            public Stock(String symbol, double currentPrice) {
                super(symbol, 0.0, currentPrice);  // call Asset c'tor
                this.numShares = 0;
            }
            
            public double getMarketValue() {
                return this.numShares * this.getCurrentPrice();
            }
            
            public int getNumShares() {
                return this.numShares;
            }
            
            public double getProfit() {
                return this.getMarketValue() - this.getTotalCost();
            }
        
            public void recordPurchase(int numShares, double pricePerShare) {
                this.numShares += numShares;
                addCost(numShares * pricePerShare);
            }
        }
        // Represents a mutual fund asset.
        public class MutualFund extends ShareAsset implements Asset {
            private double numShares;
            
            public MutualFund(String symbol, double currentPrice) {
                super(symbol, 0.0, currentPrice);  // call Asset c'tor
                this.numShares = 0.0;
            }
            
            public double getMarketValue() {
                return this.numShares * this.getCurrentPrice();
            }
            
            public double getProfit() {
                return this.getMarketValue() - this.getTotalCost();
            }
        
            public void recordPurchase(double numShares, double pricePerShare) {
                this.numShares += numShares;
                addCost(numShares * pricePerShare);
            }
        }

The DividendStock simply adds an amount of dividend payments to a normal Stock, which affects its market value. The DividendStock class adds the necessary functionality:

        // A DividendStock object models shares of stocks with dividend payments.
        public class DividendStock extends Stock {
            private double dividends;   // amount of dividends paid
        
            public DividendStock(String symbol, double currentPrice) {
                super(symbol, currentPrice);  // call Stock constructor
                this.dividends = 0.00;
            }
        
            public double getMarketValue() {
                return super.getMarketValue() + this.dividends;
            }
        
            public void payDividend(double amountPerShare) {
                this.dividends += amountPerShare * this.getNumShares();
            }
        }

You might think that we should also override the getProfit method to adjust for the dividends. However, interestingly we don't need to do this. Recall the implementation of getProfit from the Stock class:

        public double getProfit() {
            return this.getMarketValue() - this.getTotalCost();
        }

Notice how getProfit calls getMarketValue. We overrode getMarketValue, and because of polymorphism, calling getProfit on a DividendStock will use our new version of the getMarketValue method. Therefore getProfit already works the way we want, and we don't need to override it.

9.7.1 Abstract classes

If inheritance lets us specify classes as parents and children, interfaces may be like distant cousins: related, but without many details in common. There are situations where we want more of a sibling relationship--classes with common method names, like we get with interfaces, but also with some code shared between them, like we get with inheritance.

One thing you may notice about the Stock and MutualFund code in the last section is that there is still a lot of redundancy. For example, the getMarketValue and getProfit methods, while they have identical code, can't be moved up into the ShareAsset superclass because they depend on the number of shares, which is different in each child class. We'd like to get rid of the redundancy somehow.

There is another problem. A ShareAsset isn't really a type of asset that a person can buy; it's just a fictional idea created in our code. It would be undesirable if a person actually tried to construct a ShareAsset object--we only wrote that class to eliminate redundancy, not to be used directly.

If we want to create classes that shouldn't be used directly by outside code, but should only serve as a superclass for inheritance, we can call the class abstract. Writing abstract in the class's header will specify that a class is abstract and cannot be used directly to construct objects.

        public abstract class ShareAsset implements Asset {
            ...
        }

An attempt to create a ShareAsset object will produce a compiler error such as the following, if ShareAsset was declared to be abstract:

        C:\document\StockManager.java:55: ShareAsset is abstract; cannot be instantiated
              ShareAsset asset = new ShareAsset("MSFT", 20, 27.46);
              ^
        1 error
Abstract Class

A Java class that serves only as a superclass for other classes, but cannot actually be used to create objects of its type. Abstract classes capture common code to avoid redundancy, without introducing a new instantiable type into a system.

A interesting thing about abstract classes is that they can claim to implement an interface without actually writing all of the methods in that interface. This 'passes the buck' to any subclass that extends the abstract class, requiring the subclass to finish writing the remaining methods.

A strange benefit of this is that the abstract class can actually call any of the interface's methods on itself, even if they don't exist in that file, because it can count on its subclasses to write the remaining methods. For example, if we make ShareAsset abstract and declare that it implements Asset, the header looks like the following:

        public abstract class ShareAsset implements Asset {

Now that ShareAsset is an Asset, we can move the common redundant getProfit code up to ShareAsset and out of Stock and MutualFund. It can now call getMarketValue, even though that method isn't present in ShareAsset.

        // This method calls a nonexistent getMarketValue method
        public double getProfit() {
            return this.getMarketValue() - this.getTotalCost();
        }

If necessary, abstract classes can also behave like interfaces and can demand that certain methods be implemented by all of their subclasses. Such required subclass behaviors are called abstract methods. If we hadn't had the Asset interface, but still wanted every ShareAsset subclass to have a getMarketValue method, we could have declared an abstract getMarketValue method:

        public abstract double getMarketValue();

An abstract method is declared with a header similar to regular methods, but with the keyword abstract after the public modifier. The other difference about abstract methods is that they do not have method bodies--they only have a semicolon, which means that the behavior is specified in each subclass. An abstract class can contain both abstract and non-abstract methods. Technically, every method in an interface is abstract, and they can be declared with the abstract keyword if so desired (but the presence or absence of the abstract keyword has no effect).

The general syntax for abstract method declarations is the following:

public abstract <type> <name>(<type> <name>, ..., <type> <name>);

Because an abstract class can contain normal data fields and methods with implementation, and it can also contain abstract methods without implementation, abstract classes can be thought of as hybrids between classes and interfaces. One important difference between interfaces and abstract classes is that a class may choose to implement arbitrarily many interfaces, but it can only extend one abstract class.

We can modify the Stock and MutualFund classes to take advantage of ShareAsset and reduce the redundancy. Notice that we must implement getMarketValue, or else we receive an error. Here are the final versions of all three classes:

        // Represents a general asset that has a symbol and holds shares.
        public abstract class ShareAsset implements Asset {
            private String symbol;
            private double totalCost;
            private double currentPrice;
        
            public ShareAsset(String symbol, double totalCost, double currentPrice) {
                this.symbol = symbol;
                this.totalCost = totalCost;
                this.currentPrice = currentPrice;
            }
        
            public void addCost(double cost) {
                this.totalCost += cost;
            }
        
            public double getCurrentPrice() {
                return this.currentPrice;
            }
        
            // Notice how this method calls a nonexistent getMarketValue method!
            public double getProfit() {
                return this.getMarketValue() - this.totalCost;
            }
        
            public void setCurrentPrice(double currentPrice) {
                this.currentPrice = currentPrice;
            }
        }
        // A Stock object represents purchases of shares of a particular stock.
        public class Stock extends ShareAsset implements Asset {
            private int numShares;
            
            public Stock(String symbol, double currentPrice) {
                super(symbol, 0.0, currentPrice);  // call Asset c'tor
                this.numShares = 0;
            }
            
            public double getMarketValue() {
                return this.numShares * this.getCurrentPrice();
            }
            
            public void recordPurchase(int numShares, double pricePerShare) {
                this.numShares += numShares;
                addCost(numShares * pricePerShare);
            }
        }
        // Represents a mutual fund asset.
        public class MutualFund extends ShareAsset {
            private double numShares;
            
            public MutualFund(String symbol, double currentPrice) {
                super(symbol, 0.0, currentPrice);  // call Asset constructor
                this.numShares = 0.0;
            }
            
            public double getMarketValue() {
                return this.numShares * this.getCurrentPrice();
            }
            
            public void recordPurchase(double numShares, double pricePerShare) {
                this.numShares += numShares;
                addCost(numShares * pricePerShare);
            }
        }

Our final type hierarchy looks like this. We have achieved a solid object-oriented design for these tricky investment types, which can now be used by other classes to manage the user's finances.

 

9.8 Chapter Summary

9.9 Self-Check Exercises

  1. Using the Foo, Bar, Baz, and Mumble classes from Section 9.4, what is the output of the following code fragment?
            public static void main(String[] args) {
                Foo[] elements = {new Bar(), new Mumble(), new Foo(), new Baz()};
                for (int i = 0; i < elements.length; i++) {
                    elements[i].method2();
                    System.out.println(elements[i]);
                    elements[i].method1();
                    System.out.println();
                }
            }
  2. Assume the following classes have been defined:
            public class Flute extends Blue {
                public void method2() {
                    System.out.println("flute 2");
                }
            
                public String toString() {
                    return "flute";
                }
            }
            public class Blue extends Moo {
                public void method1() {
                    System.out.println("blue 1");
                }
            }
            public class Shoe extends Flute {
                public void method1() {
                    System.out.println("shoe 1");
                }
            }
            public class Moo {
                public void method1() {
                    System.out.println("moo 1");
                }
            
                public void method2() {
                    System.out.println("moo 2");
                }
            
                public String toString() {
                    return "moo";
                }
            }

    What is the output produced by the following code fragment?

            public static void main(String[] args) {
                Moo[] elements = {new Shoe(), new Flute(), new Moo(), new Blue()};
                for (int i = 0; i < elements.length; i++) {
                    System.out.println(elements[i]);
                    elements[i].method1();
                    elements[i].method2();
                    System.out.println();
                }
            }
  3. Using the same classes from the previous problem, what is the output produced by the following code fragment?
            public static void main(String[] args) {
                Moo[] elements = {new Blue(), new Moo(), new Shoe(), new Foo()};
                for (int i = 0; i < elements.length; i++) {
                    elements[i].method2();
                    elements[i].method1();
                    System.out.println(elements[i]);
                    System.out.println();
                }
            }
  4. Assume the following classes have been defined:
            public class Mammal extends SeaCreature {
                public void method1() {
                    System.out.println("warm-blooded");
                }
            }
            public class SeaCreature {
                public void method1() {
                    System.out.println("creature 1");
                }
            
                public void method2() {
                    System.out.println("creature 2");
                }
            
                public String toString() {
                    return "ocean-dwelling";
                }
            }
            public class Whale extends Mammal {
                public void method1() {
                    System.out.println("spout");
                }
            
                public String toString() {
                    return "BIG!";
                }
            }
            public class Squid extends SeaCreature {
                public void method2() {
                    System.out.println("tentacles");
                }
            
                public String toString() {
                    return "squid";
                }
            }

    What is the output produced by the following code fragment?

            public static void main(String[] args) {
                SeaCreature[] elements = {new Squid(), new Whale(), new SeaCreature(),
                                          new Mammal()};
                for (int i = 0; i < elements.length; i++) {
                    System.out.println(elements[i]);
                    elements[i].method1();
                    elements[i].method2();
                    System.out.println();
                }
            }
  5. Using the same classes from the previous problem, what is the output produced by the following code fragment?
            public static void main(String[] args) {
                SeaCreature[] elements = {new SeaCreature(), new Squid(),
                                          new Mammal(), new Whale()};
                for (int i = 0; i < elements.length; i++) {
                    elements[i].method2();
                    System.out.println(elements[i]);
                    elements[i].method1();
                    System.out.println();
                }
            }
  6. Add the following constructor to the Point3D class shown in section 9.5.1:

    public Point3D()
    Constructs a new 3D point at the origin of (0, 0, 0). Use the super keyword or the this keyword as part of your solution.

9.10 Programming Problems

  1. Write an inheritance hierarchy of shapes. Make a top-level shape interface that has methods for getting the area and perimeter of a shape. Then make classes and subclasses that implement various shapes such as rectangles, squares, ellipses, circles, and right triangles. Place common behavior in superclasses whenever possible, and use abstract classes as appropriate. Add methods to the subclasses to represent the unique behavior of each shape, such as a method to get a circle's radius.
  2. Write a set of classes that define the behavior of certain animals. They can be used in a simulation of a world with many animals moving around in it. Different kinds of animals will move in different ways (you are defining those differences). As the simulation runs, animals can "die" by ending up on the same location, in which case the simulator randomly selects one animal to survive the collision.

    The behavior of each animal class is the following:
    Class getChar getMove
    Bird B Randomly selects one of the four directions each time
    Frog F Picks a random direction, moves 3 in that direction, repeat (same as bird, but staying in a single direction longer)
    Mouse M West 1, north 1, repeat (zig zag to the NW)
    Turtle T South 5, west 5, north 5, east 5, repeat (clockwise box)
    Wolf W You define this to have any custom behavior you want

    Your classes should be stored in files called Bird.java, Frog.java, Mouse.java, Turtle.java and Wolf.java.


Stuart Reges
Marty Stepp
Last modified: Tue Jan 3 12:51:10 PST 2006

Chapter 10
Graphical User Interfaces

Copyright © 2005 by Stuart Reges and Marty Stepp

10.1 Introduction

In this chapter we will explore the creation of graphical user interfaces (GUIs). While console programs like those we have written in the preceding chapters are still very important and prevalent in computer science, the majority of modern desktop applications are written with a graphical user interface. In previous chapters, a DrawingPanel was introduced to allow 2D graphics to be drawn on the screen. But writing a GUI is not the same as drawing shapes and lines onto a drawing panel. A real graphical user interface involves the creation of one's own window frames that contain buttons, text input fields, and other onscreen components.

10.2 Graphical Input and Output with JOptionPane

The simplest way to create a graphical window in Java is through the use of a class named JOptionPane. An option pane is a simple message box that pops up on the screen and presents a message, or a request for input, to the user. JOptionPane can be thought of as a rough graphical equivalent of System.out.println output and Scanner input.

JOptionPane belongs to the javax.swing package, so you'll need to import this to use it. ("Swing" is the name of Java's GUI libraries.) Note that the package name starts with javax this time, not java (the "x" is because in Java's early days, Swing was an extension to Java's feature set.)

        import javax.swing.*;  // for GUI components

The following program creates a Hello, world! message on the screen.

        import javax.swing.*;
        
        public class HelloWorld {
            public static void main(String[] args) {
                JOptionPane.showMessageDialog(null, "Hello, world!");
            }
        }

The program produces the following graphical 'output'. (The programs throughout this chapter will use screenshots for their output.) The window looks slightly different if you're on a different operating system, but the message is the same.


The preceding program uses a static method in the JOptionPane class named showMessageDialog. This method accepts two parameters: a parent window (which we don't have, so we're passing null), and a message string to display.

JOptionPane can be used in three major ways: to display a message (as shown previously), to present a list of choices to the user, and to ask for the user to type input. The three methods that implement these three behaviors, respectively, are named showMessageDialog, showConfirmDialog, and showInputDialog.

Useful Methods of the JOptionPane class

showConfirmDialog(parent, message)
Shows a Yes/No/Cancel message box on screen with the given message on it, returning the choice as an int with one of the following constant values:

JOptionPane.YES_OPTION (user clicked 'Yes')
JOptionPane.NO_OPTION (user clicked 'No')
JOptionPane.CANCEL_OPTION (user clicked 'Cancel')

showInputDialog(parent, message)
Shows an input box on screen with the given message on it, returning the user's input value as a String.

showMessageDialog(parent, message)
Shows the given message string on a message box on screen.

The following program briefly demonstrates all three types of option panes.

        import javax.swing.*;
        
        public class UseOptionPanes {
            public static void main(String[] args) {
                String name = JOptionPane.showInputDialog(null, "What is your name?");
        
                int choice = JOptionPane.showConfirmDialog(null, "Do you like cake, " + name + "?");
                if (choice == JOptionPane.YES_OPTION) {
                    JOptionPane.showMessageDialog(null, "Of course!  Who doesn't?");
                } else {  // choice == JOptionPane.NO_OPTION or .CANCEL_OPTION
                    JOptionPane.showMessageDialog(null, "We'll have to agree to disagree.");
                }
            }
        }

The graphical input and output of this program is a series of windows, which pop up one at a time.

If you'd like to graphically request user input that is not a String, such as a number, you can still use showInputDialog and then convert the String into an int using Integer.valueOf or Double.valueOf.

Since JOptionPane is a class whose behavior is entirely static, we end up writing JOptionPane. a lot in our source code. To shorten it, we can use a Java feature named static import, which allows us to import all of the static methods and constants from a Java library class into our program.

Static import

A Java code statement that allows us to refer to the static methods and constants of a class without writing that class's name.

The syntax of a static import statement is the following:

import static <package>.<class name>.*;

For example, the following program has identical behavior to the previous one, but it is noticeably more concise:

        import static javax.swing.JOptionPane.*;
        
        public class UseOptionPanes2 {
            public static void main(String[] args) {
                String name = showInputDialog(null, "What is your name?");
        
                int choice = showConfirmDialog(null, "Do you like cake, " + name + "?");
                if (choice == YES_OPTION) {
                    showMessageDialog(null, "Of course!  Who doesn't?");
                } else {
                    // choice == JOptionPane.NO_OPTION or CANCEL_OPTION
                    showMessageDialog(null, "We'll have to agree to disagree.");
                }
            }
        }

10.3 Graphical Components

JOptionPane is useful, but it is not flexible or powerful enough to create rich graphical user interfaces. To do that, we'll need to learn about the various types of widgets that can be placed on the screen in Java. We call an onscreen window a frame. The graphical widgets inside a frame, such as buttons or text input fields, are collectively called components.

Frame

A graphical window on the screen.

Component

A widget, such as a button or text field, that resides inside a graphical window.

Here are some of the more common Java graphical components, listed by class name:

Java's Graphical Components
JButton
JRadioButton
JCheckBox

JTextField
JSlider
JToolBar
JComboBox
JList
JMenuBar, JMenu, JMenuItem
JLabel
JColorChooser
JFileChooser
JTable
JTree

A more complete pictorial reference of Java's graphical components can be found in Sun's Java Tutorial at http://java.sun.com/docs/books/tutorial/uiswing/components/components.html.

10.3.1 Working with JFrames

Any complex graphical program must construct a JFrame object to represent that program's main graphical window. Once a JFrame object has been constructed, we can tell it to display itself on the screen by calling the setVisible method on it, passing the boolean value of true. A simple program that constructs a frame and places it onscreen is the following:

        import javax.swing.*;
        
        public class SimpleFrame {
            public static void main(String[] args) {
                JFrame frame = new JFrame();
                frame.setVisible(true);
            }
        }

This program has some problems. Foremost, it produces the following amusingly bad graphical output:

There is also another problem with the program. Even after you close the window, it doesn't actually quit out of the Java program. When you display JFrames on the screen, Java doesn't exit the program by default when those frames are closed. You can notice that the program hasn't exited because a console window will remain on your screen (if you're using certain Java editors) or because your editor does not show its usual message that the program has terminated. If you do want the program to exit when the window closes, you have to say so explicitly.

To make a more interesting frame, we must learn about the properties that JFrames have. We can set or check these properties' values by calling methods on the JFrame. One property we'll set is the 'default close operation' of the frame, telling it to shut down our Java program when it is closed. We'll also give a title to the frame by setting its title property.

        public static void main(String[] args) {
            JFrame frame = new JFrame();
            frame.setTitle("A frame");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setVisible(true);
        }

Here is a summary of several JFrame properties:

Useful Properties Specific to JFrames
PropertyTypeDescriptionMethods
default close operation int

What the JFrame should do when it is closed. Possible choices:

JFrame.DO_NOTHING_ON_CLOSE:
Don't do anything.
JFrame.HIDE_ON_CLOSE:
Hide the frame.
JFrame.DISPOSE_ON_CLOSE:
Hide and destroy the frame so that it cannot be shown again.
JFrame.EXIT_ON_CLOSE:
Exit the program.

getDefaultCloseOperation, setDefaultCloseOperation(int)
icon image Image The icon that appears in the title bar and Start menu or Dock getIconImage, setIconImage(Image)
layout LayoutManager an object that controls the positions and sizes of the components inside this frame getLayout, setLayout(LayoutManager)
resizable boolean whether or not the JFrame allows itself to be resized isResizable, setResizable(boolean)
title String text shown on the JFrame's title bar getTitle, setTitle(String)

JFrames also have some useful methods that we'll discuss later in the chapter.

Useful Methods of JFrame objects

public void pack()
Resizes the frame to snugly fit the components inside it.

It turns out that all graphical components and frames share a common set of properties, because they exist in a common inheritance hierarchy. The following is a partial list of useful properties of frames/components and their respective methods:

Useful Properties of All Components (Including JFrames)
PropertyTypeDescriptionMethods
background Color background color getBackground, setBackground(Color)
enabled boolean whether or not the component can be interacted with isEnabled, setEnabled(boolean)
focusable boolean whether the keyboard may send input to the component isFocusable, setFocusable(boolean)
font Font font used to write text getFont, setFont(Font)
foreground Color foreground color getForeground, setForeground(Color)
location Point (x, y) coordinate of component's top-left corner getLocation, setLocation(Point)
size Dimension the current width and height of the component getSize, setSize(Dimension)
preferred size Dimension the 'preferred' width and height of the component; the size it would like to be to make it appear naturally on the screen (used with layout managers, seen later) getPreferredSize, setPreferredSize(Dimension)
visible boolean whether or not the component can be seen on the screen isVisible, setVisible(boolean)

There are a few new types of objects in the preceding table. The background and foreground properties are Color objects, which we've seen before. The location property is a Point object, which we've also seen. The font property is a Font object; we'll discuss fonts later in this chapter. All of these types are found in the java.awt package, so be sure to import it, just like when drawing graphical programs with DrawingPanel in previous chapters.

        import java.awt.*;  // for various graphical objects

The size property is an object of type Dimension, which simply stores a width and height (and is constructed with two integers representing those values). We'll use this property several times in this chapter. We only need to know a bit about the Dimension type, such as how to construct it.
 

Useful Methods of Dimension objects

public Dimension(int width, int height)
Constructs a Dimension representing the given size.

public int getWidth()
Returns the width of this Dimension.

public int getHeight()
Returns the height of this Dimension.

The following code creates a JFrame and sets several of its properties.

        import java.awt.*;
        import javax.swing.*;
        
        public class SimpleFrame3 {
            public static void main(String[] args) {
                JFrame frame = new JFrame();
                frame.setForeground(Color.WHITE);
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLocation(new Point(10, 50));
                frame.setSize(new Dimension(300, 120));
                frame.setTitle("A frame");
                frame.setVisible(true);
            }
        }

When the program is run, the following window appears. When the window is closed, the program exits.

10.3.2 Common Components: Buttons, Labels, and Text Fields

Our empty JFrames are not very interesting with no components inside them. Let's look at some graphical components that can be placed in a frame, such as buttons and text fields.

The first component we'll examine is the button, represented by the JButton class. Each JButton object you create represents one button on the screen.


JButton objects

A button can be constructed either with no arguments (a button with no text), or with a String representing the text on the button. Of course, we can always change the button's text by calling the setText method on it. We can also set other properties of the button such as its background color.

        JButton button1 = new JButton();
        button1.setText("I'm the first button");

        JButton button2 = new JButton("The second button");
        button2.setBackground(Color.YELLOW);

Two other components of interest are the label and the text field. A label is represented by a JLabel object, which can be constructed with an optional argument specifying the label's text. Text fields are represented by JTextField objects. A JTextField can be constructed by passing it the number of characters wide that should be able to fit in the text field.

        JLabel label = new JLabel("This is a label");
        JTextField field = new JTextField(8);  // 8 letters wide



JLabel and JTextField objects

Merely constructing the various component objects does not place them onto the screen. We must add them to our frame so it will display them. A frame acts as a container, to which you can add graphical components. To add a component to a JFrame, call its add method and pass the appropriate component. (Prior to Java v1.5, you must write .getContentPane().add instead.)

The following program creates a frame and places two buttons inside it:

        import java.awt.*;
        import javax.swing.*;
        
        public class ComponentsExample1 {
            public static void main(String[] args) {
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setSize(new Dimension(300, 100));
                frame.setTitle("A frame");
                
                JButton button1 = new JButton();
                button1.setText("I'm a button.");
                button1.setBackground(Color.BLUE);
                frame.add(button1);
                
                JButton button2 = new JButton();
                button2.setText("Click me!");
                button2.setBackground(Color.RED);
                frame.add(button2);
                
                frame.setVisible(true);
            }
        }

You'll notice a pattern here. When we create a component, we must do the following things:

The program produces the following strange graphical output:

Notice that the first button isn't visible. The second button has been stretched to fill the entire frame! This is because we haven't told the frame how to position and lay out the buttons, so it defaults to giving each component the entire frame's space. The last component added 'wins' and gets all the space.

Each button has a size and location property, but setting these won't fix the problem. The frame has an internal 'layout manager' object that it uses to forcibly position all of the components inside it. Even if we set the position and size of the buttons, the frame will set them back to what it prefers.

The solution to this problem, instead, is to set a new layout manager for the JFrame and instruct it how to position the components. If we want the buttons to flow in a left-to-right order, for example, we can use a FlowLayout. We'd insert the following line in our program before adding the buttons:

        frame.setLayout(new FlowLayout());

After setting the layout, the components now all show up on the screen, though perhaps not exactly in the positions we'd like. The following graphical output is produced:

We'll discuss layout managers in detail in the next section. For now, we will use a FlowLayout only so that we can focus on the components.

Another thing you may notice is that the frame isn't sized quite right to fit its buttons--it has some extra space hanging on the bottom. JFrames have a method named pack that tells them to resize themselves exactly to fit their contents. It's useful to call this method just before showing the frame on the screen, to make sure it's a snug size. Of course, this undoes whatever setSize calls you made previously.

        frame.pack();
        frame.setVisible(true);

The new, packed frame has the following onscreen appearance:

10.3.3 JTextArea, JScrollPane, and Font

While the JTextField component represents a single-line text input field, the JTextArea component represents a multi-line editing box. You can construct a JTextArea by passing the number of rows and columns (the number of lines, and the number of letters in each line).

        // a text area which is 5 lines tall and 20 letters wide
        JTextArea area = new JTextArea(5, 20);


A JTextArea object

The following program constructs a simple JFrame and adds a JTextArea to it:

        import java.awt.*;
        import javax.swing.*;
        
        public class TextFrame {
            public static void main(String[] args) {
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new FlowLayout());
                frame.setSize(new Dimension(300, 150));
                frame.setTitle("Text frame");
                
                JTextArea area = new JTextArea(5, 20);
                area.setFont(new Font("Serif", Font.BOLD, 14));
                frame.add(new JScrollPane(area));
                
                frame.setVisible(true);
            }
        }

The program produces the following graphical output (shown both before and after typing some text onto the text area):

There is one problem with the text area. When the user types too much text to fit in the text area, the text simply vanishes off the bottom of the text area.

To fix this, we can use a special component called a JScrollPane to attach scroll bars to the text area. A JScrollPane is constructed by passing the text area as a parameter.

Useful methods of JScrollPane objects

public JScrollPane(Component component)
Constructs a new scroll pane to add scroll bars to the given component.

We can add the JScrollPane to the frame instead of the JTextArea itself, and now the text area will have scroll bars when its text becomes too big.

        // use scroll bars on this text area
        frame.add(new JScrollPane(area));

If you don't like the default font of the text area, remember that every component has a Font property that can be set. A Font object can be constructed by passing its name, style (such as bold or italic), and size in pixels.

Useful methods of Font objects

public Font(String name, int style, int size)
Constructs a new font of the given style and size.

Font names can refer to a font on your system, such as "Times New Roman", or to special pseudo-font names known to Java such as "Serif", "SansSerif", and "Monospaced". A serif is a fan-out that occurs at the edges of letters in fonts such as Times New Roman, but not in straight-edged or "sans-serif" fonts such as Arial or Verdana. A monospaced font uses evenly sized characters, such as Courier New or your Terminal font. (Most Java editors use a monospaced font to edit your programs so that text columns line up exactly on the screen.)

Font styles can be one or more of the following:

Font.BOLD: thick text.
Font.ITALIC: slanted text.
Font.PLAIN: normal text.

Font styles can be combined by adding them, such as Font.BOLD + Font.ITALIC for thick slanted text.

The following line of code tells the text area to use a size 14 serifed font.

        area.setFont(new Font("Serif", Font.BOLD, 14));

10.3.4 Icons

Many Java components, including buttons and labels, have an icon property that can be set to place an image on the component.

Useful Properties of JButton and JLabel objects
PropertyTypeDescriptionMethods
icon Icon the icon image that appears on the component getIcon, setIcon(Icon)

The easiest way to get an Icon is to create an ImageIcon object. The ImageIcon constructor accepts a String parameter representing the image file to load. The image is a file on your hard disk, such as a GIF, JPG, or PNG image. Place your image files into the same folder as your .java files.

Useful Methods of ImageIcon objects

public ImageIcon(String filename)
Constructs an icon from the image in the given file.

public ImageIcon(Image image)
Constructs an icon from the given image. (See BufferedImage below)

The following code places an icon onto a JButton. The file smiley.jpg has already been saved to the same folder as the code.

        // create a smiley face icon for this button
        JButton button = new JButton("Have a nice day");
        button.setIcon(new ImageIcon("smiley.jpg"));

When placed into a JFrame, the button has the following appearance. The button is larger to accommodate both its text and its new icon.

Another way to create an icon is to draw one for yourself. You can create a blank image buffer using a BufferedImage object, and draw onto it using a Graphics pen, much like you did with the DrawingPanel in previous chapters. BufferedImage is a class in the java.awt.image package, so if you want to use it, you must import this package.

        import java.awt.image.*;  // for BufferedImage

A BufferedImage plays a key role in the way the DrawingPanel is implemented. Here are the useful constructor and methods from BufferedImage:

Useful Methods of BufferedImage objects

public BufferedImage(int width, int height, int type)

Constructs an image buffer of the given size and type. Valid types are:

BufferedImage.TYPE_INT_ARGB: An image with a transparent background.
BufferedImage.TYPE_INT_RGB: An image with a solid black background.

public Graphics getGraphics()
Returns the Graphics pen for drawing on this image buffer.

To use a BufferedImage, construct one of a particular size and type (we recommend TYPE_INT_ARGB), then get the Graphics object from it using getGraphics(), then issue the standard drawing commands such as drawRect or fillOval. Once you're done drawing on the BufferedImage, you can set it as the icon for an onscreen component by creating a new ImageIcon object and passing the BufferedImage to the ImageIcon constructor. You can use this ImageIcon as the icon for an onscreen component by calling setIcon on that component and passing the ImageIcon as the parameter.

        JButton button = new JButton();
        button.setText("My drawing");

        // create a shape image icon for this button
        BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
        Graphics g = image.getGraphics();
        g.setColor(Color.YELLOW);
        g.fillRect(10, 20, 80, 70);
        g.setColor(Color.RED);
        g.fillOval(40, 50, 25, 25);

        ImageIcon icon = new ImageIcon(image);
        button.setIcon(icon);

A BufferedImage can also be used as the icon for a JFrame, by calling its setIconImage method.

        frame.setIconImage(image);

When placed into a JFrame, the button's appearance is the following. Notice how a smaller version of the icon also appears at the top-left of the window, because of the setIconImage call.

10.4 Laying Out Components in a Frame

In the past section, we used FlowLayout to position components in our JFrame. In this section, we will explore several different layout manager objects that can be used to position components in a variety of ways.

10.4.1 Layout Managers

Java contains many layout manager classes in its java.awt package, so make sure to import that (along with javax.swing for the JFrame and other component classes).

        import java.awt.*;     // for LayoutManagers
        import javax.swing.*;  // for GUI components

Here are some of the most common layout managers:

Layout Managers
BorderLayout
BoxLayout
FlowLayout
GridLayout
SpringLayout
GridBagLayout

Perhaps the simplest layout manager is FlowLayout. FlowLayout positions components in a left-to-right, top-to-bottom 'flow' that is somewhat like words in a paragraph. If the components are too wide to fit in the frame, they wrap around.

Consider the following code, which adds three components to a frame that uses a FlowLayout:

        JLabel label = new JLabel("Type your ZIP code: ");
        JTextField field = new JTextField(5);
        JButton button = new JButton("Submit");

        frame.setLayout(new FlowLayout());
        frame.add(label);
        frame.add(field);
        frame.add(button);

The following graphical output is produced. The window is then resized to show the wrapping behavior:


Another common layout is named GridLayout. Construct a GridLayout by passing it a number of rows and columns. Now when components are added to the frame, they are laid out in equal-sized squares. The components are placed in the squares in row-major order (left-to-right, top-to-bottom). We'll use only buttons in this example so you can clearly see each component's size and shape:

// 2 rows, 3 columns frame.setLayout(new GridLayout(2, 3)); for (int i = 1; i <= 6; i++) { frame.add(new JButton("Button " + i)); }

The following graphical output is produced. Notice how GridLayout forces every component to take the same size, even though this results in awkwardly stretched buttons. The window is then resized to further show the stretching behavior:

The third and final layout manager introduced in this section is named BorderLayout. BorderLayout divides the frame into five sections: north, south, west, east, and center. When adding components to a frame using a BorderLayout, you pass two arguments: the component to add, and a constant representing which region to place it.

The constants are named BorderLayout.NORTH, BorderLayout.SOUTH, BorderLayout.WEST, BorderLayout.EAST, and BorderLayout.CENTER respectively. (If you do not pass a second argument, the component will be placed in the center.) We'll again make use of the 'static import' feature to import these constant names, so we only need to say a short name like NORTH or CENTER.

        import java.awt.*;
        import javax.swing.*;
        import static java.awt.BorderLayout.*;
        
        public class BorderLayoutExample {
            public static void main(String[] args) {
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setSize(new Dimension(210, 200));
                frame.setTitle("Run for the border");
        
                frame.setLayout(new BorderLayout());
                frame.add(new JButton("north"), NORTH);
                frame.add(new JButton("south"), SOUTH);
                frame.add(new JButton("west"), WEST);
                frame.add(new JButton("east"), EAST);
                frame.add(new JButton("center"), CENTER);
        
                frame.setVisible(true);
            }
        }

Here's the graphical output, both before and after resizing.

Notice the stretching behavior. The north and south buttons stretch horizontally but not vertically. The west and east buttons stretch vertically but not horizontally. The center button gets all remaining space. We don't have to place a component in all 5 regions; if we leave a region such as SOUTH empty, the CENTER consumes its space.

Here is a quick summary of the stretching and resizing behavior of all layout managers in this section:

Every graphical component has a property for its 'preferred size.' Layouts like FlowLayout allow components to appear on the screen at their preferred sizes, while layouts like GridLayout ignore the preferred size. If for some reason you want to change the component's preferred size, you may set the preferred size property by calling its setPreferredSize(Dimension) method.

For example, an earlier program from a previous section created two buttons named button1 and button2 and set background colors for them. The following short modification gives the two buttons different preferred sizes.

        button1.setPreferredSize(new Dimension(150, 14));
        button2.setPreferredSize(new Dimension(100, 45));

The onscreen result is the following:

10.4.2 SpringLayout

The layout managers in the previous section are somewhat simple, but they lack flexibility and power. Another layout manager named SpringLayout can be used to lay out components in a frame by linking edges of components. We don't specify the sizes of the components, but instead we specify their positions relative to edges of other components or edges of the overall frame. SpringLayout is more complicated than the others previously shown, but it is also more powerful.

We start by creating a SpringLayout object and setting our frame to use that layout. We'll save the SpringLayout into a variable, because we'll need to call methods on it in a moment:

        SpringLayout layout = new SpringLayout();
        frame.setLayout(layout);

The next step is that after our components are created and added to the frame, we specify constraints on their edges. Constraints are specified by calling the putConstraint method on the SpringLayout object. We pass five arguments when placing a constraint: a component and its edge (one of SpringLayout.WEST, SpringLayout.EAST, SpringLayout.NORTH, or SpringLayout.SOUTH), an int specifying the number of pixels of gap, and another component and its edge.

For example, if you have two JButton objects named button1 and button2, the following code places button2 exactly five pixels to the right of button1 by linking button2's west edge to button1's east edge. We'll use the static import again to import the constants from SpringLayout, so we can say shorter names like WEST instead of SpringLayout.WEST.

        layout.putConstraint(WEST, button2, 5, EAST, button1);

In the last example, we had two buttons. Let's put the first one in the top-left corner of the frame and the second in the bottom-right corner. To specify the frame itself as one of the edges to link, we must write .getContentPane() to specify that we want only the inner area that holds components, not the entire frame including its title and borders.

        import java.awt.*;
        import javax.swing.*;
        import static javax.swing.SpringLayout.*;
        
        public class SpringLayoutExample1 {
            public static void main(String[] args) {
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setSize(new Dimension(300, 100));
                frame.setTitle("Spring layout");
        
                SpringLayout layout = new SpringLayout();        
                frame.setLayout(layout);
        
                JButton button1 = new JButton();
                button1.setBackground(Color.BLUE);
                button1.setText("I'm a button.");
                frame.add(button1);
                
                JButton button2 = new JButton();
                button2.setBackground(Color.RED);
                button2.setText("Click me!");
                frame.add(button2);
        
                // keep button 1 at (5, 5)
                layout.putConstraint(WEST, button1, 5, WEST, frame.getContentPane());
                layout.putConstraint(NORTH, button1, 5, NORTH, frame.getContentPane());
                
                // keep button 2's bottom-right edge at (width - 5, height - 5)
                layout.putConstraint(EAST, button2, -5, EAST, frame.getContentPane());
                layout.putConstraint(SOUTH, button2, -5, SOUTH, frame.getContentPane());
        
                frame.setVisible(true);
            }
        }

When this program runs, it produces the following graphical output. The second button stays in place at the bottom-right corner when the window is resized:


10.4.3 Composite Layout

There is an alternative to using SpringLayout to get good component positioning using only the three simpler managers shown before. The layout managers may be layered on top of one another to produce combined effects. This layering is also called a composite layout.

Composite Layout

A layered layout using several layout managers in different nested containers.

To create a composite layout, we must learn about containers and the JPanel class. A JFrame contains an object called a content pane which contains graphical components. When we've been adding buttons to our JFrame, we've really been adding to its content pane container. It's possible to construct additional containers and use them to enhance our layout. The type of object that we use as a container is named JPanel.

A JPanel object is a container to which we can add graphical components such as buttons. The usefulness of a JPanel is that it can have its own layout manager. Now only the components within that container will use that layout. By creating several containers with different layouts, we can have a fine-grained control over how each group of components behaves.

JPanels can be constructed with no parameters or with an initial layout manager to use. Once constructed, a panel can hold components that are added to it.

        JPanel panel = new JPanel(new FlowLayout());
        panel.add(new JButton("button 1"));
        panel.add(new JButton("button 2"));

A very common strategy is to make the JFrame use a BorderLayout, then add smaller JPanel containers to some or all of the 5 regions of the frame. For example, the following code produces a window that looks like a crude telephone:

        import java.awt.*;
        import javax.swing.*;
        import static java.awt.BorderLayout.*;
        
        public class Telephone {
            public static void main(String[] args) {
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setSize(new Dimension(250, 200));
                frame.setTitle("Telephone");
        
                frame.setLayout(new BorderLayout());
        
                // the main phone buttons
                JPanel centerPanel = new JPanel(new GridLayout(4, 3));
                for (int i = 1; i <= 9; i++) {
                    centerPanel.add(new JButton("" + i));
                }
                centerPanel.add(new JButton("*"));
                centerPanel.add(new JButton("0"));
                centerPanel.add(new JButton("#"));
                frame.add(centerPanel, CENTER);
        
                // south status panel
                JPanel southPanel = new JPanel(new FlowLayout());
                southPanel.add(new JLabel("Number to dial: "));
                southPanel.add(new JTextField(10));
                frame.add(southPanel, SOUTH);
        
                frame.setVisible(true);
            }
        }

The graphical output is the following:


It can be very tricky to get a composite layout to look just right. It helps to practice with lots of examples and carefully consider the stretching behaviors of the various layouts. For example, how would we create a window that looks like this?


We'll need one new type of component for this example: The JTextArea, which is the same as a JTextField except that the user may type multiple lines into it. It can be constructed with no arguments.

The first stab at this would be to notice that there appear to be three rows of components in the top of the window, so we could try to make a panel with a 3x3 grid layout and place the various components into it. We'd then add this to the frame, along with the central text area and Send button.

        // This code is not quite right...
        frame.setLayout(new BorderLayout());

        JPanel north = new JPanel(new GridLayout(3, 3));
        north.add(new JLabel("From: "));
        north.add(new JTextField());
        north.add(new JButton("Browse..."));
        north.add(new JLabel("To: "));
        north.add(new JTextField());
        north.add(new JButton("Browse..."));
        north.add(new JLabel("Subject: "));
        north.add(new JTextField());

        frame.add(north, NORTH);
        frame.add(new JTextArea(), BorderLayout.CENTER);
        frame.add(new JButton("Send"), BorderLayout.SOUTH);

The following is the graphical output, which is not correct.


There are a few problems. First of all, the text labels and fields are not the proper size. This is because a grid layout forces everything inside it to take the same size. We don't want this. We want each label to be the same size as the other labels, and each text field to be the same size as the others, and so on--but we don't want the different kinds of components linked in horizontal size.

The simplest way to resolve this size problem is to create three separate JPanels with grid layouts, each with 3 rows and only 1 column. Put the labels into one grid, the text fields into the second, and the buttons into the third. (Even though there are only two buttons, make the layout 3x1 to leave a blank space to match the expected output.)

To position the three grids next to each other, we'll add another layer of compositing by creating a master north JPanel to store all three grids. The labels will occupy the west, the text fields the center, and the buttons the east. This master north JPanel will be placed into the NORTH region of the frame.

Another problem is that the Send button is stretched. This is because it's in the south region of a BorderLayout, which stretches the south horizontally. To avoid this problem, we can put the Send button into a JPanel with a FlowLayout. FlowLayout doesn't stretch the components inside it, so the button won't grow to such an odd size. Now we'll add our south JPanel to the frame rather than adding the Send button directly.

Here's a correct version of the EmailMessage program that contains these corrections and produces the proper graphical output:

        import java.awt.*;
        import javax.swing.*;
        
        public class EmailMessage {
            public static void main(String[] args) {
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setSize(new Dimension(400, 300));
                frame.setTitle("Send Message");
                frame.setLayout(new BorderLayout());
        
                JPanel northWest = new JPanel(new GridLayout(3, 1));
                northWest.add(new JLabel("From: "));
                northWest.add(new JLabel("To: "));
                northWest.add(new JLabel("Subject: "));
        
                JPanel northCenter = new JPanel(new GridLayout(3, 1));
                northCenter.add(new JTextField());
                northCenter.add(new JTextField());
                northCenter.add(new JTextField());
        
                JPanel northEast = new JPanel(new GridLayout(3, 1));
                northEast.add(new JButton("Browse..."));
                northEast.add(new JButton("Browse..."));
        
                JPanel north = new JPanel(new BorderLayout());
                north.add(northWest, BorderLayout.WEST);
                north.add(northCenter, BorderLayout.CENTER);
                north.add(northEast, BorderLayout.EAST);
        
                JPanel south = new JPanel(new FlowLayout());
                south.add(new JButton("Send"));
        
                frame.add(north, BorderLayout.NORTH);
                frame.add(new JTextArea(), BorderLayout.CENTER);
                frame.add(south, BorderLayout.SOUTH);
        
                frame.setVisible(true);
            }
        }

10.5 Events

So far we've created user interfaces that use graphical components and look the way we want, but they are not interactive -- nothing happens when the user clicks or types on the components. In order to create useful interactive GUIs, we must understand a Java feature named events.

When the user clicks on a component, presses a key on it, moves the mouse over it, or otherwise interacts with a component, Java's GUI system creates a special type of object to represent this action. This type of object is called an event.

Event

An object representing a user's interaction with a GUI component, which can be handled by your programs to create interactive components.

By default, if you don't specify anything to do about an event, it goes unnoticed by your program. Therefore, nothing happens when the user clicks your buttons or types on your text fields.

We can cause a response to a particular event (such as when the user clicks on a particular button) by creating an object called a listener, and attaching that listener to the component of interest. The listener object contains the code we want to run when the appropriate event occurs.

Listener

An object that is notified when an event occurs and executes code to respond to that event.

10.5.1 Action Events and ActionListener

The first classes we'll use for handling events in Java are named ActionEvent and ActionListener. (Both of these classes are in the java.awt.event package, so we'll need to import that.)

        import java.awt.event.*;  // for action events

An action event is a fairly general event type that occurs when the user interacts with many standard components, such as clicking on a button or pressing Enter on a TextField. There are other types of events for more specific actions, such as MouseEvent, KeyEvent, and WindowEvent.

Many Java Swing components have a method named addActionListener that accepts a parameter of type ActionListener. An ActionListener is an object that can be notified when events occur and can respond to those events.

Useful Methods of Components

public void addActionListener(ActionListener listener)
Used to attach an action listener to hear action events.

ActionListener is not a class but is actually an interface. To listen to an event, we write a class that implements the ActionListener interface, and we specify in that class how we want to respond to the event. Then we attach an object of our listener class to the appropriate component.

The ActionListener interface contains only the following method:

        public void actionPerformed(ActionEvent event)

Here is a simple example of an action listener class that responds to an event by displaying a message dialog box on the screen. For now we will ignore the ActionEvent parameter and not use it in our code inside the actionPerformed method.

        // Responds to a button click event by displaying a message dialog box.
        public class MessageListener implements ActionListener {
            public void actionPerformed(ActionEvent event) {
                JOptionPane.showMessageDialog(null, "You clicked the button!");
            }
        }

Now that we've created this class, we can attach MessageListener objects to buttons. This will cause a message to pop up whenever that button is clicked.

        JButton button = new JButton("Click me");
        MessageListener listener = new MessageListener();
        button.addActionListener(listener);

Here is the result when the program is executed, and when the user clicks on the button:

To summarize, here are the necessary steps to handle an event in Java:

  1. Write a class that implements ActionListener
  2. Place the code to handle the event into its actionPerformed method
  3. Attach an object of your listener class to the component you want to listen to

10.5.2 More Sophisticated ActionEvents

The ActionEvent object passed as the parameter to an ActionListener's actionPerformed method can be useful, depending on what response we want to occur. The ActionEvent has methods that we can call to find out more about the event that occurred -- what component was clicked, and so on.

Useful Methods of ActionEvent objects

public String getActionCommand()
Returns a String representing the event that occurred. For example, if the component in question is a JButton, this method returns the button's text. If it is a JTextField, this method returns the text the user has typed into the text field.

public Object getSource()
Returns a reference to the component that generated the event. Since the return type is written as Object to be general, you'll have to cast it to the type of component you expect.

The following action listener could be attached to a JButton. It would change the button's background color from red to blue each time the user clicked the button.


        // Responds to a button click event by displaying a message dialog box.
        public class MessageListener implements ActionListener {
            public void actionPerformed(ActionEvent event) {
                JButton button = (JButton) event.getSource();
                if (button.getBackground() == Color.RED) {
                    button.setBackground(Color.BLUE);
                } else {
                    button.setBackground(Color.RED);
                }
            }
        }

The following action listener could be attached to a JTextField. It would capitalize the text in the text field when the user presses Enter in the field.

        // Responds to a text field Enter keypress by capitalizing its text
        public class CapitalizeListener implements ActionListener {
            public void actionPerformed(ActionEvent event) {
                JTextField field = (JTextField) event.getSource();
                field.setText(field.getText().toUpperCase());
            }
        }

10.5.3 A Larger GUI Example with Events: Credit Card GUI

In larger graphical programs, your responses to an event that occurs in one component will affect another component. In this section, we'll explore such an example program that needs event responses that affect multiple components.

You may not know that credit card numbers can be checked using an algorithm to determine if they are valid. For example, Visa card numbers always begin with 4, and a valid Visa card number also passes a digit-sum test known as the Luhn checksum algorithm. Luhn's algorithm states that if you sum the digits of the number in a certain way, the total sum must be a multiple of 10 for a valid Visa number.

The method of summing the digits is the following. For digits at even indexes (the 0th digit, 2nd digit, etc.), simply add that digit to the sum. For digits at odd indexes (index 1, 3, etc.), double the digit's value, then if that doubled value is more than 10, add its digits together to make a number that is smaller than 10, then add this result into the sum.

Here is an example credit card number that passes the Luhn algorithm. Notice how the number 5 at index 10 is doubled to 10 which becomes (1+0), how the number 7 at index 12 is doubled to 14 which becomes (1+4), and so on.

        4408 0412 3456 7893

        (8)+4+(0)+8 + (0)+4+(2)+2 + (6)+4+(1+0)+6 + (1+4)+8+(1+8)+3 = 70

        70 % 10 == 0, therefore card number is valid

We could write a GUI where the user could type in a credit card number, press a button to verify, then receive a message stating whether the number was valid. The GUI might look like this:

However, let's think about the event response code for a second. Clicking the "Verify CC Number" button would cause the action event, so we'd put an ActionListener on the button. But in the code for the actionPerformed method, we'd need read the text from the top-left text field, decide whether the number was valid, and use that information to set the text of the bottom JLabel. The GUIs we've written so far were not built to allow so much interaction between components.

To make a GUI that does allow access to each component from your listeners, make your GUI itself into an object. Declare your components as data fields inside that object, and initialize them in the GUI's constructor. The following code demonstrates this; a listener is also attached to our JButton, of type VerifyListener. We'll implement this in a moment.

public class CreditCardGUI {
    private JFrame frame;
    private JTextField numberField;
    private JLabel validLabel;
    private JButton verifyButton;

    public CreditCardGUI() {
        this.frame = new JFrame("Credit card number verifier");
        this.numberField = new JTextField(16);
        this.validLabel = new JLabel("not yet verified");
        this.verifyButton = new JButton("Verify CC Number");
        // ... (some code omitted)
        this.frame.setVisible(true);
    }

To run the program, you can include a main method in your GUI class that calls the constructor to create your GUI object.

        public static void main(String[] args) {
            new CreditCardGUI();
        }

Now let's write the VerifyListener to listen to the action events on the Verify button. As we said earlier, this listener class will need to be able to communicate with the numberField JTextField and the validLabel JLabel. To make this easier, we'll use a Java feature called an inner class. An inner class is a class that is declared inside of another class. The inner class can only be used in conjunction with the outer class, and it can access and use all of the data fields and methods from the outer class. Inner classes are especially useful for complicated event listeners on GUIs that need to manipulate several GUI components.

Inner class

A Java class declared inside of another class. Inner classes can access all data fields and methods of the enclosing outer class.

Inner classes are declared outside any methods of the outer class, at the same indentation level as its methods. Inner classes use the same syntax as regular classes, except that they can access the data fields and methods of their enclosing outer class.

Assuming we had a method public boolean isValidCreditCardNumber(String number) that told us whether a given String represents a valid Visa card number, we could write an action listener as an inner class, that called this method and displayed the result in the text label. It would be legal to refer to those component data fields from the GUI, because the VerifyListener class is an inner class inside the GUI.

        class VerifyListener implements ActionListener {
            public void actionPerformed(ActionEvent event) {
                String text = numberField.getText();
                if (isValidCreditCardNumber(text)) {
                    validLabel.setText("Valid number!");
                } else {
                    validLabel.setText("Invalid number.");
                }
            }
        }

The following is the code for the complete CreditCardGUI program.

        import java.awt.*;
        import java.awt.event.*;
        import javax.swing.*;
        
        // Example valid numbers to test: 4111111111111111, 4408041234567893
        public class CreditCardGUI {
            public static void main(String[] args) {
                new CreditCardGUI();
            }
            
            private JFrame frame;
            private JTextField numberField;
            private JLabel validLabel;
            private JButton verifyButton;
        
            public CreditCardGUI() {
                // set up components
                this.frame = new JFrame("Credit card number verifier");
                this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                this.frame.setSize(new Dimension(350, 100));
                this.numberField = new JTextField(16);
                this.validLabel = new JLabel("not yet verified");
                this.verifyButton = new JButton("Verify CC Number");
                
                // event listeners
                this.verifyButton.addActionListener(new VerifyListener());
        
                // layout
                this.frame.setLayout(new FlowLayout());
                this.frame.add(this.numberField);
                this.frame.add(this.verifyButton);
                this.frame.add(this.validLabel);
                this.frame.setVisible(true);
            }
            
            // Returns whether the given string is a valid Visa card number
            // according to the Luhn checksum algorithm.
            public boolean isValidCreditCardNumber(String text) {
                if (!text.startsWith("4")) {
                    return false;
                }
                
                // add all of the digits
                int sum = 0;
                for (int i = 0; i < text.length(); i++) {
                    int digit = Integer.valueOf(text.substring(i, i + 1));
                    if (i % 2 == 0) {  // double every other number, add digits
                        digit *= 2;
                        sum += (digit / 10) + (digit % 10);
                    } else {
                        sum += digit;
                    }
                }
                // valid numbers add up to a multiple of 10
                return (sum % 10 == 0);
            }
        
            // Sets the label's text to show whether the credit card number is valid.
            public class VerifyListener implements ActionListener {
                public void actionPerformed(ActionEvent event) {
                    String text = numberField.getText();
                    if (isValidCreditCardNumber(text)) {
                        validLabel.setText("Valid number!");
                    } else {
                        validLabel.setText("Invalid number.");
                    }
                }
            }
        }

10.5.4 Mouse Events

When we want to listen to mouse clicks or movement, we use another type of listener named a MouseInputAdapter. This class resides in the javax.swing.event package, so you'll need to import it.

        import javax.swing.event.*;  // for mouse events

While you write an action listener by implementing an interface, you write a mouse listener by extending a pre-existing class. That class is named MouseInputAdapter, and it has the following methods:

Useful Methods of MouseInputAdapter objects

public void mouseClicked(MouseEvent event)
Invoked when the mouse button has been clicked (pressed and released) on a component.

public void mouseDragged(MouseEvent event)
Invoked when a mouse button is pressed on a component and then dragged.

public void mouseEntered(MouseEvent event)
Invoked when the mouse enters a component.

public void mouseExited(MouseEvent event)
Invoked when the mouse exits a component.

public void mouseMoved(MouseEvent event)
Invoked when the mouse cursor has been moved onto a component but no buttons have been pushed.

public void mousePressed(MouseEvent event)
Invoked when a mouse button has been pressed on a component.

public void mouseReleased(MouseEvent event)
Invoked when a mouse button has been released on a component.

There are quite a few methods, and you probably won't want to implement them all. The default version of these methods in MouseInputAdapter is an empty method that does nothing. So for example, to make a class that only responds to a mouseEntered event, extend the MouseInputAdapter class and override only the mouseEntered method.

        public class MovementListener extends MouseInputAdapter {
            public void mouseEntered(MouseEvent event) {
                JOptionPane.showMessageDialog(null, "Mouse entered!");
            }
        }

For historical reasons, mouse listeners must be added in two ways to components. If we wish to hear about mouse button presses, such as the mousePressed or mouseReleased methods, we call the addMouseListener method on the component. If we wish to hear about mouse motion, such as the mouseMoved or mouseDragged methods, we call the addMouseMotionListener method on the component. If we want to hear all the events, we can add the mouse input adapter in both ways. (This is what we'll do in the examples in this chapter.) These methods accept parameters of type MouseListener and MouseMotionListener respectively, and MouseInputAdapter implements both of these interfaces.

Useful Methods of Components

public void addMouseListener(listener)
Used to attach a MouseInputAdapter to hear mouse enter, exit, press, release, and click events.

public void addMouseMotionListener(listener)
Used to attach a MouseInputAdapter to hear mouse move and drag events.

Here is an entire program that uses the listener to respond to moving the mouse over the JLabel:

        import java.awt.*;
        import javax.swing.*;
        
        public class MouseGUI {
            public static void main(String[] args) {
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new FlowLayout());
                frame.setSize(new Dimension(200, 100));
                frame.setTitle("A frame");
        
                JLabel label = new JLabel();
                label.setText("Move the mouse over me!");
                frame.add(label);
        
                MovementListener mListener = new MovementListener();
                label.addMouseListener(mListener);
                label.addMouseMotionListener(mListener);
        
                frame.setVisible(true);
            }
        }
        
        import java.awt.event.*;
        import javax.swing.*;
        import javax.swing.event.*;
        
        public class MovementListener extends MouseInputAdapter {
            public void mouseEntered(MouseEvent event) {
                JOptionPane.showMessageDialog(null, "Mouse entered!");
            }
        }

The program produces the following graphical output, shown both before and after the user moves the mouse onto the JLabel:

Just like we saw in the previous section with ActionEvents, there are useful pieces of information stored in the MouseEvent parameter. Here is a partial list:

Useful Methods of MouseEvent objects

public int getButton()
Returns the number of the mouse button that was pressed or released (1 for the left button, 2 for the right button, and so on).

public int getClickCount()
Returns the number of times the user clicked the button; useful for detecting double-clicks.

public Point getPoint()
Returns the (x, y) point where the mouse event occurred.

public int getX()
Returns the x-coordinate where the mouse event occurred.

public int getY()
Returns the y-coordinate where the mouse event occurred.

public Object getSource()
Returns a reference to the component that generated the event. Since the return type is written as Object to be general, you'll have to cast it to the type of component you expect.

For example, the following mouse listener could be attached to any component, even the JFrame itself. It would make a message appear any time the user pressed the mouse button:

        // Responds to a mouse click by showing a message of where the user clicked
        public class ClickListener extends MouseInputAdapter {
            public void mousePressed(MouseEvent event) {
                JOptionPane.showMessageDialog(null, "Mouse pressed at position ("
                    + event.getX() + ", " + event.getY() + ")");
            }
        }

Here is an example program that uses a MouseInputAdapter to set a JLabel's text to show the mouse's position as it moves over the JLabel.

        import java.awt.*;
        import javax.swing.*;
        
        public class MousePointGUI {
            public static void main(String[] args) {
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setSize(new Dimension(200, 100));
                frame.setTitle("A frame");
        
                JLabel label = new JLabel();
                label.setText("Move the mouse over me!");
                frame.add(label);
        
                PointListener mListener = new PointListener();
                label.addMouseListener(mListener);
                label.addMouseMotionListener(mListener);
        
                frame.setVisible(true);
            }
        }
        import java.awt.event.*;
        import javax.swing.*;
        import javax.swing.event.*;
        
        public class PointListener extends MouseInputAdapter {
            public void mouseMoved(MouseEvent event) {
                JLabel label = (JLabel) event.getSource();
                label.setText("(" + event.getX() + ", " + event.getY() + ")");
            }
        }

The program produces the following graphical output, shown after the user moves the mouse onto several points on the JLabel:

10.6 2D Graphics

Earlier in this textbook, we introduced a graphical class named DrawingPanel. DrawingPanel was kept simple so that you did not need to learn a lot of details about graphical user interfaces. Now that we're starting to uncover those details, we'll examine how to do our own 2D graphics by hand. This will lead us to the point where we could implement DrawingPanel by ourselves, if we so chose.

10.6.1 Drawing Onto Panels

Earlier in this chapter we saw the JPanel component, which we used as a container for laying out components. JPanels have one other important function: they serve as surfaces onto which we can draw. The JPanel class has a method named paintComponent that draws the panel on the screen. By default, this method draws nothing, so the panel is transparent. If we wanted to change this default behavior, how could we do it?

If you thought of using inheritance, you're on the right track. We can extend JPanel and override the paintComponent method if we want to make a panel that has shapes drawn on it. Here is the signature of the paintComponent method we must override:

        public void paintComponent(Graphics g)

This parameter, Graphics g, should look familiar to you if you used DrawingPanel in previous chapters. Graphics is an object in Java's java.awt package that houses methods for drawing shapes, lines, and images onto a surface. Think of the Graphics as a pen and the JPanel as a sheet of paper.

There is one quirk when extending JPanel and writing a paintComponent method. Remember that when you override a method, you replace the superclass method's previous functionality. We don't want to lose the behavior of paintComponent from JPanel, because it does important things on the interior of the panel. We just want to add to it. Therefore, the first thing you should do when overriding paintComponent is to call super.paintComponent, and pass it your same Graphics g as its argument. (If you don't, you can get strange ghosty afterimages when you drag or resize your window.)

Here's a small class that represents a panel with two rectangles drawn on it:

        import java.awt.*;
        import javax.swing.*;
        
        public class RectPanel extends JPanel {
            public void paintComponent(Graphics g) {
                super.paintComponent(g);  // call JPanel's original version
                g.setColor(Color.RED);
                g.fillRect(20, 40, 70, 30);
                g.setColor(Color.BLUE);
                g.fillRect(60, 10, 20, 80);
            }
        }

Now, in your GUI class, you can create a RectPanel object and add it to your JFrame. It will appear on screen and will have the shapes drawn on it:

        public static void main(String[] args) {
            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(200, 200);
            frame.setTitle("A panel with rectangles");
            
            RectPanel panel = new RectPanel();
            panel.setBackground(Color.WHITE);
            frame.add(panel);
            
            frame.setVisible(true);
        }

The following graphical output is produced:

You might ask yourself, why not just use a DrawingPanel, rather than going to all this trouble. DrawingPanel is a good tool for simple drawing, but there are many things it can't do. For example, we can't make a DrawingPanel part of a larger GUI with other components. The following quick and dirty GUI achieves a mixture of components that would be impossible to do with DrawingPanel:

        public static void main(String[] args) {
            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(400, 200);
            frame.setTitle("A panel with rectangles");
            frame.setLayout(new BorderLayout());
            
            JPanel north = new JPanel(new FlowLayout());
            north.add(new JLabel("Type your name:"));
            north.add(new JTextField(10));
            frame.add(north, BorderLayout.NORTH);
            
            frame.add(new JButton("Submit"), BorderLayout.SOUTH);
            
            RectPanel panel = new RectPanel();
            panel.setBackground(Color.WHITE);
            frame.add(panel, BorderLayout.CENTER);
            
            frame.setVisible(true);
        }

The program looks like this:

For a more complete description of 2D graphical methods and objects, see Chapter 3's discussion of DrawingPanel and Graphics.

10.6.2 Simple Animation with Timers

A JPanel doesn't have to show a simple static drawing. A panel can be animated to show a moving picture, through the use of an object named a Timer. A Timer is an object that, once started, fires an action event at regular intervals. You supply the timer with the action listener to fire and the delay time in milliseconds, and it does the rest. We'll need two things in order to do animation:

  1. An ActionListener object that somehow updates the way the panel will draw itself
  2. A Timer object to invoke that ActionListener at regular intervals, causing the panel to animate

Let's modify our RectPanel from the previous section so that its rectangles move on the panel. To make them move, we must store their positions as data fields in the panel object. We'll change the data fields' values as the program is running, then redraw the rectangles, which will make it look like they're moving. We'll store the desired change-in-x and change-in-y as variables named dx and dy, respectively.

        import java.awt.*;
        import java.awt.event.*;
        import javax.swing.*;

        // Initial version -- will change in the following section.
        public class AnimatedRectPanel extends JPanel {
            private Point p1;  // location of first rectangle
            private Point p2;  // location of second rectangle
            private int dx;    // amount by which to move horizontally
            private int dy;    // amount by which to move vertically

            public AnimatedRectPanel() {
                this.p1 = new Point(20, 40);
                this.p2 = new Point(60, 10);
                this.dx = 5;
                this.dy = 5;
            }

            public void paintComponent(Graphics g) {
                super.paintComponent(g);  // call JPanel's original version
                g.setColor(Color.RED);
                g.fillRect(this.p1.x, this.p1.y, 70, 30);
                g.setColor(Color.BLUE);
                g.fillRect(this.p2.x, this.p2.y, 20, 80);
            }
        }

Let's make a method named move that shifts each rectangle's position slightly. Our action listener can call this move method repeatedly to animate the panel. Let's decide that the first rectangle will move horizontally back and forth on the panel, "bouncing" back and forth off the panel's left and right edges. The second rectangle will move vertically, bouncing off the top and bottom edges. The most straightforward thing to do in our ActionListener is to adjust the two points' coordinates.

        // Initial version -- doesn't turn around.
        public void move() {
            this.p1.x += this.dx;
            this.p2.y += this.dy;
        }

However, since the rectangles are supposed to bounce off the edges, we need to check to see if a rectangle has hit either edge. We'll know the rectangle has hit the edge when its left edge has dropped to 0, or when its right edge (the left edge plus the width) has passed the panel's width. The calculation is similar for the y, except that we'll look at the panel's height.

        public void move() {
            this.p1.x += this.dx;
            this.p2.y += this.dy;
            if (this.p1.x <= 0 || this.p1.x + 70 >= this.getWidth()) {
                this.dx = -this.dx;  // rectangle 1 has hit left/right edge
            }
            if (this.p2.y <= 0 || this.p2.y + 80 >= this.getHeight()) {
                this.dy = -this.dy;  // rectangle 2 has hit top/bottom edge
            }
        }

The next step is to create an ActionListener that moves the panel each time it is invoked. We'll make this class accept an AnimatedRectPanel as an argument to its constructor and call move() on that panel when its action listener is invoked.

There's one last thing we need to know about animating JPanels. When we change the way the JPanel should be drawn, we have to call a method named repaint to instruct the JPanel to draw itself again. If we don't call repaint, we won't see any change on the screen at all; this is a common bug.

Here's the complete ActionListener that moves and repaints the panel. It is implemented as an inner class inside AnimatedRectPanel, so that it can call the move and repaint methods of that object.

        public class RectangleMover implements ActionListener {
            public void actionPerformed(ActionEvent event) {
                move();
                repaint();
            }
        }

Once our ActionListener is complete, we have to create and start the timer that invokes the ActionListener. Let's add code for this to the constructor for the AnimatedRectPanel.

        // set up the timer to animate the motion of the rectangles
        RectangleMover mover = new RectangleMover();
        Timer time = new Timer(100, mover);  // update every 100 ms
        time.start();

Here's the complete AnimatedRectPanel class that contains an ActionListener that incorporates all of these features.

        import java.awt.*;
        import java.awt.event.*;
        import javax.swing.*;
        
        public class AnimatedRectPanel extends JPanel {
            private Point p1;  // location of first rectangle
            private Point p2;  // location of second rectangle
            private int dx;    // amount by which to change position horizontally
            private int dy;    // amount by which to change position vertically
            
            public AnimatedRectPanel() {
                this.p1 = new Point(20, 40);
                this.p2 = new Point(60, 10);
                this.dx = 5;
                this.dy = 5;
        
                // set up the timer to animate the motion of the rectangles
                RectangleMover mover = new RectangleMover();
                Timer time = new Timer(100, mover);  // update every 100 ms
                time.start();
            }
        
            // draws two rectangles on this panel on the screen
            public void paintComponent(Graphics g) {
                super.paintComponent(g);  // call JPanel's original version
                g.setColor(Color.RED);
                g.fillRect(this.p1.x, this.p1.y, 70, 30);
                g.setColor(Color.BLUE);
                g.fillRect(this.p2.x, this.p2.y, 20, 80);
            }
        
            // adjusts the position of both rectangles    
            public void move() {
                this.p1.x += this.dx;
                this.p2.y += this.dy;
                if (this.p1.x <= 0 || this.p1.x + 70 >= this.getWidth()) {
                    this.dx = -this.dx;  // rectangle 1 has hit left/right edge
                }
                if (this.p2.y <= 0 || this.p2.y + 80 >= this.getHeight()) {
                    this.dy = -this.dy;  // rectangle 2 has hit top/bottom edge
                }
            }
            
            // redraws the rectangle panel at timed intervals
            public class RectangleMover implements ActionListener {
                public void actionPerformed(ActionEvent event) {
                    move();
                    repaint();
                }
            }
        }

The client code to display this new animated rectangle panel looks identical to the UseRectPanel class shown before, except the name AnimatedRectPanel appears in place of RectPanel. The graphical window produced as output animates, just as we intended.

Here is a list of the methods you may use from the Timer class:

Useful Methods of Timer objects
Method Description
public Timer(int msDelay, ActionListener listener) Creates a Timer that activates the given action listener every msDelay milliseconds.
public void start() tells the timer to start ticking and activating its action listener
public void stop() tells the timer to stop ticking and not activate its action listener

10.7 Case Study: Demystifying DrawingPanel

In previous chapters, we have used the DrawingPanel class for simple 2D graphics. In this final section, we'll analyze the DrawingPanel in detail to understand how it works.

The start of the DrawingPanel class looks a lot like the other GUIs we have used in this chapter, declaring various graphical components as data fields. The main fields are the overall window frame, a panel on which the user can draw, and a status bar label that shows the mouse's position. The DrawingPanel also keeps a Graphics2D object as a data field, which will represent the pen used to draw on the main panel. DrawingPanel declares a constant delay which is used for a Timer, seen later.

        public class DrawingPanel {
            public static final int DELAY = 250;  // ms delay between repaints

            private JFrame frame;         // overall window frame
            private JPanel panel;         // overall drawing surface
            private Graphics2D g2;        // graphics context for painting
            private JLabel statusBar;     // status bar showing mouse position

The constructor of the DrawingPanel initializes the data fields, as well as constructing a BufferedImage to serve as the persistent buffer where shapes and lines can be drawn. The image is set to be the icon of an onscreen JLabel.

        public DrawingPanel(int width, int height) {
            // set up the empty image onto which we will draw
            BufferedImage image = new BufferedImage(width, height, TYPE_INT_ARGB);
            this.g2 = (Graphics2D) image.getGraphics();
            this.g2.setColor(Color.BLACK);

            JLabel label = new JLabel();
            label.setIcon(new ImageIcon(image));

            this.panel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
            this.panel.setBackground(Color.WHITE);
            this.panel.setPreferredSize(new Dimension(width, height));
            this.panel.add(label);

A special mouse listener is attached to the main panel, so that whenever the mouse moves on the DrawingPanel, the status bar text can be updated to show the mouse position.

            // the status bar that shows the mouse position
            this.statusBar = new JLabel(" ");
            this.statusBar.setBorder(BorderFactory.createLineBorder(Color.BLACK));

            StatusBarMouseAdapter mouse = new StatusBarMouseAdapter();
            this.panel.addMouseListener(mouse);
            this.panel.addMouseMotionListener(mouse);

Here is the code for the mouse listener:

        class StatusBarMouseAdapter extends MouseInputAdapter {
            public void mouseMoved(MouseEvent e) {
                statusBar.setText("(" + e.getX() + ", " + e.getY() + ")");
            }
        }

Next, the main window frame is created and shown on the screen. The last action in the DrawingPanel's constructor is to create and start a Timer, which periodically repaints the screen. This is done to ensure that every shape the user draws will appear on the screen, even if the client's code takes a few seconds to run.

            // set up the JFrame
            this.frame = new JFrame("DrawingPanel");
            this.frame.setResizable(false);
            this.frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
            this.frame.add(panel);
            this.frame.add(statusBar, SOUTH);
            this.frame.pack();
            this.frame.setVisible(true);
            this.frame.toFront();

            // start a repaint timer so that the screen will update
            TimerListener listener = new TimerListener();
            Timer timer = new Timer(DELAY, listener);
            timer.start();

The ActionListener attached to the timer simply repaints the main panel:

        class TimerListener implements ActionListener {
            public void actionPerformed(ActionEvent e) {
                this.panel.repaint();
            }
        }

As you can see, the DrawingPanel uses concepts that have been introduced in this chapter, and it now consists of programming constructs that you have seen. If you wish, you could modify or enhance the functionality of DrawingPanel yourself. Here is the complete code for the DrawingPanel class:

        // The DrawingPanel class provides a simple interface for drawing persistent
        // images using a Graphics object.
        
        import java.awt.*;
        import java.awt.event.*;
        import java.awt.image.*;
        import javax.swing.*;
        import javax.swing.event.*;
        import static javax.swing.JFrame.*;
        import static java.awt.BorderLayout.*;
        import static java.awt.image.BufferedImage.*;
        import static java.awt.RenderingHints.*;
        
        public class DrawingPanel {
            public static final int DELAY = 250;  // ms delay between repaints
        
            private JFrame frame;         // overall window frame
            private JPanel panel;         // overall drawing surface
            private Graphics g;           // graphics context for painting
            private JLabel statusBar;     // status bar showing mouse position
        
            // constructs a drawing panel of given width and height enclosed in a window
            public DrawingPanel(int width, int height) {
                // set up the empty image onto which we will draw
                BufferedImage image = new BufferedImage(width, height, TYPE_INT_ARGB);
                this.g = image.getGraphics();
                this.g.setColor(Color.BLACK);
                JLabel label = new JLabel();
                label.setIcon(new ImageIcon(image));
        
                this.panel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
                this.panel.setBackground(Color.WHITE);
                this.panel.setPreferredSize(new Dimension(width, height));
                this.panel.add(label);
        
                // the status bar that shows the mouse position
                this.statusBar = new JLabel(" ");
                this.statusBar.setBorder(BorderFactory.createLineBorder(Color.BLACK));
                
                StatusBarMouseAdapter mouse = new StatusBarMouseAdapter();
                this.panel.addMouseListener(mouse);
                this.panel.addMouseMotionListener(mouse);
        
                // set up the JFrame
                this.frame = new JFrame("Drawing Panel");
                this.frame.setResizable(false);
                this.frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
                this.frame.add(panel);
                this.frame.add(statusBar, SOUTH);
                this.frame.pack();
                this.frame.setVisible(true);
                this.frame.toFront();
        
                // start a repaint timer so that the screen will update
                TimerListener listener = new TimerListener();
                Timer timer = new Timer(DELAY, listener);
                timer.start();
            }
        
            // obtain the Graphics object to draw on the panel
            public Graphics getGraphics() {
                return this.g;
            }
        
            // set the background color of the drawing panel
            public void setBackground(Color c) {
                this.panel.setBackground(c);
            }
        
            // show or hide the drawing panel on the screen
            public void setVisible(boolean visible) {
                this.frame.setVisible(visible);
            }
        
            // makes the program pause for the given amount of time, for animation
            public void sleep(int millis) {
                try {
                    Thread.sleep(millis);
                } catch (InterruptedException e) {}
            }
        
            // makes drawing panel become the frontmost window on the screen
            public void toFront() {
                this.frame.toFront();
            }
            
            // used for an internal timer that repeatedly repaints the screen
            class TimerListener implements ActionListener {
                public void actionPerformed(ActionEvent e) {
                    this.panel.repaint();
                }
            }
        
            // draws the status bar text when the mouse moves
            class StatusBarMouseAdapter extends MouseInputAdapter {
                public void mouseMoved(MouseEvent e) {
                    statusBar.setText("(" + e.getX() + ", " + e.getY() + ")");
                }
            }
        }

 

10.8 Chapter Summary


Stuart Reges
Marty Stepp
Last modified: Tue Jan 3 12:51:10 PST 2006

Appendix A
Answers to Selected Self-Check Exercises

Copyright © 2005 by Stuart Reges and Marty Stepp

Chapter 1

  1. The legal identifiers shown are println, AnnualSalary, ABC, sum_of_data, _average, and B4.
    • On line 1, the class name should be LotsOfErrors (no space).
    • On line 2, the word 'void' should appear after 'static'.
    • On line 2, String should be String[] .
    • On line 3, System.println should be System.out.println .
    • On line 3, "Hello, world!) should be "Hello, world!") .
    • On line 4, there should be a semicolon after message() .
    • On line 7, there should be () after message .
    • On line 8, System.out println should be System.out.println .
    • On line 8, cannot"; should be cannot"); .
    • On line 9, the phrase "errors" cannot appear inside a String. 'errors' would work.
    • On line 11, there should be a closing } brace.
    • Syntax error: The program would not compile because its class name (Demonstration) would not match its file name (Example.java).
    • Different program output: The program would not run because Java would be unable to find the main method.
    • Different program output: There would now be a blank line between the two printed messages.
    • Syntax error: The program would not compile because the main method would be calling a method displayRule that no longer existed.
    • No effect. The program would still compile successfully and produce the same output.
    • Different program output: The output would now have no line break between "The first rule " and "of Java Club is," in its output.
  2. "Quotes"
    Slashes \//
    How '"confounding' "\" it is!
    
  3. Shaq is 7'1"
    The string "" is an empty message.
    \'""
    
  4. Dear "DoubleSlash" magazine,
    	Your publication confuses me.  Is it a
    \\ slash or a //// slash that I should use?
    
    Sincerely,
    Susan "Suzy" Smith
    
  5. System.out.println("\"Several slashes are sometimes seen,\"");
    System.out.println("said Sally.  \"I've said so myself.\"  See?");
    System.out.println("\\ / \\\\ // \\\\\\ ///");
    
  6. System.out.println("This is a test of your");
    System.out.println("knowledge of \"quotes\" used");
    System.out.println("in 'string constants.'");
    System.out.println();
    System.out.println("You're bound to \"get it right\"");
    System.out.println("if you read the section on");
    System.out.println("''quotes.''");
    
  7. System.out.println("What is the difference between");
    System.out.println("a ' and a \"?  Or between a \" and a \\\"?");
    System.out.println();
    System.out.println("One is what we see when we're typing our program.");
    System.out.println("The other is what appears on the \"console.\");
    
  8. public class Stewie { public static void main(String[] args) { System.out.println("//////////////////////"); System.out.println("|| Victory is mine! ||"); System.out.println("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"); } }
  9. public class Spikey { public static void main(String[] args) { System.out.println(" \\/"); System.out.println(" \\\\//"); System.out.println("\\\\\\///"); System.out.println("///\\\\\\"); System.out.println(" //\\\\"); System.out.println(" /\\"); } }
  10. public class PrintMessage { public static void main(String[] args) { System.out.println("A well-formed Java program has a main"); System.out.println("method with { and } braces."); System.out.println(); System.out.println("A System.out.println statement has ( and )"); System.out.println("and usually a String that starts and ends"); System.out.println("with a \" character. (But we type \\\" instead!)"); } }
  11. Inside first method
    Inside third method
    Inside first method
    Inside second method
    Inside first method
    Inside second method
    Inside first method
    Inside third method
    Inside first method
    Inside second method
    Inside first method
    
  12. Inside first method
    Inside first method
    Inside second method
    Inside first method
    Inside third method
    Inside second method
    Inside first method
    Inside first method
    Inside second method
    Inside first method
    Inside third method
    
  13. Inside second method
    Inside first method
    Inside first method
    Inside second method
    Inside first method
    Inside third method
    Inside first method
    Inside second method
    Inside first method
    
  14. I am method 1.
    I am method 1.
    I am method 2.
    I am method 3.
    I am method 1.
    I am method 1.
    I am method 2.
    I am method 1.
    I am method 2.
    I am method 3.
    I am method 1.
    
  15. I am method 1.
    I am method 1.
    I am method 1.
    I am method 2.
    I am method 3.
    I am method 1.
    I am method 2.
    I am method 1.
    I am method 1.
    I am method 2.
    I am method 3.
    
  16. I am method 1.
    I am method 2.
    I am method 1.
    I am method 1.
    I am method 2.
    I am method 3.
    I am method 1.
    I am method 1.
    I am method 2.
    
  17. public class Stewie2 { public static void main(String[] args) { System.out.println("//////////////////////"); printVictory(); printVictory(); printVictory(); printVictory(); printVictory(); } public static void printVictory() { System.out.println("|| Victory is mine! ||"); System.out.println("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"); } }
  18. public class DrawBoxes { public static void main(String[] args) { System.out.println(" _______"); System.out.println(" / \"); System.out.println(" / \"); System.out.println(" -\"-'-\"-'-\"-\""); System.out.println(" \ /"); System.out.println(" \_______/"); } }
  19. public class DrawBoxes2 { public static void main(String[] args) { drawTop(); drawBottom(); drawMiddle(); drawTop(); drawBottom(); drawMiddle(); drawBottom(); drawTop(); drawMiddle(); drawBottom(); } public static void drawTop() { System.out.println(" _______"); System.out.println(" / \"); System.out.println(" / \"); } public static void drawMiddle() { System.out.println(" -\"-'-\"-'-\"-\""); } public static void drawBottom() { System.out.println(" \ /"); System.out.println(" \_______/"); } }
  20. public class RocketShip { public static void main(String[] args) { printTop(); printSquare(); printLabel(); printSquare(); printTop(); } public static void printTop() { System.out.println(" /\\ /\\"); System.out.println(" / \\ / \\"); System.out.println(" / \\ / \\"); } public static void printSquare() { System.out.println("+------+ +------+"); System.out.println("| | | |"); System.out.println("| | | |"); System.out.println("+------+ +------+"); } public static void printLabel() { System.out.println("|United| |United|"); System.out.println("|States| |States|"); } }
  21. // This program prints a pattern of starry figures. public class ManyStars { public static void main(String[] args) { printFigure1(); System.out.println(); printFigure2(); System.out.println(); printFigure3(); } public static void printFigure1() { printHorizontalBar(); printX(); } public static void printFigure2() { printHorizontalBar(); System.out.println(" * *"); printHorizontalBar(); } public static void printFigure3() { System.out.println(" *"); System.out.println(" *"); System.out.println(" *"); printFigure1(); } public static void printHorizontalBar() { System.out.println("*****"); System.out.println("*****"); } public static void printX() { System.out.println(" * *"); System.out.println(" *"); System.out.println(" * *"); } }
  22. public class GiveAdvice { public static void main(String[] args) { System.out.println("Programs can be easy or difficult"); System.out.println("to read, depending upon their format."); System.out.println(); System.out.println("Everyone, including yourself, will be"); System.out.println("happier if you choose to format your"); System.out.println("Programs."); } }
  23. public class Messy { public static void main(String[] args) { message(); System.out.println(); message(); } public static void message() { System.out.println("I really wish that"); System.out.println("I had formatted my source"); System.out.println("code correctly!"); } }

Chapter 2

  1. 22, -1, and -6875309 are legal int literals.
  2. 8
    11
    6
    4
    33
    -16
    6.4
    9.0
    9.6
    2.2
    
  3. 'f'
    'j'
    
  4. 11
    "2 + 2 34"
    "2 2 + 3 4"
    "7 2 + 2"
    "2 + 2 7"
    "(2 + 2) 7"
    
  5. The following are four legal ways to declare the two variables:
    1. int width;
      int height;
      
    2. int height;
      int width;
      
    3. int width, height;
      
    4. int height, width;
      
  6. int age;
    String gender;
    double height;
    int weight;
    
  7. String year;
    int numberOfCourses;
    double gpa;
    
  8. Last digit: number % 10
  9. Second-to-last digit: (number % 100) / 10 or (number / 10) % 10
    Third-to-last digit: (number % 1000) / 100 or (number / 100) % 10
  10. first: 19
    second: 8
    
  11. The code swaps the values of the variables first and second.
  12. int first = 8, second = 19;
    first += second;
    second = first - second;
    first -= second;
    
  13. System.out.println("Twas brillig and the ");
    System.out.println("   slithy toves did gyre and");
    System.out.println("gimble");
    System.out.println();
    System.out.println("in the wabe.");
    
  14. a: 7
    b: 10
    c: 16
    
  15. int count = 26; for (int i = 1; i <= count; i++) { System.out.print("--"); } System.out.println(); for (int i = 0; i < count; i++) { System.out.print( (char)('a' + i) ); System.out.print( (char)('A' + i) ); } System.out.println(); for (int i = 1; i <= count; i++) { System.out.print(i % 10); System.out.print(i % 10); } System.out.println(); for (int i = 1; i <= count; i++) { System.out.print("--"); } System.out.println();
  16. for (int i = 1; i <= 6; i++) System.out.print(" |"); System.out.println(); for (int i = 1; i <= 6; i++) { for (int j = 1; j <= 10; j++) System.out.print(j % 10); } System.out.println();
  17. public class NumberOutput2 { public static final int COUNT = 6; public static final int INNER_COUNT = 10; public static void main(String[] args) { for (int i = 1; i <= COUNT; i++) { for (int j = 1; j <= INNER_COUNT - 1; j++) System.out.print(" "); System.out.print("|"); } System.out.println(); for (int i = 1; i <= COUNT; i++) { for (int j = 1; j <= INNER_COUNT; j++) System.out.print(j % INNER_COUNT); } System.out.println(); } }
  18. for (int i = 1; i <= 3; i++) {
        for (int j = 0; j <= 9; j++) {
            for (int k = 1; k <= 3; k++) {
                System.out.print(j);
            }
        }
        System.out.println();
    }
    
  19. for (int i = 1; i <= 5; i++) {
        for (int j = 9; j >= 0; j--) {
            for (int k = 1; k <= 5; k++) {
                System.out.print(j);
            }
        }
        System.out.println();
    }
    
  20. for (int i = 1; i <= 4; i++) {
        for (int j = 9; j >= 0; j--) {
            for (int k = 1; k <= j; k++) {
                System.out.print(j);
            }
        }
        System.out.println();
    }
    
  21. int width = 11; for (int i = 0; i < width / 2; i++) { int numDashes = width / 2 - i; for (int j = 1; j <= numDashes; j++) { System.out.print("-"); } int number = width - (2 * numDashes); for (int j = 1; j <= number; j++) { System.out.println(number); } for (int j = 1; j <= numDashes; j++) { System.out.print("-"); } System.out.println(); }
    • The loop prints every third number, not every odd number. The statement count = count + 2 on line 8 should be moved into the loop header instead of count++.
    • On line 12, the variable count is no longer defined (its scope is only within the for loop above). It should be declared before the loop begins rather than inside the loop's header.
    • On line 13, too large a value is printed for the final odd number; count should be printed, not count + 2.
    • On line 20, it is illegal to try to assign a new value to a constant such as MAX_ODD. One way to fix this would be to write two methods: one to print the odds up to 21 and a second to print the odds up to 11. But this is admittedly redundant. The better solution to this kind of problem is called parameter passing, which will be seen in later chapters.
  22. 4
    2
    
  23. The result is: 55
  24. 24  1
    22  2
    19  3
    15  4
    10  5
    
  25. ****!****!****!
    ****!****!****!
    
  26. ************!
    ************!
    
  27. public class Squared { public static final int MAX = 10; public static void main(String[] args) { for (int i = 1; i <= MAX; i++) { System.out.print(i * i + " "); } System.out.println(); } }
  28. public class Squared1 { public static final int MAX = 10; public static void main(String[] args) { int number = 1; int increment = 3; for (int i = 1; i <= MAX; i++) { System.out.print(number + " "); number += increment; increment += 2; } System.out.println(); } }

    or:

    public class Squared2 { public static final int MAX = 10; public static void main(String[] args) { for (int i = 1; i <= MAX; i++) { int number = 0; for (int j = 1; j <= number; j++) number += i; System.out.print(number + " "); } System.out.println(); } }
  29. overall algorithm:
        draw a horizontal line
        draw 3 lines of bars
        draw a line
        draw 3 lines of bars
        draw a line
    
    how to draw a horizontal line:
        print a +
        print 3 = signs
        System.out.print("+");
        print a +
        print 3 = signs
        print a +
    
    how to draw a line of bars:
        print a |
        print 3 spaces
        print a |
        print 3 spaces
        print a |
    
  30. public class Window { public static final int COUNT = 3; public static void main(String[] args) { drawLine(); for (int i = 1; i <= COUNT; i++) { drawBars(); } drawLine(); for (int i = 1; i <= COUNT; i++) { drawBars(); } drawLine(); } public static void drawLine() { System.out.print("+"); for (int i = 1; i <= COUNT; i++) System.out.print("="); System.out.print("+"); for (int i = 1; i <= COUNT; i++) System.out.print("="); System.out.println("+"); } public static void drawBars() { System.out.print("|"); for (int i = 1; i <= COUNT; i++) System.out.print(" "); System.out.print("|"); for (int i = 1; i <= COUNT; i++) System.out.print(" "); System.out.println("|"); } }

Chapter 3

  1. 1 2 3 4 5
    1 2 3 4 5 6 7
    1 2 3 4
    number = 8
    
  2. whom and who like it
    it and him like whom
    whom and him like him
    stu and boo like who
    her and him like who
    
  3. three times two = 6
    1 times three = 28
    1 times 1 = 42
    three times 1 = 2
    1 times eight = 20
    
  4. efgnopqrs
    
    qr
    
  5. public static int smallestOf3(int n1, int n2, int n3) { return Math.min(n1, Math.min(n2, n3)); }
  6. public static int largerAbsVal(int n1, int n2) { return Math.max(Math.abs(n1), Math.abs(n2)); }
  7. public static int largestAbsVal(int n1, int n2, int n3) { int larger12 = Math.max(Math.abs(n1), Math.abs(n2)); int larger23 = Math.max(Math.abs(n2), Math.abs(n3)); return Math.max(larger12, larger23); }

    or:

    public static int largestAbsVal2(int n1, int n2, int n3) { return Math.max(largerAbsVal(n1, n2), largerAbsVal(n2, n3)); }
  8. public static void printNumbers(int max) { for (int i = 1; i <= max; i++) { System.out.print("[" + i + "]"); } System.out.println(); // to end the line of output }
  9. public static void printPowersOf2(int max) { for (int i = 0; i <= max; i++) { System.out.print((int) Math.pow(2, i) + " "); } System.out.println(); // to end the line of output }

    or (without using the Math class):

    public static void printPowersOf2_2(int max) { int power = 1; for (int i = 0; i <= max; i++) { System.out.print(power + " "); power += power; } System.out.println(); // to end the line of output }
  10. public static void printPowersOfN(int base, int exp) { for (int i = 0; i <= exp; i++) { System.out.print((int) Math.pow(base, i) + " "); } System.out.println(); // to end the line of output }

    or (without using the Math class):

    public static void printPowersOfN_2(int base, int exp) { int power = 1; for (int i = 0; i <= exp; i++) { System.out.print(power + " "); power = power * base; } System.out.println(); // to end the line of output }
  11. public static void quadratic(int a, int b, int c) { double determinant = b * b - 4 * a * c; double root1 = (-b + Math.sqrt(determinant)) / (2 * a); double root2 = (-b - Math.sqrt(determinant)) / (2 * a); System.out.println("First root = " + root1); System.out.println("Second root = " + root2); }
  12. public static double hypotenuseLength(double a, double b) { return Math.sqrt(a * a + b * b); }
  13. "arm"
  14. "EGADs"
  15. "berneti"
  16. str1 is "Project Mayhem"
    str2 is "Phantasmagorical"
    

    In other words, the variables' values are unchanged. This is because the statements did not reassign any new value to str1 or str2.

  17. public static void vertical(String str) { for (int i = 0; i < str.length(); i++) { System.out.println(str.charAt(i)); } }
  18. public static String reverse(String str) { String reversed = ""; for (int i = 0; i < str.length(); i++) { reversed = str.charAt(i) + reversed; } return reversed; }
  19. (2, 5)
    (-3, 6)
    (-5, -2)
    
  20. public static void swapPoints(Point p1, Point p2) { int temp = p1.x; p2.x = p1.x; p1.x = temp; // swap x values temp = p1.y; p2.y = p1.y; p1.y = temp; // swap y values }
  21. The Draw7 program draws black circles that get smaller and smaller, each circle with its right and bottom edges touching the right and bottom corners of the window.

  22. import java.awt.*; public class Draw4 { public static void main(String[] args) { DrawingPanel panel = new DrawingPanel(220, 150); panel.setBackground(Color.YELLOW); Graphics g = panel.getGraphics(); g.setColor(Color.BLUE); g.fillOval(50, 25, 40, 40); g.fillOval(130, 25, 40, 40); g.setColor(Color.RED); g.fillRect(70, 45, 80, 80); g.setColor(Color.BLACK); g.drawLine(70, 85, 150, 85); } }
  23. import java.awt.*; public class Draw5 { public static void main(String[] args) { DrawingPanel panel = new DrawingPanel(450, 150); panel.setBackground(Color.YELLOW); Graphics g = panel.getGraphics(); drawFigure(g, new Point(50, 25)); drawFigure(g, new Point(250, 45)); } public static void drawFigure(Graphics g, Point location) { g.setColor(Color.BLUE); g.fillOval(location.x, location.y, 40, 40); g.fillOval(location.x + 80, location.y, 40, 40); g.setColor(Color.RED); g.fillRect(location.x + 20, location.y + 20, 80, 80); g.setColor(Color.BLACK); g.drawLine(location.x + 20, location.y + 60, location.x + 100, location.y + 60); } }
  24. public static void showDesign() { DrawingPanel panel = new DrawingPanel(200, 200); panel.setBackground(Color.WHITE); Graphics g = panel.getGraphics(); g.setColor(Color.BLACK); for (int i = 1; i <= 4; i++) { int x = i * 20; int y = i * 20; int w = (10 - 2 * i) * 20; int h = (10 - 2 * i) * 20; g.drawRect(x, y, w, h); } }
  25. public static void showDesign(int width, int height) { DrawingPanel panel = new DrawingPanel(width, height); panel.setBackground(Color.WHITE); Graphics g = panel.getGraphics(); g.setColor(Color.BLACK); for (int i = 1; i <= 4; i++) { int x = i * width / 10; int y = i * height / 10; int w = (10 - 2 * i) * width / 10; int h = (10 - 2 * i) * height / 10; g.drawRect(x, y, w, h); } }
  26. import java.awt.*; public class Draw6 { public static void main(String[] args) { int size = 170; DrawingPanel panel = new DrawingPanel(size, size); panel.setBackground(Color.WHITE); Graphics g = panel.getGraphics(); g.setColor(Color.BLACK); int x = 0; int y = 10; int len = size - 10; for (int i = 0; i < 8; i++) { g.drawLine(x, y, x + len, y); // right x += len; len -= 10; g.drawLine(x, y, x, y + len); // down y += len; g.drawLine(x, y, x - len, y); // left x -= len; len -= 10; g.drawLine(x, y, x, y - len); // up y -= len; } } }

Chapter 4

  1. Scanner console = new Scanner(System.in);
    
    System.out.print("Type a number: ");
    int number = console.nextInt();
    
    System.out.println("Your number times 2 = " + (number * 2));
    
  2. System.out.print("Please enter your full name: "); Scanner console = new Scanner(System.in); String name = console.nextLine(); int indexOfSpace = name.indexOf(" "); String firstName = name.substring(0, indexOfSpace); String lastName = name.substring(indexOfSpace + 1); System.out.println("Your name in reverse order is " + lastName + ", " + firstName);
  3. // read the input
    Scanner console = new Scanner(System.in);
    
    System.out.print("What is your phrase? ");
    String phrase = console.nextLine();
    
    System.out.print("How many times should I repeat the phrase? ");
    int count = console.nextInt();
    
    // print the phrase
    for (int i = 1; i <= count; i++) {
        System.out.println(phrase);
    }
    
  4. System.out.print("Enter a student record: "); Scanner console = new Scanner(System.in); String name = console.next(); int count = console.nextInt(); int sum = 0; for (int i = 1; i <= count; i++) { sum += console.nextInt(); } double average = (double) sum / count; System.out.println(name + "'s grade is " + average);
    • z % 2 == 1
    • z <= Math.sqrt(y)
    • y > 0
    • x % 2 != y % 2
    • y % z == 0
    • z != 0
    • Math.abs(y) > Math.abs(z)
    • (x >= 0) == (z < 0)
    • y % 10 == y
    • z >= 0
    • x % 2 == 0
    • Math.abs(x - y) < Math.abs(z - y)
    • true
    • false
    • true
    • false
    • true
    • false
    • false
    • true
    • true
  5. The code incorrectly uses an if/else/if/else/if pattern, when it should really use an if/if/if pattern, because the three conditions are not mutually exclusive of each other. The given code only answers correctly when zero or one of the numbers are odd; if more than one is odd, the code only enters one branch and does not properly increment the counter multiple times.

    The following version of the code fixes the problem:

        if (n1 % 2 == 1) {
            count++;
        }
        if (n2 % 2 == 1) {
            count++;
        }
        if (n3 % 2 == 1) {
            count++;
        }
    

    Or, the following version achieves the same thing without the need for if/else statements:

        count = n1 % 2 + n2 % 2 + n3 % 2;
    
  6.     Scanner console = new Scanner(System.in);
    
        System.out.print("Type a number: ");
        int number = console.nextInt();
    
        if (number % 2 == 0) {
            System.out.println("even");
        } else {
            System.out.println("odd");
        }
    
  7. System.out.print("How many numbers do you want to enter? "); Scanner console = new Scanner(System.in); int count = console.nextInt(); System.out.print("Number 1: "); int smallest = console.nextInt(); int largest = smallest; for (int i = 1; i <= count; i++) { int num = console.nextInt(); if (num > largest) { largest = num; } else if (num < smallest) { smallest = num; } } System.out.println("Smallest = " + smallest); System.out.println("Largest = " + largest);
  8. public static int medianOf3(int n1, int n2, int n3) { if (n1 < n2) { if (n2 < n3) { return n2; } else { return n3; } } else { if (n1 < n3) { return n1; } else { return n3; } } }

    or:

    public static int medianOf3_2(int n1, int n2, int n3) { return Math.max(Math.max(Math.min(n1, n2), Math.min(n2, n3)), Math.min(n1, n3)); }
  9. public static void printRange(int n1, int n2) { if (n1 <= n2) { for (int i = n1; i <= n2; i++) { System.out.print(i + " "); } } else { for (int i = n1; i >= n2; i--) { System.out.print(i + " "); } } System.out.println(); }
  10. public static void printTriangleType(int s1, int s2, int s3) { if (s1 == s2) { if (s2 == s3) { System.out.println("equilateral"); } } else if (s2 == s3) { System.out.println("isosceles"); } else { System.out.println("scalene"); } }
  11. public static void moneyMultiply(int sum, int total, int count1, int count2) { Scanner console = new Scanner(System.in); System.out.print("Is your money multiplied 1 or 2 times? "); int times = console.nextInt(); System.out.print("And how much are you contributing? "); int donation = console.nextInt(); sum += times * donation; total += donation; if (times == 1) { count1++; } else if (times == 2) { count2++; } }

    If the user could type any number, the code might need additional if statements to increment the proper count variable. If the user could type anything, even a non-integer, the code might need to use the hasNextInt method of the Scanner to ensure valid input before proceeding.

  12. second
    third
    
  13. Scanner console = new Scanner(System.in); System.out.print("What color do you want? "); String choice = console.nextLine(); if (choice.equalsIgnoreCase("r")) { System.out.println("You have chosen Red."); } else if (choice.equalsIgnoreCase("g")) { System.out.println("You have chosen Green."); } else if (choice.equalsIgnoreCase("b")) { System.out.println("You have chosen Blue."); } else { System.out.println("Unknown color: " + choice); }
  14. Scanner console = new Scanner(System.in); System.out.print("Enter a card: "); String rank = console.next(); String suit = console.next(); if (rank.equals("2")) { rank = "Two"; } else if (rank.equals("3")) { rank = "Three"; } else if (rank.equals("4")) { rank = "Four"; } else if (rank.equals("5")) { rank = "Five"; } else if (rank.equals("6")) { rank = "Six"; } else if (rank.equals("7")) { rank = "Seven"; } else if (rank.equals("8")) { rank = "Eight"; } else if (rank.equals("9")) { rank = "Nine"; } else if (rank.equals("10")) { rank = "Ten"; } else if (rank.equals("J")) { rank = "Jack"; } else if (rank.equals("Q")) { rank = "Queen"; } else if (rank.equals("K")) { rank = "King"; } else { // rank.equals("A") rank = "Ace"; } if (suit.equals("C")) { suit = "Clubs"; } else if (suit.equals("D")) { suit = "Diamonds"; } else if (suit.equals("H")) { suit = "Hearts"; } else { // suit.equals("S") suit = "Spades"; } System.out.println(rank + " of " + suit);
  15. Scanner console = new Scanner(System.in); System.out.print("Type one or more words: "); String input = console.nextLine(); input = input.toLowerCase(); // compare each pair of characters boolean palindrome = true; for (int i = 0; i < input.length() / 2; i++) { if (input.charAt(i) != input.charAt(input.length() - 1 - i)) { palindrome = false; } } if (palindrome) { System.out.println(input + " is a palindrome!"); } else { System.out.println(input + " is not a palindrome."); }

    or (if we use the reverse method from last chapter's self-check):

    Scanner console = new Scanner(System.in); System.out.print("Type one or more words: "); String input = console.nextLine(); input = input.toLowerCase(); // If it is a palindrome, it will be the same forwards as backwards... String backwards = reverse(input); if (input.equals(backwards)) { System.out.println(input + " is a palindrome!"); } else { System.out.println(input + " is not a palindrome."); }
  16. Invalid values are when a = 0 (because it makes the denominator of the equation equal 0), or when b2 - 4ac < 0, because then it has no real square root. public static void quadratic(int a, int b, int c) { double determinant = b * b - 4 * a * c; if (a == 0) { throw new IllegalArgumentException("Invalid a value of 0"); } if (determinant < 0) { throw new IllegalArgumentException("Invalid determinant"); } double root1 = (-b + Math.sqrt(determinant)) / (2 * a); double root2 = (-b - Math.sqrt(determinant)) / (2 * a); System.out.println("First root = " + root1); System.out.println("Second root = " + root2); }

Chapter 5

    • executes body 10 times
    • 1 11 21 31 41 51 61 71 81 91
    • executes body 3 times
    • 2 4 16
    • executes body 0 times
    • (no output)
    • executes body 5 times
    • bbbbbabbbbb
    • loops infinitely
    • 250
      250
      250
      
      .... (repeats forever)
    • executes body 7 times
    • 10
      5
      2
      1
      0
      0
      0
      
  1. Method Call                     Output
    --------------------------------------
    mystery(1);                     1 0
    mystery(6);                     4 2
    mystery(19);                    16 4
    mystery(39);                    32 5
    mystery(74);                    64 6
    
  2. Method Call                     Value Returned
    ----------------------------------------------
    mystery(3, 3)                    3
    mystery(5, 3)                    1
    mystery(2, 6)                    2
    mystery(12, 18)                  6
    mystery(30, 75)                  15
    
  3. int n = 1;
    while (n <= max) {
        System.out.println(n);
        n++;
    }
    
  4. The code will always print that the ZIP code was invalid at least once, even if the user's initial number typed is valid. The correct solution is to initialize the number using the user's first input instead of 0, like this:
    int number = console.nextInt();
    
  5. public static void printFactors(int n) { System.out.print(1); for (int i = 2; i <= n; i++) { if (n % i == 0) { // i is a factor System.out.print(" and " + i); } } System.out.println(); }
  6. public static void printLetters(String str) { if (str.length() > 0) { System.out.print(str.charAt(0)); for (int i = 1; i < str.length(); i++) { System.out.print(", " + str.charAt(i)); } System.out.println(); } }
  7. int SENTINEL = -1; System.out.print("Type a number (or " + SENTINEL + " to stop): "); Scanner console = new Scanner(System.in); int input = console.nextInt(); int min = input; int max = input; while (input != SENTINEL) { if (input < min) min = input; else if (input > max) max = input; System.out.print("Type a number (or " + SENTINEL + " to stop): "); input = console.nextInt(); } if (min != SENTINEL || max != SENTINEL) { System.out.println("Maximum was " + max); System.out.println("Minimum was " + min); }
  8. Scanner console = new Scanner(System.in); String line; String allNames = ""; do { System.out.print("Type a person's name (or an empty line to stop): "); line = console.nextLine(); allNames += " " + line; } while (!line.equals("")); System.out.println("Welcome to all:" + allNames);
  9. System.out.print("Type a number: "); Scanner console = new Scanner(System.in); int input = console.nextInt(); double sum = 0.0; int count = 0; while (input >= 0) { count++; sum += input; System.out.print("Type a number: "); input = console.nextInt(); } if (count > 0) { double average = sum / count; System.out.println("Average was " + average); }
  10. The code should re-prompt for a valid integer for the user's age and a valid real number for the user's GPA. If the user types a token of the wrong type, their line of input should be consumed, and they should be reprompted.

    The following code implements the corrected behavior:

    Scanner console = new Scanner(System.in); System.out.print("Type your age: "); while (!console.hasNextInt()) { console.nextLine(); // throw away the offending token System.out.print("Type your age: "); } int age = console.nextInt(); System.out.print("Type your GPA: "); while (!console.hasNextDouble()) { console.nextLine(); // throw away the offending token System.out.print("Type your GPA: "); } double gpa = console.nextDouble();
    • When the user types Jane:
      Type something for me! Jane
      Your name is Jane
      
    • When the user types 56:
      Type something for me! 56
      Your IQ is 56
      
    • When the user types 56.2:
      Type something for me! 56.2
      Your name is 56.2
      
  11. Scanner console = new Scanner(System.in); System.out.print("Type a number: "); if (console.hasNextDouble()) { double value = console.nextDouble(); System.out.println("You typed the real number " + value); } else if (console.hasNextInt()) { int value = console.nextInt(); System.out.println("You typed the integer " + value); }
  12. String prompt = "Please enter a number: "; Scanner console = new Scanner(System.in); int num1 = getInt(console, prompt); int num2 = getInt(console, prompt); int num3 = getInt(console, prompt); double average = (num1 + num2 + num3) / 3.0; System.out.println("Average: " + average);
  13. a: 0 through 99 inclusive
    b: 50 through 69 inclusive
    c: 0 through 69 inclusive
    d: -20 through 79 inclusive
    e: 0, 4, 8, 12, 16, 20, 24, 28, 32, or 36
  14. Random rand = new Random();
    int random0to10 = rand.nextInt(11);
    
  15. Random rand = new Random();
    int oddBtwn51and99 = 51 + rand.nextInt(25) * 2;
    
  16. Random rand = new Random();
    int linesCount = rand.nextInt(9) + 2;
    
    for (int i = 1; i <= linesCount; i++) {
        int xCount = rand.nextInt(15) + 5;
    
        for (int j = 1; j <= xCount; j++)
            System.out.print("x");
        System.out.println();
    }
    
    • true
    • true
    • false
    • true
    • true
    • false
    • false
    • true
    • true
    • true
    • true
    • false
    • executes body 2 times
    • 100
      50
      
    • executes body 3 times
    • /\/\/\/\/\/\/\/\
  17.          y < x       y == 0      count > 0
    Point A: SOMETIMES   SOMETIMES   NEVER
    Point B: ALWAYS      SOMETIMES   SOMETIMES
    Point C: ALWAYS      ALWAYS      ALWAYS
    Point D: SOMETIMES   SOMETIMES   SOMETIMES
    Point E: NEVER       SOMETIMES   SOMETIMES
    
  18.          n > b       a > 1       b > a
    Point A: SOMETIMES   SOMETIMES   SOMETIMES
    Point B: ALWAYS      SOMETIMES   SOMETIMES
    Point C: SOMETIMES   ALWAYS      ALWAYS
    Point D: SOMETIMES   ALWAYS      NEVER
    Point E: NEVER       SOMETIMES   SOMETIMES
    

Chapter 6

  1. The String "C:\temp\new files\test.dat" is not a valid file path. The backslash characters should be escaped by writing two backslashes. The intended String was "C:\\temp\\new files\\test.dat".
    • "numbers.dat" or "C:\\Documents and Settings\\amanda\\My Documents\\programs\\numbers.dat"
    • "data\\homework6\\input.dat" or "C:\\Documents and Settings\\amanda\\My Documents\\programs\\data\\homework6\\input.dat"
    • There is only one legal way to refer to this file: by its absolute path, "C:\\Documents and Settings\\amanda\\My Documents\\homework\\data.txt"
    • "names.txt" or "/home/amanda/Documents/hw6/names.txt"
    • "data/numbers.txt" or "/home/amanda/Documents/hw6/data/numbers.txt"
    • There is only one legal way to refer to this file: by its absolute path, "/home/amanda/download/saved.html"
  2. input: 6.7      This file has
    input:  several input lines.
    input:
    input:   10 20  30   40
    input:
    input: test
    6 total
    
  3. input: 6.7
    input: This
    input: file
    input: has
    input: several
    input: input
    input: lines.
    input: 10
    input: 20
    input: 30
    input: 40
    input: test
    12 total
    
  4. using hasNextInt and nextInt:
    0 total
    
    using hasNextDouble and nextDouble:
    input: 6.7
    1 total
    
  5. import java.io.*; import java.util.*; public class PrintMyself { public static void main(String[] args) throws FileNotFoundException { Scanner input = new Scanner(new File("PrintMyself.java")); while (input.hasNextLine()) { System.out.println(input.nextLine()); } } }
  6. public static String getFileName() { Scanner console = new Scanner(System.in); String filename = null; do { System.out.print("Type a file name: "); filename = console.nextLine(); } while (!(new File(filename).exists())); return filename; }
  7. public static void printEntireFile(String filename) throws FileNotFoundException { Scanner input = new Scanner(new File(filename)); while (input.hasNextLine()) { System.out.println(input.nextLine()); } }
  8. Version that does not work when inFile equals outFile: public static void doubleSpace(String inFile, String outFile) throws FileNotFoundException { Scanner input = new Scanner(new File(inFile)); PrintStream out = new PrintStream(new File(outFile)); while (input.hasNextLine()) { out.println(input.nextLine() + "\n"); } }

    Version that does work when both file names are the same:

    public static void doubleSpace2(String inFile, String outFile) throws FileNotFoundException { // read the input file and store as a long String String output = ""; Scanner input = new Scanner(new File(inFile)); while (input.hasNextLine()) { output += input.nextLine() + "\n\n"; } input.close(); // write the doubled output to the outFile PrintStream out = new PrintStream(new File(outFile)); out.print(output); }
  9. public static void wordWrap(String filename) throws FileNotFoundException { Scanner input = new Scanner(new File(filename)); while (input.hasNextLine()) { String line = input.nextLine(); while (line.length() > 60) { String first60 = line.substring(0, 60); System.out.println(first60); line = line.substring(60); } System.out.println(line); } }
  10. public static void wordWrap2(String filename) throws FileNotFoundException { int maxLineLength = 60; Scanner input = new Scanner(new File(filename)); String outputString = ""; while (input.hasNextLine()) { String line = input.nextLine(); while (line.length() > maxLineLength) { String first = line.substring(0, maxLineLength); outputString += first + "\n"; line = line.substring(maxLineLength); } outputString += line + "\n"; } PrintStream out = new PrintStream(new File(filename)); out.print(outputString); out.close(); }
  11. public static void wordWrap3(String filename) throws FileNotFoundException { int maxLineLength = 60; Scanner input = new Scanner(new File(filename)); while (input.hasNextLine()) { String line = input.nextLine(); while (line.length() > maxLineLength) { // find the nearest token boundary int index = maxLineLength; while (!Character.isWhitespace(line.charAt(index))) { index--; } String first = line.substring(0, index + 1); System.out.println(first); line = line.substring(index + 1); } System.out.println(line); } }
  12. import java.io.*; import java.util.*; public class MaxMinSumAverage { public static void main(String[] args) { Scanner console = new Scanner(System.in); Scanner input = getInput(console); processFile(input); } public static Scanner getInput(Scanner console) { Scanner result = null; while (result == null) { System.out.print("What is the name of the input file? "); String name = console.nextLine(); try { result = new Scanner(new File(name)); } catch (FileNotFoundException e) { System.out.println("File not found. Please try again."); } } System.out.println(); return result; } public static void processFile(Scanner input) { // read the first number and use it to prime our variables int sum = input.nextInt(); int count = 1; int min = sum; int max = sum; // read each number from the file and process it while (input.hasNext()) { int next = input.nextInt(); sum += next; count++; if (next > max) { max = next; } if (next < min) { min = next; } } // print results System.out.println("Maximum = " + max); System.out.println("Minimum = " + min); System.out.println("Sum = " + sum); System.out.println("Count = " + count); System.out.println("Average = " + (double) sum / count); } }
  13. import java.io.*; import java.util.*; public class MaxMinSumAverage2 { public static void main(String[] args) { Scanner console = new Scanner(System.in); Scanner input = getInput(console); processFile(input); } public static Scanner getInput(Scanner console) { Scanner result = null; while (result == null) { System.out.print("What is the name of the input file? "); String name = console.nextLine(); try { result = new Scanner(new File(name)); } catch (FileNotFoundException e) { System.out.println("File not found. Please try again."); } } System.out.println(); return result; } public static void processFile(Scanner input) { // advance to the first integer token while (!input.hasNextInt()) { input.next(); } // read the first number and use it to prime our variables int sum = input.nextInt(); int count = 1; int min = sum; int max = sum; // read each number from the file and process it while (input.hasNext()) { if (input.hasNextInt()) { int next = input.nextInt(); sum += next; count++; if (next > max) { max = next; } if (next < min) { min = next; } } else { // discard the non-integer token input.next(); } } // print results System.out.println("Maximum = " + max); System.out.println("Minimum = " + min); System.out.println("Sum = " + sum); System.out.println("Count = " + count); System.out.println("Average = " + (double) sum / count); } }
  14. public static void collapseSpaces(String filename) throws FileNotFoundException { Scanner input = new Scanner(new File(filename)); while (input.hasNextLine()) { String line = input.nextLine(); Scanner lineScanner = new Scanner(line); while (lineScanner.hasNext()) { String word = lineScanner.next(); System.out.print(word + " "); } System.out.println(); } }
  15. public static String readEntireFile(String filename) throws FileNotFoundException { Scanner input = new Scanner(new File(filename)); String text = ""; while (input.hasNextLine()) { text += input.nextLine() + "\n"; } return text; }
  16. public static void stripHtmlTags(String filename) throws FileNotFoundException { String text = readEntireFile(filename); int indexOfTag = text.indexOf("<"); while (indexOfTag >= 0) { String start = text.substring(0, indexOfTag); text = text.substring(indexOfTag, text.length()); int indexOfTagEnd = text.indexOf(">"); text = start + text.substring(indexOfTagEnd + 1, text.length()); indexOfTag = text.indexOf("<"); } System.out.print(text); }
  17. public static void stripComments(String filename) throws FileNotFoundException { String text = readEntireFile(filename); int index1 = text.indexOf("//"); int index2 = text.indexOf("/*"); while (index1 >= 0 || index2 >= 0) { if (index2 < 0 || (index1 >= 0 && index1 < index2)) { String start = text.substring(0, index1); text = text.substring(index1, text.length()); text = start + text.substring(text.indexOf("\n"), text.length()); } else { String start = text.substring(0, index2); text = text.substring(index2, text.length()); text = start + text.substring(text.indexOf("*/") + 2, text.length()); } index1 = text.indexOf("//"); index2 = text.indexOf("/*"); } System.out.print(text); }

Chapter 7

  1. first element: numbers[0]
    last element: numbers[9] or numbers[numbers.length - 1]
  2. Long version: int[] data = new int[5]; data[0] = 27; data[1] = 51; data[2] = 33; data[3] = -1; data[4] = 101;

    Short version:

    int[] data = {27, 51, 33, -1, 101};
  3. Version that works for min of -6 and max of 38: int[] odds = new int[22]; for (int i = 0; i < 22; i++) { odds[i] = i * 2 - 5; }

    Version that works for any min and max:

    int min = -6; int max = 38; // adjust min and max to the nearest even numbers if (Math.abs(min) % 2 == 0) { min++; } if (Math.abs(max) % 2 == 0) { max--; } int[] odds = new int[(max - min) / 2 + 1]; for (int i = 0; i < odds.length; i++) { odds[i] = min + 2 * i; }
  4. {20, 30, 40, 50, 60, 70, 80, 90, 100, 100}
  5. {10, 10, 10, 10, 10, 10, 10, 10, 10, 10}
  6. for (int i = 0; i < data.length; i++) {
        System.out.println("Element [" + i + "] is " + numbers[i]);
    }
    
  7. Array a1 contains the following elements: {26, 19, 14, 11, 10}
  8. Array a1 contains the following elements: {1, 3, -3, 13, -4, -24, -6, -14}
  9. public static void printAll(int[] data) { System.out.print("{" + data[0]); for (int i = 1; i < data.length; i++) { System.out.print(", " + data[i]); } System.out.println("}"); }
  10. public static void swap(int[] array, int index1, int index2) { int temp = array[index1]; array[index1] = array[index2]; array[index2] = temp; }
  11. public static void reverse(int[] array) { for (int i = 0; i < array.length / 2; i++) { int temp = array[i]; array[i] = array[array.length - 1 - i]; array[array.length - 1 - i] = array[i]; } }

    or, a more elegant solution using the swap method from the previous exercise:

    public static void reverse1(int[] array) { for (int i = 0; i < array.length / 2; i++) { swap(array, i, array.length - 1 - i); } }
  12. public static void copyAll(int[] array1, int[] array2) { for (int i = 0; i < array1.length; i++) { array2[i] = array1[i]; } }
  13. 7 3 1 0 8 18 5 -1 5
    
  14. public static boolean equal(int[] array1, int[] array2) { if (array1.length != array2.length) { return false; } for (int i = 0; i < array1.length; i++) { if (array1[i] != array2[i]) { return false; } } return true; }
  15. public static boolean isPalindrome(int[] array) { for (int i = 0; i < array.length / 2; i++) { if (array[i] != array[array.length - 1 - i]) { return false; } } return true; }

    or, more elegant solution using code from previous exercises:

    public static boolean isPalindrome2(int[] array) { return equal(array, reverse2(array)); }
  16. public static void copyRange(int[] a1, int[] a2, int i1, int i2, int length) { for (int i = 0; i < length; i++) { a2[i2 + i] = a1[i1 + i]; } }
  17. public static int sumAll(int[] a) { int sum = 0; for (int i = 0; i < a.length; i++) { sum += a[i]; } return sum; }
  18. public static double average(int[] a) { double mean = 0.0; for (int i = 0; i < a.length; i++) { mean += a[i]; } return mean / a.length; } or, more concise version using solution from previous exercise: public static double average2(int[] a) { return (double) sumAll(a) / a.length; }
  19. First solution (relies on assumption that every element is between 0 and 100): public static int mode(int[] a) { // tally all the occurrences of each element int[] tally = new int[101]; for (int i = 0; i < a.length; i++) { tally[a[i]]++; } // scan the array of tallies to find the highest tally (the mode) int maxCount = 0; int modeValue = 0; for (int i = 0; i < tally.length; i++) { if (tally[i] > maxCount) { maxCount = tally[i]; modeValue = i; } } return modeValue; }

    Second solution (works for any element values):

    public static int mode2(int[] a) { // sort the array Arrays.sort(a); // scan it to find the element that occurs the most // (all occurrences of a given element will now be consecutive) int maxCount = 1; int mode = a[0]; int count = 1; int value = a[0]; for (int i = 1; i < a.length; i++) { if (a[i] == value) { count++; if (count > maxCount) { maxCount = count; mode = value; } } else { count = 1; value = a[i]; } } return mode; }
  20. public static double stdev(int[] a) { double average = average(a); double sumDiff = 0.0; for (int i = 0; i < a.length; i++) { sumDiff += Math.pow(a[i] - average, 2); } return Math.sqrt(sumDiff / (a.length - 1)); }
  21. public static int kthLargest(int[] a, int k) { int[] a2 = copyAll(a); Arrays.sort(a2); return a2[a2.length - 1 - k]; }
  22. First version (relies on elements' values being between 0 and 99 and odd array size):

    public static int median(int[] a) { // count the number of occurrences of each number into a "tally" array int[] tally = new int[100]; for (int i = 0; i < a.length; i++) { tally[a[i]]++; } // examine the tallies and stop when we have seen half the numbers int i; for (i = 0; tally[i] <= a.length / 2; i++) { tally[i + 1] += tally[i]; } return i; }

    Second solution (works for any element values, and correctly averages the middle two elements for an evenly-sized array):

    public static double median2(int[] a) { int[] a2 = copyAll(a); Arrays.sort(a2); int mid = a2.length / 2; if (a2.length % 2 == 1) { return a2[mid]; } else { // average the two middle elements return (a2[mid - 1] + a2[mid]) / 2; } }
  23. import java.io.*; import java.util.*; public class MedianFile { public static void main(String[] args) throws FileNotFoundException { Scanner input = new Scanner(new File("medianinput.dat")); int count = input.nextInt(); int[] data = new int[count]; for (int i = 0; i < count; i++) { data[i] = input.nextInt(); } System.out.println("Median is " + median(data)); } // Computes and returns the median of the numbers in the array. // Assumes the array consists of numbers between 0 and 99. public static int median(int[] array) { // count the number of occurrences of each number into a "tally" array int[] tally = new int[100]; for (int i = 0; i < array.length; i++) { tally[array[i]]++; } // examine the tallies and stop when we have seen half the numbers int i; for (i = 0; tally[i] <= array.length / 2; i++) { tally[i + 1] += tally[i]; } return i; } public static void printAll(int[] data) { System.out.print("(" + data[0]); for (int i = 1; i < data.length; i++) { System.out.print(", " + data[i]); } System.out.println(")"); } }
  24. public static void wordLengths(String filename) throws FileNotFoundException { // tally the lengths of every word in the file Scanner input = new Scanner(new File(filename)); int[] tally = new int[81]; int maxLength = 0; while (input.hasNext()) { String token = input.next(); tally[token.length()]++; maxLength = Math.max(maxLength, token.length()); } // report the results for (int i = 1; i <= maxLength; i++) { System.out.print(i + ": " + tally[i] + "\t"); for (int j = 0; j < tally[i]; j++) { System.out.print("*"); } System.out.println(); } }
  25. public static boolean areAnagrams(String word1, String word2) { return sorted(word1.toLowerCase()).equals(sorted(word2.toLowerCase())); } public static String sorted(String s) { char[] letters = s.toCharArray(); Arrays.sort(letters); String result = new String(letters); return result; }
  26. public static int maxLength(ArrayList<String> list) { int max = 0; for (int i = 0; i < list.size(); i++) { String s = list.get(i); if (s.length() > max) { max = s.length(); } } return max; }
  27. public static void removeEvenLength(ArrayList<String> list) { int index = 0; while (index < list.size()) { String s = list.get(index); if (s.length() % 2 == 0) { list.remove(index); } else { index++; } } }
  28. public static void stutter(ArrayList<String> list) { for (int i = 0; i < list.size(); i += 2) { list.add(i, list.get(i)); } }
  29. public static void removeDuplicates(ArrayList<String> list) { int index = 0; while (index < list.size() - 1) { String s1 = list.get(index); String s2 = list.get(index + 1); if (s1.equals(s2)) { list.remove(index + 1); } else { index++; } } }

Chapter 8

  1. public double getDistance(Point other) { int dx = this.x - other.x; int dy = this.y - other.y; return Math.sqrt(dx * dx + dy * dy); }
  2. Initial solution: public double getDistanceFromOrigin(Point other) { return Math.sqrt(this.x * this.x + this.y * this.y); }

    Second, more elegant solution:

    public double getDistanceFromOrigin2(Point other) { Point origin = new Point(0, 0); return this.getDistance(origin); }
  3. public double getManhattanDistance(Point other) { int dx = this.x - other.x; int dy = this.y - other.y; return Math.abs(dx) + Math.abs(dy); }
  4. public String toString() { return "(" + this.x + ", " + this.y + ")"; // return "java.awt.Point[x=" + this.x + ", y=" + this.y + "]"; }
  5. public double getSlope(Point other) { if (other.x == this.x) { throw new IllegalArgumentException("Line has undefined slope"); } return (other.y - this.y) / (other.x - this.x); }
  6. public boolean isColinear(Point p1, Point p2) { // basic case: all points have same x or y value if ((this.x == p1.x && this.x == p2.x) || (this.y == p1.y && this.y == p2.y)) { return true; } // complex case: compare slopes double slope1 = (p1.y - this.y) / (p1.x - this.x); double slope2 = (p2.y - this.y) / (p2.x - this.x); return round(slope1, 4) == round(slope2, 4); }
  7. Version for original toString "(-2, 3)" format: public static Point parsePoint(String str) { int indexOfLParen = str.indexOf("("); int indexOfComma = str.indexOf(","); int indexOfRParen = str.indexOf(")"); int x = new Scanner(str.substring(indexOfLParen + 1, indexOfComma)).nextInt(); int y = new Scanner(str.substring(indexOfComma + 2, indexOfRParen)).nextInt(); return new Point(x, y); }

    Version for Java-Point-matching toString "java.awt.Point[x=-2,y=3]" format:

    public static Point parsePoint2(String str) { int indexOfEquals = str.indexOf("="); int indexOfComma = str.indexOf(","); int x = new Scanner(str.substring(indexOfEquals + 1, indexOfComma)).nextInt(); str = str.substring(indexOfEquals + 1, str.length()); indexOfEquals = str.indexOf("="); int indexOfRBracket = str.indexOf("]"); int y = new Scanner(str.substring(indexOfEquals + 1, indexOfRBracket)).nextInt(); return new Point(x, y); }
  8. public int getShares() { return this.numShares; } public String getSymbol() { return this.symbol; } public double getTotalCost() { return this.totalCost; }
  9. public void clear() { this.numShares = 0; this.totalCost = 0.00; }
  10. public String toString() { String cost = "$ " + this.totalCost; if (cost.charAt(cost.length() - 2) == '.') { // add another digit after . ($ 500.2 --> $ 500.20) cost += "0"; } return this.symbol + " ( " + this.numShares + ", " + cost + " total cost )"; }
  11. public boolean equals(Object o) { Stock other = (Stock) o; return this.symbol.equals(other.symbol) && this.numShares == other.numShares && this.totalCost == other.totalCost; }
  12. // Represents a line segment between two Points. public class Line { private Point p1; private Point p2; // Constructs a new Line that contains the given two Points. public Line(Point p1, Point p2) { this.p1 = p1; this.p2 = p2; } // Returns this Line's first endpoint. public Point getP1() { return this.p1; } // Returns this Line's second endpoint. public Point getP2() { return this.p2; } // Returns a String representation of this Line, such as "[(-2, 3), (4, 7)]". public String toString() { return "[" + this.p1 + ", " + this.p2 + "]"; } }
  13. public double getSlope() { if (this.p1.getX() == this.p2.getX()) { throw new IllegalArgumentException("Line has undefined slope"); } return (double) (this.p2.getY() - this.p1.getY()) / (this.p2.getX() - this.p1.getX()); }
  14. public Line(int x1, int y1, int x2, int y2) { this(new Point(x1, y1), new Point(x2, y2)); }
  15. public boolean equals(Object o) { Line other = (Line) o; return this.p1.equals(other.p1) && this.p1.equals(other.p2); }
  16. public boolean isColinear(Point p) { // basic case: all points have same x or y value if ((p.getX() == this.p1.getX() && p.getX() == this.p2.getX()) || (p.getY() == this.p1.getY() && p.getY() == this.p2.getY())) { return true; } // complex case: compare slopes double slope1 = (this.p1.getY() - p.getY()) / (this.p1.getX() - p.getX()); double slope2 = (this.p2.getY() - p.getY()) / (this.p2.getX() - p.getX()); return round(slope1, 4) == round(slope2, 4); } public static double round(double value, int places) { double pow10 = Math.pow(10, places); return Math.round(value * pow10) / pow10; }
  17. // Represents a 2-dimensional rectangular region.
    public class Rectangle {
        private int x;
        private int y;
        private int width;
        private int height;
    
        // Constructs a new Rectangle whose top-left corner is specified by the
        // given coordinates and with the given width and height.
        public Rectangle(int x, int y, int width, int height) {
            if (width < 0 || height < 0) {
                throw new IllegalArgumentException();
            }
    
            this.x = x;
            this.y = y;
            this.width = width;
            this.height = height;
        }
    
        // Returns this Rectangle's height.
        public int getHeight() {
            return this.height;
        }
    
        // Returns this Rectangle's width.
        public int getWidth() {
            return this.width;
        }
    
        // Returns this Rectangle's x coordinate.
        public int getX() {
            return this.x;
        }
    
        // Returns this Rectangle's y coordinate.
        public int getY() {
            return this.y;
        }
    
        // Returns a String representation of this Rectangle, such as
        // "Rectangle[x=1,y=2,width=3,height=4]".
        public String toString() {
            return "Rectangle[x=" + this.x + ",y=" + this.y +
                   ",width=" + this.width + ",height=" + this.height + "]";
        }
    }
    
  18. public boolean equals(Object o) { if (o == null || this.getClass() != o.getClass()) { return false; } Rectangle other = (Rectangle) o; return other.x == this.x && other.y == this.y && other.width == this.width && other.height == this.height; }
  19. public Rectangle(int x, int y, int width, int height) { if (width < 0 || height < 0) { throw new IllegalArgumentException(); } this.x = x; this.y = y; this.width = width; this.height = height; }
  20. public boolean contains(int x, int y) { return this.x <= x && x < this.x + this.width && this.y <= y && y < this.y + this.height; }
  21. public Rectangle union(Rectangle rect) { int left = Math.min(this.x, rect.x); int top = Math.min(this.y, rect.y); int right = Math.max(this.x + this.width, rect.x + rect.width); int bottom = Math.max(this.y + this.height, rect.y + rect.height); return new Rectangle(left, top, right - left, bottom - top); }
  22. public Rectangle intersection(Rectangle rect) { int left = Math.max(this.x, rect.x); int top = Math.max(this.y, rect.y); int right = Math.min(this.x + this.width, rect.x + rect.width); int bottom = Math.min(this.y + this.height, rect.y + rect.height); int width = Math.max(0, right - left); int height = Math.max(0, bottom - top); return new Rectangle(left, top, width, height); }

Chapter 9

  1. bar 2 foo foo 1 mumble 2 baz baz 1 foo 2 foo foo 1 foo 2 baz baz 1
  2. fue shoe 1 fue 2 fue blue 1 fue 2 moo moo 1 moo 2 moo blue 1 moo 2
  3. moo 2 blue 1 moo moo 2 moo 1 moo fue 2 shoe 1 fue fue 2 blue 1 fue
  4. squid creature 1 tentacles BIG! spout creature 2 ocean-dwelling creature 1 creature 2 ocean-dwelling warm-blooded creature 2
  5. creature 2 ocean-dwelling creature 1 tentacles squid creature 1 creature 2 ocean-dwelling warm-blooded creature 2 BIG! spout
  6. Two solutions are shown.
    // Constructs a 3D Point at the origin of (0, 0, 0).
    public Point3D() {
    	super(0, 0);
    	this.z = 0;
    }
    
    // Constructs a 3D Point at the origin of (0, 0, 0).
    public Point3D() {
    	this(0, 0, 0);
    }
    

Stuart Reges
Marty Stepp
Last modified: Tue Jan 03 00:38:34 PDT 2006