HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML

This excerpt discusses the specifics of coding the Student Registration System (SRS). Java is an extremely rich language, and our goal is not to duplicate the hard work that has gone into existing Java language books, but rather to complement them by showing you how to bridge the gap between producing an object model and turning it into live Java code.

Setting Up a Java Programming Environment
Everything you'll need to get started programming in Java on various platforms - Solaris, Windows, Linux - is available for free with Sun Microsystems' Java 2 Software Developer's Kit (SDK), which can be downloaded from Sun's Web site, http://java.sun.com. We strongly advise that you take the time now to establish your Java development environment, so you'll be prepared to experiment with the language as you learn.

Note that the Java 2 SDK is a command line-driven toolkit, which means that on a Windows platform you'll be opening up an MS-DOS Prompt window, from which you'll be doing all of your work (UNIX and Linux are naturally command-line oriented). Of course, there are also numerous Java Integrated Development Environments (IDEs) to choose from, some of which are available on a free trial basis as Web downloads. However, my personal bias is that if you first learn Java from the ground up, writing all your code from scratch using only Sun's SDK and your favorite text editor, you'll gain a much better understanding of Java language fundamentals than if you rely too heavily on an IDE, particularly those that provide drag-and-drop, GUI-building capabilities and automated code generation. You can always consider graduating to an IDE after you've mastered the language to take advantage of their debugging and code/project management features, among others.

Anatomy of a Java Program, Revisited
The anatomy of a trivially simple Java program, which consisted of a main() method, contains the logic of our program inside of a class "wrapper" (see Figure 1).

Figure 1
Figure  1:

Such a class would reside in a file by the name of classname.java; Simple.java, in this example.

Note: The external name of the file containing the source code of a Java class must match the name given to the class inside the file, including the same use of upper/lowercase, with .java tacked on at the end. Java is a case-sensitive language, even on operating systems like DOS that are traditionally case insensitive in most respects.

A common error for beginners is to assume that case doesn't matter, and to name the file for our example program simple.java, SIMPLE.java, or some other variation. This leads to compilation problems, as we'll see in a moment.

A nontrivial Java application consists of many such .java files, because the source code for each class comprising your application typically (but not always) resides in its own *.java file.

You'll have one .java file for each of the domain classes that you defined in your object model. For the SRS application, for example, we'll have eight. Course.java, Person.java, Professor.java, ScheduleOfClasses.java, Section.java, Student.java, Transcript.java, and TranscriptEntry.java.

You'll also typically have a separate .java file for each of the primary "windows" comprising the graphical user interface of your application, if any. For the SRS application, we'll have two: MainFrame.java and PasswordPopup.java.

Typically you'll have a separate .java file that contains the "official" main() method that serves as the application driver. One of the primary responsibilities of this driver class's main() method is to instantiate the core objects needed to fulfill a system's mission; of course, actions taken by the objects as well as by the user will cause additional objects to come to life over time, as the application executes. The main() method is also responsible for displaying the start-up window of the graphical user interface of an application, if any (see Figure 2).

Figure 2
Figure  2:

We'll name the driver class for our Student Registration System application SRS, so of course it will need to be stored in a file named SRS.java.

Finally, you'll quite possibly have other "helper" classes necessary for behind-the-scenes application support; with the SRS, we'll have a need for three such classes: CollectionWrapper.java, CourseCatalog.java, and Faculty.java.

Assuming that you've properly installed Sun's Java 2 SDK, a Java source code file (.java file) can be compiled at the command line via the command:

javac classname.java
for example:
javac Simple.java
Again, pay close attention to match upper/lowercase usage; if you were to name your class Simple (uppercase "S"), but store it in a file named simple.java (lower case "s"), the Java compiler would generate the following compilation error:
Public class Simple must be defined in a file called 'Simple.java'.
Note that you can compile multiple files in a single step:
javac file1.java file2.java ...

filen.java
or
javac *.java
Assuming that no compilation errors occur, compilation produces at least one classname.class file for every .java file; for example, Simple.class for our sample program (see Figure 3). (We'll see later in this article why more than one .class file might be produced from a single .java file.) The *.class files contain platform-independent Java byte code that can be run on any platform for which there is a Java Virtual Machine available.

Figure 3
Figure  3:

To run a Java program from the command line, type the following to invoke the JVM:

java MainClassName
for example:
java Simple
or
java SRS
where MainClassName is the name of the class file (minus the .class suffix) containing the compiled byte code version of the "official" main() driver method for your application.

The JVM loads the byte code for whatever class you've named, and if it discovers a main() method within that byte code with the proper signature (recall that the name of the argument being passed into the main() method - args, in this case - is the only thing that's flexible in the signature):

public static void main(String[] args)
then the JVM executes that main() method to jump-start your application. From that point on the JVM will load additional classes - either classes that you've written and compiled or classes built into the Java language - as needed, when referenced by your application. That is, the first time the SRS application has occasion to refer to the Person class, the byte code for the Person class will be loaded into the JVM, and so forth.

It's important that you don't type the .class suffix when attempting to run a program, as you'll get an error:

java Simple.class

Exception in thread "main" java.lang.NoClassDefFoundError: Simple/class

which is, to say the least, not very intuitive! This particular error message arises because the Java compiler interprets the name Simple.class as being the name of a class called "class", to be found within a package called "Simple"; we'll be talking about packages shortly.

