Chapter 3. Explanation of a Sample Program

Explanation of a Sample Program

Ever since The C Programming Language (Brian Kernighan and Dennis Ritchie, Prentice Hall) was published in 1978, writers of programming textbooks have been using the “Hello World” program as an introductory example. Programmers deserve a bit of innovation. Java is the first popular language to support graphics, networking, multimedia, multithreaded code, and software portability. Surely we can do a bit better than a first example that spits up a bit of text on the screen?

Chapter 1, “ What Is Java?,” introduced an example program that did some GUI work to display a window and to roll some text across it in several directions and colors. In this chapter we’ll look at that code in more detail and explain how it works. Then we’ll round off the chapter with a discussion of the stack and the heap and what they do.

If you haven’t yet installed the JDK and tried running the myframe example from Chapter 1, now would be an excellent time to do that. As the great Arnold Schwarzenegger once said, “You can’t get muscles by watching me lift weights.”

The source listing appears on the following page with the annotations appearing on the page after that.

Explanation of a Sample Program

Explanation of the Example Program

  1. The import keyword saves the programmer from having to write out the full package names of all the library classes that will be used in this source file. Here we are importing java.awt.* which means all the classes in the java.awt package, just as * means all the files in a directory. Doing this import means we can write Frame or Font instead of java.awt.Frame or java.awt.Font. The java.awt package contains basic windowing support.

  2. This box encloses the class we have written. The class is called myframe, and it has some field members (boxes 4 and 5) and two method members (boxes 6 and 7).

  3. To say that we want this class to be a subclass of the Frame class, use extends Frame. Frame is a class in the AWT package that displays a basic window on the screen. By saying we extend Frame, we get all the things that Frame can do, plus we can add our own specializations or variations.

  4. These four fields (x, y, i, horizScroll) are declared static so that we can reference them without an instance of the class existing. The field horizScroll controls whether the text is scrolled horizontally or vertically across the screen. The program alternates every few words between the two directions.

  5. These three fields represent the text that we are going to move across the screen and its color and font. Font and Color are two classes from the java.awt package. We declare a Font variable called fb and create an instance of it using a constructor from the Font class. The variables msg and color are both four-element arrays which we initialize here.

  6. The paint() method is a standard part of many classes in the awt package. The convention is that the Java runtime calls it when the window system needs to update what is on the screen. Since we extended the Frame class, our version of paint() here replaces the basic one in Frame. (This is a piece of OOP that we haven’t yet covered.)

    When you call setVisible(true) on a Frame, the window system knows it has to display it. It does that by calling our paint method at the right times to put it up on the screen. It will call paint when execution starts and any time after that when we request it. The statements inside paint set the default font and color, and then write some text from msg[i] onto the graphics context argument at location x,y. The window system translates that into pixels on the screen.

  7. The main() method has a special signature, or method name and arguments, that is recognized as the place where a program starts executing when you run the class that it is in. More about this in the next section.

  8. The first variable we declare inside the main routine is a myframe. That gives us an instance variable on which we can invoke the ordinary (non-static) methods of myframe and its parent Frame. The first statement is a method call, mf.setSize(200,200), to set mf ’s size to 200 pixels wide and 200 pixels high. Again, this is a method we inherit by virtue of extending java.awt.Frame.

  9. This is in the body of a loop that is executed a few hundred times. Our first action is to go to sleep and delay the program for 25 milliseconds. Since the method sleep() is a static method of the standard Java runtime class Thread, we can just invoke it with the classname. Animation looks better if you match the speed of display changes to the persistence quality of the human eye. About 40 changes per second is plenty.

    After the loop delay, we ask for the mf instance of myframe to be scheduled for repainting on the screen. What we are going to do here is change the location where we display the text a little bit each time through the loop. The overall effect will be to make the text appear to glide across the screen.

  10. This is the “else” part of an “if...then...else” statement that is in the body of the big loop. Just for fun, the program alternates between rolling the text across the screen horizontally and vertically. This “if” statement is where the choice is made. If the horizScroll variable has the value 1, we execute the “then” part. Otherwise, we execute the “else” part that drops the text vertically.

    First, we increase y by three and check that the result is less than 200, that being the height of the screen. The variable y, of course, is used in paint as one of the coordinates for locating the text message. By changing its value, we change where the text is drawn. If y is less than 200, then continue. That means branch immediately to the head of the loop. If y is greater than 200, then we are at the end of scrolling the text down, and we want to select another message and color, and scroll the other way. We increment i, ensuring it stays in the range 0–3, we reset x, y and we change horizScroll so that the “then” part of the “if” statement before 10 will be chosen. That clause is similar to the “else” clause, but increments the x variable, instead of y, to move text horizontally.

