Chapter 1
Introduction

Copyright © 2004 by Stuart Reges

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. Most C and C++ compilers 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 have to have a compiler for each computer and you'd have to produce a different executable for each.

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.

To execute Java bytecodes you need a special program known as an interpreter.

Interpreter

A program that dynamically executes programs written in a particular programming language.

You'll have the right idea if you think about what happens at the United Nations. Suppose that someone is speaking in French and you understand only English. We work this out by hiring an interpreter who listens to the French and translates into equivalent English for you as its spoken. For Java we have a program that is known as the Java Runtime Environment that is a similar kind of interpreter. The problem is that we have a program file that contains Java bytecodes and a computer that doesn't understand Java bytecodes. So we need a program that plays the role of interpreter, reading the bytecodes and giving instructions to the computer that are equivalent to the bytecodes.

It seems odd to have both a compiler and an interpreter. For example, why have the interpreter work with Java bytecodes? Why not have it work with the original Java programs? This is one of those goldilocks situations where we're searching for a middle ground that is "just right." Java itself is too high level but machine code is too low-level. Java bytecodes are in the middle and, therefore, just right.

Remember that the Java designers had as one of their major goals that Java would run on many different computers. If we compile all the way down to a particular machine language, then the resulting program will run on just that computer. That's why machine language is too low level. But if we don't compile at all, then we have to write a series of interpreters for each different computer and those interpreters will have to deal with the full complexity of Java. That would discourage people from running Java on their computers because they'd have to write complex interpreters that might not run very quickly.

The goldilocks middle ground is to compile down to Java bytecodes. These 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 for the interpreter. That means that it becomes a fairly simple matter for people to write a different interpreter for every different computer.

Microsoft made a similar choice when they redesigned Windows in the late 1990's (creating a system they refer to as the ".NET framework"). All of the .NET languages (including Visual Basic and C#) are compiled into an intermediate state known as IL (short for "Intermediate Language") which represents the machine language of a virtual machine. These IL files are very similar to the files of Java bytecodes produced by the Java compiler. But instead of using an interpreter to execute these on specific computers, they write a second compiler that converts IL into machine language just before it is executed. These are known as "Just In Time" or JIT compilers.

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. The simplest way to create a Java file is to use a simple text editor (e.g., using NotePad on Windows or emacs on unix). Once created, you compile the program to generate Java bytecodes. For example, if have created a program file called Fun.java and you are using the Sun compiler on unix or Windows, you would give a command like the following to compile it:

    javac Fun.java
The "javac" command is short for "Java Compiler." The compiler creates files with the extension ".class" that contain the corresponding Java bytecodes. In the example above, if the compiler is able to translate Fun.java into bytecodes, it stores them in a file called Fun.class. You'll find that this file isn't in a human readable format, but if you're curious, you can ask the computer to show you the bytecodes in a more convenient format. For example, after compiling Fun.java, you can give this command to see it in bytecode form if you are using the Sun tools:

    javap -c Fun
It isn't very easy to read even in this form, but it gives you a sense of what the Java bytecodes are all about.

To actually execute your Java program, you have to fire up the Java interpreter. With the Sun tools, you would say:

    java Fun
This is going to execute the contents of Fun.class, but you don't say the ".class" part when you give this command.

Often 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. There are many popular commercial IDEs including Visual Studio, Eclipse, CodeWarrior and JBuilder. There are also IDEs developed specifically for computer science instruction including BlueJ and DrJava. And there are many free IDEs floating around like JCreator and JEdit. Your instructor will tell you what environment you should be using.

Most of the sample programs in this book involve what is called console or terminal interaction. By default, output goes to a particular window known as the console window and you can also read information from the console, in which case the computer will pause and wait for the user to type something before it proceeds with execution. This is an old-fashioned style of interaction, but it is simple. Console programs allow us to have simple interaction, which allows us to focus our attention and effort on other aspects of the program.

Java was not intended for console programs. It was intended for programs with a modern Graphical User Interface (GUI) that takes advantage of advanced graphics for drawing and the mouse for user input. Java has extensive libraries known as AWT and Swing that provide support for GUI programming. We will look at a few elements from these libraries, but for the most part we won't explore GUIs or graphics in this book.

1.5 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.6 Some Java Terminology