Why must the main() method of an application be declared static? At the moment that the JVM first loads whatever class you've told it to load to jump-start your application, no objects exist yet, because the main() method hasn't yet executed; and it's the main() method that will start the process of instantiating your application's objects. So, at the moment of application start-up, all the JVM has at its disposal is a class; and a static method is a type of method that can be invoked on a class as a whole, even if we don't have an instance of that class handy.

One final note about program structure: we said that the source code for each class comprising your application typically resides in its own .java file. It's actually permissible to place the source code for two or more Java classes back to back in the same physical .java file. We don't generally do so, however, as it's much easier to manage Java source code when there is a one-to-one correspondence between the external file name and the internal Java class name. If we were to combine multiple class definitions back-to-back in a single .java file, however, they would each produce their own .class file when compiled.

Importing Packages
To appreciate import statements (see Figure 4), we first must understand the notion of Java packages.

Figure 4
Figure  4:

Because the Java language is so extensive, its various built-in classes are organized into logical groupings called packages. For example, we have:

  • java.sql: Contains classes related to communicating with Object Database Connectivity-compliant relational databases
  • java.io: Contains classes related to file input/output
  • java.util: Contains a number of utility classes, such as the Java collection classes that we'll be learning about
  • java.awt: Contains classes related to GUI development
Most built-in Java package names start with java, but there are some that start with other prefixes, such as javax. If we acquire Java classes from a third party, they typically come in a package that starts with com.companyname, for example, com.xyzcorp.stuff.

The package named java.lang contains the absolute core of the Java language, and the classes contained within that package are always available to us whenever we write Java programs, so we needn't worry about importing java.lang. However, if we wish to instantiate a Vector (one of Java's built-in collection classes) as an attribute inside one of our classes, for example, then we must import the java.util package, as the following example illustrates:

// Simple.java

// Our class needs to instantiate a Vector, and so we must import the package
// that defines what a Vector is.

import java.util.*;

public class Simple {
public static void main(String[] args) {
Vector v = new Vector();
}
}

The asterisk (*) at the end of the import statement above is a wild card character; it informs the Java compiler that we wish to import all of the classes contained within the java.util package. As an alternative, we can import individual classes from a package:
// ImportExample.java

// We can import individual classes, to better document where each class
// that we are using originates.

import java.util.Enumeration;
import java.util.Vector;
import java.util.Date;
import java.io.PrintWriter;

// etc.

Of course this requires more typing, but it serves as better documentation of where each class that we're using in our program originates.

If we were to attempt to reference the Vector class in one of our classes without this import statement, we'd get the following compilation error when compiling that particular class:

Class Vector not found.
This is because Vector is not in the name space of our class; that is, it's not one of the names the Java compiler recognizes in the context of that class. Generally speaking, the name space for a given class contains the following categories of names, among others:
  • The class
  • All the attributes of the class
  • All the methods of the class
  • Any local variables declared within a method of a class
  • All classes belonging to the package that the class in question belongs to
  • All public classes in any other package that have been imported
  • All public features (attributes, methods) of any of the classes whose names are in the name space
  • All public classes in java.lang
We could work around the failure to import a package by fully qualifying the names of any classes, methods, or other, that we use from such a package; that is, we can prefix the name of the class, method, or other, with the name of the package from which it originates, as shown in the next example:
// Simple2.java

// no import statement

public class Simple {
public static void main(String[] args) {
java.util.Vector v = new java.util.Vector();
}
}

This, of course, requires a lot more typing, and impairs the readability of the code.

Although most built-in Java packages have names that consist of two terms separated by periods (for example, java.awt) some built-in Java packages have three, for example, java.awt.event. As far as packages developed by third-party organizations are concerned, there's really no limit to the number of terms that can be concatenated to form a package name. The important point to note about all of this is that the statement:

import nameA.nameB.*;
will only import classes in the nameA.nameB package; it won't import classes in the nameA.nameB.someothername package. That is, the wild card pertains to class names only.

It's also important to note that importing a package is only effective for the particular .java file in which the import statement resides. If you have three different classes of your own that all need to manipulate Vectors, then all three of their .java files must include an import statement for java.util.

Java also provides programmers with the ability to logically group their own classes into packages. If we wanted to, we could invent a package, such as com.objectstart.srs to house our SRS application. Then, anyone else wishing to incorporate our SRS classes within an application that they were going to write could include the statement:

import com.objectstart.srs;
in their code, and even though our compiled class files are kept physically separated from their application's compiled class files, our classes would become logically combined with theirs, assuming a few other environmental details had been taken care of.

Going into a detailed discussion of how to create our own packages is beyond the scope of this article. But, as it turns out, if you do nothing in particular to take advantage of programmer-defined packages, then as long as all of the compiled .class files for your code reside in the same directory on your computer system, they're automatically considered to be in the same package, known as the default package. All the code that we write for the SRS application will be housed in the same directory, and hence will fall within the same default package. This is what enables us to write code such as:

public class SRS {
public static void main(String[] args) {
Student s = new Student();
Professor p = new Professor();
// etc.
}
}
without using import statements, because Student, and Professor, and SRS, and all of the other classes comprising our SRS applications are within the same default package.

The bottom line is that import statements as a building block of a .java source code file are optional; they're needed only if we're using classes that are neither found in package java.lang nor in our own (default) package.

Author Bio
Jacquie Barker is a professional software engineer and adjunct faculty member at George Washington University and Johns Hopkins University and a principal member of the technical staff at SRA International, Inc. in Fairfax, Virginia. She holds a BS in computer engineering from Case Western Reserve University and an MS in computer science, from UCLA. [email protected]

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.