Applications vs. Applets vs. Servlets

A Java program can be written to run in these three different ways:

  • As a stand-alone program that can be invoked from the command line, termed an application. The sample program we were reviewing is an application.

  • As a program embedded in a web page, to be downloaded to the client and run in the browser when the page is browsed, termed an applet. Just as a booklet is a little book, an applet is a little application.

  • As a program invoked from a web server, to be run on the server when a particular URL is browsed, termed a servlet.

The execution vehicles differ in the default execution privileges they have and the way in which they indicate where to start execution. Almost all the code and examples are the same whether you are writing an application, an applet, or a servlet. Only a few trivial startup details differ. We will deal with applications in this chapter. Applets appear in a later chapter.

How and why to set up a servlet is dealt with at length towards the end of the book. The Tomcat add-on to the Apache web server is included on the CD, so you can actually turn your system into an servlet server. Apache is the most widely used web server in the world, according to statistics at www.netcraft.com/Survey/Reports/200108/platform.html.

Netcraft also keeps statistics on how long different servers have been running, and it publishes them at uptime.netcraft.com/up/today/top.avg.html.

The top end is completely dominated by Unix servers running Apache. Other systems and servers do not even rate a mention in the top fifty available and reliable web sites.

Where an Application Starts

Looking at the signature [1] of main (), which is the method in which every application starts execution, we see the following:

public static void main(String args[]) {

The keywords say that the function is:

  • publicvisible everywhere.

  • statica class method that can be called using the classname without needing an object of the class. Static methods are often used where you would use a global function in C or C++. That is, you are not doing something to some specific object, you are just doing something that generally needs doing.

  • voidthe method does not return a value.

That last point is a difference between Java and C/C++. In C, main() is defined to return an int. In Java, main() is defined to return a void, that is “no value.” Java has a better way to indicate program failure, namely, throwing an exception.

The main() routine where execution starts is a static method. That means it can be invoked before any individual instance objects have been created.

Passing over the modifiers, the actual function is as follows:

void main(String args[]) {

It declares a function called “main” that has no return value, and takes just one argument here called “args” (the parameter name doesn’t matter, just its data type) which is an array of Strings. The empty array bracket pair is a reminder that the function is not restricted to any one size of array. The strings are the command-line arguments with which the program was invoked. “String” is a class in Java with more to it than just the nul-terminated character array that it is in C.

You don’t need a separate count of the number of arguments, because all arrays have a length field holding the size of the array. Simply use args.length, which gets the number of strings in the args array.

The zeroth argument in the args array is the first command-line argument, not the program name as in C and C++. The program name is already known inside the program: It is the name of the class that contains the “main()” function. The name of this class will match the name of the file it is in. This framework is all that is needed to indicate the entry point program where execution will start.

We’re working from the middle out here. Having seen our first reasonable Java program, we’ll look at the small building blocks of individual tokens that the compiler sees in the next chapter.

For the rest of this chapter we’ll cover the stack and the heap—which are a couple of popular runtime data structures common to many programming languages—we’ll review how to read the Java API, then we’ll finish up with some light relief.

Runtime Internals: Stack and Heap

The stack is a runtime data structure for keeping track of memory to support functions and recursive function invocation. All modern block-structured languages use a stack. Many processors have some hardware support for them.

When you call a method, some housekeeping data, known as an activation record or stack frame, is pushed onto the stack. The activation record contains the return address, the arguments passed to the function, the space for local variables, and so on. When you return from a function, the record is popped from the stack. The next function call will push another record into the same space (see Figure 3-1).

Stacks in Java.

Figure 3-1. Stacks in Java.

If you have any pointers back into the old activation record on the stack, memory can be corrupted, as the pointer references an area that the next function call will reuse for a different purpose. This is a common problem in C/C++, and can be hard to debug. In other words, the lifetime of stack-based storage is tied to the scope in which it was allocated, and although some languages let you get this wrong, Java doesn’t!

The heap is another runtime data structure used to support your program. It is a large storage area that is managed dynamically. All objects in Java are allocated on the heap; no objects are ever allocated on the stack.

Many programming languages support heap-based memory allocation. Different pieces of storage can be allocated from and returned to the heap in no particular order. Whenever something is allocated on the heap, its lifetime is independent of the scope in which it was allocated.

Fruit saveIt; 
void foo() {
     Fruit f = new Fruit(); 
     saveIt = f; 
} 

... foo(); 
saveIt.grams = 23; // is it valid? f was allocated in foo 
                   // and foo is no longer live 

In the code above, a Fruit object is allocated in a method and a reference to that Fruit is stashed away for later use. Later, after the method has returned, the reference is used to access the Fruit. This is valid for heap-based storage. It is not valid in stack-based storage and is the source of a large number of high-profile bugs and failings in C/C++ code. The Internet Worm of 1988 used this flaw to subvert a system. Ten years later, the Hotmail bug of 1998 fell vulnerable to the same flaw! Since Java uses only heap-based storage for variables accessed through references, objects can and do happily live on after the scope in which they were created.

Since the heap and the stack both grow dynamically on demand, they are usually put at opposite ends of the address space and grow into the hole between them.

More Terminology. These characters are called parentheses: ( ). They go around expressions and parameter lists.

These characters are called square brackets: [ ]. They go around array indexes.

These characters are called braces or curly braces: { }. They indicate the start and end of a new block of code, or an array literal.

The Class “Object”

Most of the following chapters will present a brief listing of one of the standard built-in classes. You should just scan the list, look at the typical methods, and then go on. Later when you want to refer to the class in detail, you can look back at the appropriate chapter.

Related Java classes are grouped into packages. Packages are software libraries, and they correspond to file directories in most implementations. They are hierarchical, just like directories. All the classes in one directory are in the same package.

You write a line at the top of a source file to give the name of the package a class it belongs to. You can refer to packages by name in your Java code to reference some other class. The name of a package is the same as the name of the directory it is in. Java comes with a very large standard library stored in a package called “java.” Part of the java package is a subpackage called “lang.” The lang (language) package contains 20 or 30 standard classes that are imported automatically into every compilation. One such class is java.lang.Object.

We’ve mentioned a couple of times that there is a class called “Object” that is the ultimate superclass (parent class) of all other classes in the system. The members that exist in Object are thus inherited by every class in the system. Whatever class you have, an instance of it can call any of these methods. Here is what Object has:

public class Object {
     public java.lang.Object(); 
     public java.lang.String toString(); 

     protected native java.lang.Object clone() throws 
                                   CloneNotSupportedException; 

     public boolean equals(java.lang.Object); 
     public native int hashCode(); 

     public final native java.lang.Class getClass(); 

     // methods relating to thread synchronization 
     public final native void notify(); 
     public final native void notifyAll(); 
     public final void wait() throws InterruptedException; 
     public final native void wait(long) throws 
                                             InterruptedException; 
     public final void wait(long, int) throws 
                                             InterruptedException; 
} 

The “throws SomeException” clause is an announcement of the kind of unexpected error return that the method might give you.

There are several variations on the standard OS synchronization primitives wait() and notify(). These are described in Chapter 11. The remaining methods in Object, and thus in every class, are further highlighted below.

In each case, the method offers some useful, though often elementary, functionality. Programmers are supposed to provide their own version of any of these methods to replace the basic one if necessary. When an object (any object) calls a method, its own version of that method is preferred over an identically named method in a parent class, as we’ll see in the second chapter on OOP.

String toString();This is a very handy method! You can call it explicitly, and it will return a string that “textually represents” this object. Use it while doing low-level debugging to see if your objects really are what you thought they were. Here’s what I get for Fruit:

public class Fruit {
     int grams; 
     int cals_per_gram; 

     public static void main (String args[]) {
          Fruit f = new Fruit(); 
          System.out.println( " f = " + f.toString() ); 
     } 
} 

When you run the program, the output is as follows:

f = Fruit@a04c8d82 

It is also invoked implicitly when any object and a String object are joined in a “+” operation. That operation is defined as String concatenation. To turn the other object into a String, its toString() method is called. It constitutes a piece of “magic” extra operator support for type String. The “+” operator is the only operator that can be applied to an object, at least until operator overloading is brought into Java, possibly with JDK 1.5.

Object clone() throws CloneNotSupportedException. The “native” keyword says that the body of this method is not written in Java, but will be provided in a native library linked in with the program.

Java supports the notion of cloning, meaning to get a complete bit-for-bit copy of an object. Java does a shallow clone, meaning that when an object has data fields that are other objects, it simply copies the reference. The alternative is to recursively clone all referenced classes, too, known as a deep clone.

As a programmer, you can choose the exact cloning behavior you want. If a class wants to support deep cloning on its objects, it can provide its own version of clone(). When you provide your own version of clone, you can also change the access modifier to public (so other classes can call it and hence clone this object) rather than the default protected (outside this package, only subclasses can clone).

Note that clone() returns an Object. The object returned by clone() is usually immediately cast to the correct type. “Cast” is the Java term for type conversion, and it is written as the new type in parentheses immediately before the thing that is being converted, as in this example:

Vector v = new Vector(); 
Vector v2; 

Object o = v.clone(); // clone the Vector 
v2 = (Vector) o;      // cast it back to Vector type 

Casting is “type safe,” meaning that the validity of the type conversion is checked at runtime, and it will cause an error if you try to cast an object to a type that it does not have.

Not every class should support the ability to clone. If I have a class that represents unique objects, such as employees in a company, the operation of cloning doesn’t make sense. Since methods in Object are inherited by all classes, the system places a further requirement on classes that want to be cloneable—they need to implement the cloneable interface to indicate that cloning is valid for them. Implementing an interface is described later.

boolean equals(Object obj). The method does a comparison of reference types, such as:

if (obj1.equals(obj2) ) ... 

This is equivalent to the following:

if (obj1 == obj2 ) ... 

The method is provided so that you can supply your own version of equals() to give it whatever semantics make sense for your class. This has already been done for String, because two Strings are usually considered equal if they contain the exact same sequence of characters, even if they are different objects. When you override equals(), you should also override hashCode() to make certain equal Objects get the same hashcode. If you fail to do this, your objects will fail to work in certain data structures, which are described later.

native int hashCode (). Ideally, a hashcode is a value that uniquely identifies an individual object. For example, you could use the memory address of an object as its hashcode if you wanted. It’s used as a key in the standard java.util.Hashtable class. We’ll say more about hashtables later.

final native Class getClass(). All class types have an object that represents them at runtime. This runtime representation is known as Run Time Type Identification or RTTI. The method getClass( ) gets that RTTI information, returning it in an object of type java.lang.Class.

The class whose name is Class has several methods to tell you more about the class to which an arbitrary object belongs. You invoke the getClass method in the usual way:

Class whatAmI = myobject.getClass(); 

Reading the Java API

This is a very important section! You should do the download and try the steps shown. We will use java.lang.Object as an example to demonstrate how to use Java’s documentation. The Java Application Programmer Interface (API) is a vast collection of libraries. Luckily, it is well documented, and the documentation is easily accessed in a browser.

The API documentation is a separate download from the JDK. You can also browse it online. I find it most convenient to download the API documentation, which you can do by following these steps:

  1. Go to java.sun.com and follow the links to “Products and APIs” then to “Java 2 Platform, Standard Edition.” Or just search for “J2SE documentation.”

    That will take you to a J2SE page which offers you three downloads for each of the various releases:

    1. J2SE software development kit (also known as the JDK)

    2. J2SE runtime library for running programs only (aka JRE)

    3. J2SE API documentation

  2. For the downloadable documentation, click on the first of these, not the API documentation link (I know, it’s perverse). You can choose any release, but I recommend the most recent that is available there. The API documentation link takes you to a page full of documentation links to online documentation. Browse that, too, according to time and interest.

  3. The Java 2 SDK link takes you to a page with a prominent link marked “Download documentation.” Finally! If you have not yet downloaded the JDK, you can and should do it from this page first.

    Click on the “download documentation” link, and then on the “continue” button. Then “accept” the click-through license. Finally, finally, click on “FTP download.”

  4. That lets you save a file called something like j2sdk-1_4_0-doc.zip onto your system. It’s about 30 MBytes, so that’s a couple of hours of download using a 56Kbps modem. 56Kbps is about 5 Kbytes per second.

  5. Once the zip file is on your system, you want to unpack it into the same directory where you installed the JDK. Let’s say that was C:jdk1.4. Enter the directory with the command “cd c:jdk1.4”. Move the zip file here, too.

  6. You can use any unzip utility to unpack the documentation. I like to use the “jar” utility that is part of the JDK. Once you are in the correct directory, the command is: “ jar -xf j2sdk-1_4_0-doc.zip ”. That creates a docs subdirectory of the directory you are in and puts the files there.

  7. At last you are in a position to look at the API documentation. Start up your favorite browser and browse the URL file:/c:/jdk1.4/docs/api/index.html. When the page comes up, set a bookmark for it. Your browser should be displaying something like Figure 3-2.

    The API documentation.

    Figure 3-2. The API documentation.

This is the standard form of the Java API documentation. There is a tool called “javadoc” that reads Java source code and generates this HTML documentation. All the Java library code is heavily annotated with comments, which javadoc processes and displays to you (see Figure 3-3).

Detailed and general information in Frame 2.

Figure 3-3. Detailed and general information in Frame 2.

There are three frames in the window. Frame 1 has a scrolling list of all package names in the release. Frame 2 has an alphabetical list of all classes. Frame 3 starts off by displaying more detail on each package.

To look at a particular class, you can either click on its name in Frame 2, or if you know the package it is in, you can click on that package in Frame 1. That causes Frame 2 to display all the classes in that package. Try it now, with package java.lang. Then click on class “Object” in Frame 2.

That brings up all the information about class java.lang.Object in Frame 3. You will see documentation on all the methods of the class, the constructors, the error conditions it can hit, and often some more general information too.

You should get into the habit of using the javadoc documentation heavily. You should not use any library class without first reading through its API in your browser. You should supplement the library classes presented in this book by reading the HTML from javadoc.

Some Light Relief—Napster and LimeWire

Everyone knows the story of Napster by this point. College student Shawn Fanning (baby nickname: “the napster” because of his sleeping habits) threw together some rickety old Windows software to broadcast the titles of any music tracks you had on your PC to a central database.

Other users could search that database looking for titles they wanted. When they found a match, the Napster software would set up a peer-to-peer connection and allow direct transfer of the music bits from your PC to the unknown fan elsewhere. Your incentive to share was that you in turn would be able to get files from other people similarly sharing their titles.

That was the concept at least. In practice, the record companies and some bands took exception to freelance distribution. By this time, Fanning had parlayed his idea and some prototype software into a start-up venture backed by premier venture capitalist company Kleiner Perkins Caulfield and Byers. At its peak, Napster claimed it had 70 million users, and even if they only exaggerated by the industry standard factor of five, that’s still a lot of users.

Napster’s demise took a couple of years to wind through the legal system, but the central point never seemed that subtle to me: you cannot legally broker the wholesale transfer of other people’s intellectual property. Naturally, the record companies conducted themselves with their usual rapacious shortsightedness. Instead of licensing the Napster software and charging a membership fee, they tried to sue Napster out of existence. It’s a reprise of 1992 when they killed DAT (Digital Audio Tape) by encumbering it with anticopying hardware backed by law.

Which brings us to the current situation. Napster is just about completely dead. Shawn Fanning will resume his college education if he has any sense. And a number of open source, distributed peer-to-peer music-sharing databases have replaced Napster’s centralized model. All the file sharing goes on as before, but in a decentralized way, so the record companies have no deep-pockets adversary to sue. Ironic, isn’t it? By killing Napster, they also killed their best chance to make money on the net.

When I first tried the Napster software in its heyday, all the way back in the last millennium, my first thought was, “why on earth didn’t they write this in Java?” It was a simple network database lookup with peer file transfer capability front-ended by a simple GUI. Tailor-made for Java! Fanning was not familiar with Java, so he churned out his Windows-only software. Though Napster has now passed on to that great big recording studio in the sky, others still carry the conductor’s baton.

Bearshare, Gnutella, and LimeWire are currently the most popular applications for sharing files (including .mp3 music files) across the net. They use the Gnutella client protocol, which is a search engine and file serving system in one. It is wholly distributed. Anyone can implement it to share their content (any files) with others. The great thing about LimeWire is that it is written in Java.

You can download the application from www.limewire.com and see for yourself. The main screen is shown in Figure 3-4.

LimeWire: Napster’s successor.

Figure 3-4. LimeWire: Napster’s successor.

Since LimeWire is written in Java, the program runs on Windows, Macintosh, Linux, Solaris, IBM mainframes, and other computing platforms. The application has a solid, professional feel to it, and is fully functional. It has more than 3 million users, and is estimated to be present on 1.5% of all PCs.

LimeWire is now being sued by the record industry. Win or lose, they will probably be able to drive LimeWire out of business. LimeWire has responded by going open source, so download a copy of the application while you can, and see how the experts write Java GUIs.

When someone asks me about Java client applications, LimeWire is one of the programs I like to show them.



[1] Strictly speaking, the signature doesn’t include the access modifiers or return type.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.53.202