The designers of Java described the language in a report known as the Java white paper. They begin by describing the language with a set of buzzwords, as "A simple, object-oriented, network-savvy, interpreted, robust, secure, architecture neutral, portable, high-performance, multithreaded, dynamic language." Each of these buzzwords represents a design goal for the original language. You can find the complete white paper online if you want to explore this detail, but I will briefly review some of these terms that are relevant to what we'll be covering in this book.

Several of these terms relate to the overall goal of allowing Java to run on many different computers. Section 1.3 describes how being an interpreted language furthers that goal. Keeping it architecture neutral meant that it could run well on all computers. Keeping it portable meant that programmers wouldn't waste time on machine specific details. The vision was for a language that would allow programmers to "Write once, run everywhere." Of course, the reality didn't always match the vision and programmers often complained, particularly in the early years, that Java was a "Write once, debug everywhere" language.

Several other terms involved the goal of having Java work well with the web. That meant it needed to be network-savvy to provide programmers with the tools they would need to write web applications. People wouldn't be willing to run web programs if they couldn't be trusted, so it became important for Java to be robust and secure.

Java was presented as an alternative to C++ and the designers felt it was important to keep the language as simple as possible because C++ was a highly complex language. They wanted it to be multithreaded by design versus the single-threaded model of C and C++. At the same time, they wanted it to be high-performance so that people didn't sacrifice performance to get the other benefits of Java. Again, the reality didn't always match the vision as programmers found that Java had performance problems. It was often very slow. In recent years, though, Java's underlying performance has improved greatly to the point where Java programs run almost as fast as equivalent C and C++ programs. The two final terms from this list are object oriented and dynamic. These are the terms that we will spend the most time exploring in this book. We will see that Java programs involve the dynamic interaction of many different kinds of objects. It was possible in older languages like C++ to create such programs, but it was clear that objects were an add-on and that you would have to go to a great deal of trouble to get objects to work well. In Java objects are king and the designers of the language have made all sorts of choices to make it easier to create and manipulate objects.

The various objects in a Java program fall into different categories that we call classes. A class represents a family of similar objects.

Class

A category or type of object.

The various objects of a particular class are known as the instances of that class.

Instance

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

This is similar to the way that biologists talk about a species. They look at the variety of plants and animals that exist in the world and divide them up into different categories. One category is for humans. There is one category to represent the entire species of humans even though there are billions of individual members of that one category.

In a similar way, each Java class describes a particular kind of object of which there might be many existing members. For example, Java has a class called String that defines a family of objects that store textual information. It is possible to have many String objects in a program. In fact often there are thousands of String objects interacting in a single program. But there is only one String class. The various String objects are known as the instances of the String class.

When we as Java programmers want to request that a certain action be performed, we call what are known as "methods."

Method

A program unit that represents a particular action or computation.

Each object has various methods that it can perform. Other programming languages have referred to these as functions or procedures. The Smalltalk language referred to them as "messages" that you send to objects.

To keep track of particular values and particular objects we will use what are known as variables. As their name implies, variables can change value as the program executes. For example, if we were writing a game playing program we might have variables to keep track of how many games the user had played and how many of those games the user had won.

Sometimes we want to use values that we know aren't going to change, in which case we use what are called constants instead. Java has a predefined constant called Math.PI that represents the value of the mathematical quantity called pi.

1.7 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."); } } 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. Programs in Java always involve a class that contains what we call a main method. At the outermost level, the program above defines a class called Hello:

    public class Hello {
        ...
    }
The keyword "public" indicates that this class is available to anyone to use. The curly braces are used as a grouping mechanism to say that "everything defined inside these braces is part of the public class called Hello." It may seem odd to have the opening curly brace at the end of the first 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.

Inside the Hello class you will find the main method.

        public static void main(String[] args) {
            System.out.println("Hello world.");
        }
Later in this chapter we will examine methods in greater detail, but for now you just need to know that every executable Java program needs to have a method with this general form:

        public static void main(String[] args) {
            ...
        }
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. In this case there is just a single command to produce a line of output with "Hello world" on it, but consider the following variation 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."); } } The main method is referred to as the "entry point" for program execution. Execution begins with the first line of main and continues line by line until they have all been executed. Each of these lines represents a statement. Just as you put together an essay by stringing together complete sentences, you put together a Java program by stringing together statements.

Statement

An executable snippet of code that represents a complete command.

The statements in these programs are all calls on a method known as println. Method calls are just one kind of Java statement.

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

1.7.1 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.7.2 String Literals and Escape Sequences

Suppose that Joe says to Mary, "Please say your name." How is she supposed to respond? We expect her to say "Mary", but she might also say "Your name." The second response makes sense if Joe is asking her to literally say the words "your name" versus asking her what her name is.

You will make this distinction often in your Java programs. When you want to include some literal text, you put quotation marks around it to form what we call a string literal, as in the example below. We call this a string because you are stringing together a series of characters.

    "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."
You can, however, 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 ("\"). For example, you wouldn't normally be able to include the quotation mark itself inside a string, so there is an escape sequence for this. 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.

Notice that 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.
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.7.3 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. 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. But it's worth exploring where the name comes from.

Java uses the dot notation as a way to open up something, as in opening up a folder to see what is inside it. So you might say something like earth.usa.texas.dallas as a way to say "start with earth, go inside it to find the usa, go inside that to find texas and go inside that to find dallas." The phrase "System.out.println" begins with "System." What's that? It begins with a capital letter, so it's the name of a class. In Java the System class contains several useful general-purpose tools. One of those is a variable called "out" which refers to the standard output stream. For interactive programs, standard output goes to the console.

To recap, System is the name of a class and System.out is a variable inside the System class. So what about the println part? That's a method. The variable System.out refers to an object that has a method called println. You'll learn to spot methods because they always have parentheses after them. The parentheses are used to indicate any arguments being passed to the method. The simplest form of the println has no arguments 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 produce 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.7.4 Punctuation and Simple Program Structure

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. Semicolons are a required part of the syntax. If any are left out, the program will have a compilation error.

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.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.

They are called static because they don't require an object. Whenever you see the keyword "Static" in Java you know that you are dealing with something that is associated with the class rather than the instances of the class.

Static

Associated with a class rather than the instances of a class.

It is cumbersome and confusing to have to include the keyword "static" for each of these methods, but remember that Java was designed for object oriented programming. Without the "static" keyword, Java assumes that a method is intended for objects. We will see how to define and use object methods later in the book. For now we will limit ourselves to static methods because even though they require a bit more work to declare, they are much easier to understand in terms of how they work. To become an effective Java programmer you should be familiar with both kinds of methods, so it makes sense to start with the one that is simpler to understand.

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 or class method rather than a dynamic method associated with the instances of the class. The word "void" is the return type for the method. It 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. Consider this sample program.

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(" \\/"); } } This is a structured version of the DrawFigures1 program you saw earlier. The output generated is identical. The program is named DrawFigures2 and has six static methods defined within it. The first static method is the familiar 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. The rules are still fairly simple, however. 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. The main method of DrawFigures2 first executes the body of method drawDiamond, which 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.

    public static void main(String[] args) {
        drawDiamond();
              \
               \
                +------------------------------------+
                | public static void drawDiamond() { |
                |     drawCone();                    |
                |     drawV();                       |
                |     System.out.println();          |
                | }                                  |
                +------------------------------------+
               /
              /
        drawX();
        drawRocket();
    }
A complete breakdown of the flow of control from static method to static method in DrawFigures2 follows.

         1st            drawDiamond
         2nd                drawCone
         3d                 drawV
         4th            drawX
         5th                drawV
         6th                drawCone
         7th            drawRocket
         8th                drawCone
         9th                drawBox
        10th                drawBox
        11th                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. I started with main at the top and worked my 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 six 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.9 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.

    /* Thadeous 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.");  // sucker!
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.10 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.

Little 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.

Many 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.

The Java compiler will also catch some other minor errors. For example, Java makes sure that all variables are initialized before they are used. This helps to achieve the goals of being robust and secure. If Java determines that some variable might not be initialized, it will tell you about it and will refuse to translate your program into bytecodes until you fix it.

Once your program compiles properly, it generates a file of Java bytecodes that can be executed by the interpreter. This does not mean it is free of errors. You might still have logic errors in your code. A logic error or bug occurs when you say one thing when you really meant something else. Every programmer makes logic errors, so you will have to learn to deal with them.

The form that a bug takes will vary a lot. Sometimes your program will make a request that is clearly illegal, which leads to an execution or runtime error. For example, you are likely to get a "null pointer exception" when you execute some of your programs. This happens when you tell Java to ask an object to do something when there isn't an object to ask. Java is unable to continue executing your program, so it generates the exception. The word "exception" is just another word for "error." In a later chapter we will see how exceptions work in Java.

There are many other ways that a bug can manifest itself. Your program might go into what we call an infinite loop or it might simply behave incorrectly.

1.11 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
Last modified: Fri Oct 1 15:04:50 PDT 2004