Richard G Baldwin (512) 223-4758, baldwin@austin.cc.tx.us, http://www2.austin.cc.tx.us/baldwin/

Packages

Java Programming, Lecture Notes # 39, Revised 10/03/99.

Preface
Introduction
Classpath Environment Variable
Developing Your Own Packages

Preface

Students in Prof. Baldwin's Introductory Java Programming classes at ACC are responsible for knowing and understanding all of the material in this lesson.

This lesson is a major rewrite and relocation of the material previously in lesson # 48. The lesson was rewritten to improve its content. It was relocated to provide the necessary information on packages ahead of the lesson on access control.

Introduction

The next lesson following this one is on access control. Because access control in Java is intimately tied to the concept of packages, this lesson is being placed in this location to teach you about packages before attempting to teach you about access control.

Before you can understand much about packages, you will need to understand the classpath environment variable, so that is where we will begin our discussion.

After learning about the classpath environment variable, we will learn how you can create your own packages.

Classpath Environment Variable

The purpose of the classpath environment variable is to tell the JVM and other Java applications (such as the javac compiler and the java interpreter) where to find class files and class libraries. This includes those class files which are part of the JDK, and class files that you may create yourself.

It is assumed that you already have some familiarity with the use of environment variables in your operating system. This discussion will focus on DOS/Win95 because that is what this author is most familiar with. All of the discussion in this lesson will be based on the use of DOS/Win95. Hopefully you will be able to translate the information to your operating system if you are using a different operating system.

In a nutshell, environment variables provide information that the operating system uses to do its job.

There are usually a fairly large number of environment variables installed on a machine at any give time. If you would like to see what kind of environment variables are currently installed on your machine, bring up an MS-DOS Prompt screen and enter the command set. This should cause the names of several environment variables, along with their settings to be displayed on your screen.

While you are at it, see if any of those items begin with CLASSPATH=. If so, you already have a classpath environment variable set on your machine.

For example, my machine currently displays the following line for the classpath variable. A little later I will show you how this came to be.

CLASSPATH=.;c:\Baldwin\JavaProg

For purposes of working with Java, there are several ways that you can set the classpath variable. One way is to execute a set command at the DOS prompt such as the following;

SET CLASSPATH=.;c:\Baldwin\JavaProg

You need to be careful with this because it will replace your existing classpath environment variable with the new one that you type in. classpath variable settings can be quite long, so it is easy to misspell something. A safer way is to edit this command into a batch file and make certain that it is correct before executing the batch file.

Another way to set the classpath environment variable is to enter the information into your autoexec.bat file. This can be even more dangerous because you might destroy something else that is in that file that is critical. Before you attempt to do this, be sure to make a backup copy of the file. The following line is contained in my autoexec.bat file.

SET CLASSPATH=.;c:\Baldwin\JavaProg

You should be able to recognize the elements here that make up the classpath setting that I showed you earlier.

The third way to set the classpath when working with java is to use a command-line parameter that is available with javac and java and possibly some of the other JDK tools as well. For example, to compile a program and set the classpath at the same time, I could enter the following at the DOS prompt (all on one line).

javac -classpath .;c:\Baldwin\JavaProg;c:\java_jdk\
Java\lib\classes.zip junk2.java

In this case, junk2.java is the name of the Java source file that I am compiling If you specify the classpath with javac, be sure to also specify the same classpath later when you use java to run the program.

The problem with this approach is that you have to add the portion that is highlighted in boldface as it pertains to your particular installation of the JDK. It is unlikely that your installation will be exactly the same as mine, so what you will have to enter will be different (but probably only different at the beginning).

You can examine the directory structure on your machine to determine the path to get from the letter that identifies your hard drive (such as c:) to the file named classes.zip and substitute that path in place of what I have above.

That leaves the question of what do you use in place of [.;c:\Baldwin\JavaProg;]. That is a fairly complex topic. We'll come back to that topic later.

This is definitely a case where you will probably want to construct a batch file once and use it over and over again. You can even use the %1 substitution indicator for the file name and simply enter the name of your batch file followed by the name of your Java source file at the DOS prompt.

There are some rules that you must follow when working with the classpath variable, and if you fail to do so, things simply won't work.

For example, if your class files are in a zip file or a jar file, the classpath must end with the name of the file.

On the other hand, if the class files are not in a zip or jar file, the classpath must end with the name of the folder that contains the class files.

Your classpath must contain a fully-qualified path name for every folder that contains class files of interest, or for every zip or jar file of interest. The paths should begin with the letter specifying the drive and end either with the name of the zip or jar file or the name of the folder that contains the class files.

The rules are a little different for setting the classpath when you are creating your own packages. We will get to that later.

If you are not creating class files as part of a package, but simply want to make certain that the Java interpreter can find them in order to execute them, your classpath should end with the name of the folder containing the class files.

For example, if you have class files in the folder C:\java\MyClasses, you could set the classpath with the following:

set CLASSPATH=c:\java\MyClasses 

If you also have class files in a folder named C:\java\Other Classes and want to include the path C:\java\OtherClasses, you could use the command:

set CLASSPATH=C:\java\MyClasses;C:\java\OtherClasses 

Note that the two paths are separated by a semicolon.

If you followed the default JDK installation procedure and are simply compiling and executing Java programs in the current directory you probably won't need to set the classpath. By default, the system already knows (or can figure out) how to allow you to compile and execute programs in the current directory and how to use the JDK classes that come as part of the JDK.

In effect, the runtime system appends the proper classpath to the existing classpath at runtime to provide the support described above.

However, if you are saving your class files in one or more different folders, or are ready to start creating your own packages, you will need to set the classpath so that the system can find the class files in your packages.

As an alternative to setting the classpath to accommodate folders containing your class files, the default classpath also includes a path to a classes folder on the same directory level as the bin and lib folders in a standard JDK installation. You can put your own (unzipped) class files in a classes folder that you create there, and the Java executables will be able to find them with the default classpath.

If you did not follow the default installation instructions when you installed the JDK, I am going to assume that you know what you are doing and will not attempt to provide any guidance. If all else fails, you can go back and re-install the JDK according to the default installation instructions.

Also, I'm not going to address the possibility at this point of saving your files in zip or jar files. If you are that far along, you probably don't need any help anyway. I am planning a lesson to be written later that discusses the use and ramifications of zip and jar files.

So, the bottom line on classpath, when I am not creating packages, is the following.

My classpath setting looks like this:

CLASSPATH=.;c:\Baldwin\JavaProg 

The (.;) at the beginning puts the current directory on the classpath. This is probably superfluous because this is also included in the default classpath provided by the Java runtime system.

The important point is that I can store class files (that were compiled without a package directive) in the folder identified by c:\Baldwin\JavaProg and can execute those classes from any folder on my machine, because that folder is "on the classpath".

Note however that in this instance, the Java interpreter only searches the folder named JavaProg for the specified class files. It does not also search the folder named Baldwin along the way. If I store the class files in the folder named Baldwin, I can only execute them when Baldwin is the current directory.

If the class files were compiled with a package directive, this does not hold and you will need to read further.

Developing Your Own Packages

The Package Directive
The Import Directive
Compiling Programs with the Package Directive
Sample Program

One of the problems with storing all of your class files in one or two folders is that you will likely experience name conflicts between class files.

Every Java program can consist of a large number of separate classes. A class file is created for each class that is defined in your program, even if they are all combined into a single source file.

It doesn't take very many programs to create a lot of class files, and it isn't long before you find yourself using the same class names over again. If you do, you will end up overwriting class files that were previously stored in the folder.

For me, it only takes two GUI programs to get in trouble because I tend to use the same class names in every program for certain standard operations such as closing a Frame or processing an ActionEvent. For the case of the ActionEvent, the body of the class varies from one application to the next so it doesn't make sense to turn it into a library class.

What it all comes down to is that as a practical matter, you probably need to store the class files for each separate Java application in a separate folder. It simply isn't practical for either you or your client to keep adding folders to the classpath settings.

Aside from the hassle involved in frequently changing the classpath settings, there is also a limit on the amount of information that can be stored in the environment space on DOS/Win95 systems. If you exceed this limit, and you are lucky, you will see a message flash across your screen when you boot your computer telling you that you are out of environment space. But, who watches the screen while the computer is booting?

Other than that message, the only indication that you have exhausted your environment space is that some of your programs will quit working properly, and those may not be Java programs. Your word processor may quit working because you exhausted your environment space during bootup while setting your Java classpath.

So we need a better way to organize our class files, and the Java package provides that better way.

The Java package approach allows us to store our class files in a hierarchy of folders (or a zip file that represents that hierarchy) while only requiring that the classpath variable point to the top of the hierarchy. The remaining hierarchy structure is encoded into our programs using package directives and import directives.

Now here is a little jewel of information that cost me about seven hours of effort to discover when I needed it badly.

When I first started developing my own packages, I spent about seven hours trying to determine why the compiler wouldn't recognize the top-level folder in my hierarchy of package folders.

I consulted numerous books by respected authors and none of them was any help at all. I finally found the following statement in the JavaSoft documentation (when all else fails, read the documentation). By the way, a good portion of that wasted seven hours was spent trying to find this information in the JavaSoft documentation which is voluminous.

The following text was extracted directly from the JavaSoft JDK 1.1 documentation.

If you want the CLASSPATH to point to class files that belong to a package, you should specify a path name that includes the path to the directory one level above the directory having the name of your package.

For example, suppose you want the Java interpreter to be able to find classes in the package mypackage. If the path to the mypackage directory is C:\java\MyClasses\mypackage, you would set the CLASSPATH variable as follows:

set CLASSPATH=C:\java\MyClasses

If you didn't catch the significance of this, read it again. When you are creating a classpath variable to point to a folder containing classes, it must point to the folder. However, when you are creating a classpath variable to point to your package, it must point to the folder that is one level above the directory that is the top-level folder in your package.

Once I learned that I had to cause the classpath to point to the folder immediately above the first folder in the hierarchy that I was including in my package directives, everything started working.

The Package Directive

So, what is the purpose of a package directive, and what does it look like.

The purpose of the package directive is to identify a particular class (or group of classes contained in a single source file (compilation unit)) as belonging to a specific package.

This is very important information for a variety of reasons, not the least of which is the fact that the entire access control system is wrapped around the concept of a class belonging to a specific package. For example, code in one package can only access public classes in a different package. (The next lesson will discuss access control in more detail).

Stated in the simplest possible terms, a package is a group of class files contained in a single folder on your hard drive.

At compile time, a class can be identified as being part of a particular package by providing a package directive at the beginning of the source code..

A package directive, if it exists, must occur at the beginning of the source code (ignoring comments and white space). No text other than comments and whitespace is allowed ahead of the package directive.

If your source code file does not contain a package directive, the classes in the source code file become part of the default package. With the exception of the default package, all packages have names, and those names are the same as the names of the folders that contain the packages. There is a one-to-one correspondence between folder names and package names. The default package doesn't have a name.

Some examples of package directives that you will see in upcoming sample programs follow:

package Combined.Java.p1; 
package Combined.Java.p2;

Given the following as the classpath on my machine,

CLASSPATH=.;c:\Baldwin\JavaProg

these two package directives indicate that the class files being governed by the package directives are contained in the following folders:

c:\Baldwin\JavaProg\Combined\Java\p1 
c:\Baldwin\JavaProg\Combined\Java\p2

Notice how I concatenated the package directive to the classpath setting and substituted the backslash character for the period when converting from the package directive to the fully-qualified path name.

Code in one package can refer to a class in another package (if it is accessible) by qualifying the class name with its package name as follows:

Combined.Java.p2.Access02 obj02 = 
  new Combined.Java.p2.Access02();

Obviously, if we had to do very much of this, it would become very burdensome. As you already know, the import directive is available to allow us to specify the package containing the class just once at the beginning of the source file and then refer only to the class name for the remainder of that source file.

The Import Directive

This discussion will be very brief because you have been using import directives since the very first lesson. Therefore, you already know what they look like and how to use them.

If you are interested in the nitty-gritty details (such as what happens when you provide two import directives that point to two different packages containing the same class file name), you can consult the Java Language Reference by Mark Grand, or you can download the Java language specification from the JavaSoft site.

The purpose of the import directive is to help us avoid the burdensome typing requirement described in the previous section when referring to classes in a different package.

An import directive makes the definitions of classes from other packages available on the basis of their file names alone.

You can have any number of import directives in a source file. However, they must occur after the package directive (if there is one) and before any class or interface declarations.

There are essentially two different forms of the import directive, one with and the other without a wild card character. These two forms are illustrated in the following box.

import java.awt.* 
import java.awt.event.ActionEvent 

The first import directive makes all of the class files in the java.awtpackage available for use in the code in a different package by referring only to their file names.

The second import directive makes only the class file named ActionEvent in the java.awt.event package available on that basis.

Compiling Programs with Package Directives

So, how do you compile programs containing package directives? There are probably several ways. I am going to describe the way that I have found to be successful.

First, you must create your folder hierarchy to match the package directive that you intend to use. Remember to construct this hierarchy downward relative to the folder specified at the end of your classpath setting. If you have forgotten the critical rule in this respect, go back and review it.

Next, place source code files in each of the folders where you intend for the class files associated with those source code files to reside. (After you have compiled and tested the program, you can remove the source code files if you wish.)

You can compile the source code files individually if you want to, but that isn't necessary.

One of the source code files will contain the controlling class. To me, the controlling class is that class that contains the main() method that will be executed when you run the program from the command line using the Java interpreter.

Make the directory containing that source code file be the current directory. If you don't know what the current directory is, go out and get yourself a DOS For Dummies book and read it.

Each of the source code files must contain a package directive that specifies the package that will contain the compiled versions of all the class definitions in that source code file. Using the instructions that I am giving you, that package directive will also describe the folder that contains the source code file.

Any of the source code files containing code that refers to classes in a different package must also contain the appropriate import directives, or you must use fully-qualified package names to refer to those classes.

Then use the javac program with your favorite options to compile the source code file containing the controlling class. This will cause all of the other source code files containing classes that are linked to the code in the controlling class, either directly or indirectly, to be compiled also. At least an attempt will be made to compile them all. You may experience a few compiler errors if your first attempt at compilation is anything like mine.

Once you eliminate all of the compiler errors, you can test the application by using the java program with your favorite options to execute the controlling class.

Once you are satisfied that everything works properly, you can copy the source code files over to an archive folder and remove them from the package folders if you want to do so.

Finally, you can also convert the entire hierarchy of package folders to a zip file if you want to, and distribute it to your client. If you don't remember how to install it relative to the classpath variable, go back and review that part of the lesson.

Once you have reached this point, how do you execute the program. Well, there are probably several different ways. However, you must remember that the controlling class may not be on the classpath. Rather, it may be down in a folder somewhere in your package folder hierarchy. Therefore, simply entering its name following java at the DOS prompt won't do the job, unless you first cause the folder containing the controlling class file to be the current directory.

I do it with batch files and icons on my desktop. In particular, I create a batch file that:

In these days of GUI, I don't often use command-line parameters following the name of the controlling class, but if I need to do so, I can use the %1, %2 feature of batch files to allow me to enter the command line parameters following the name of the batch file (again if you don't know what this means, and if you care, see DOS For Dummies).

If command-line parameters are not an issue, I create an icon for the batch file and place it on my desktop. Then I can execute the application simply by double-clicking on that icon.

Sample Program

Let's take a look at a simple program that is designed to pull all of this together.

NOTICE: The following is part of the text of a message that was received from a reader on 3/19/98. My thanks go out to this reader for providing this feedback regarding changes in the JVM. Since I haven't had time to make the necessary correction, I am providing the text of this message for your information.
=========================
First, let me introduce myself. My name is Jean-Marc Tisserand...

I would like to inform you that I found a problem while running the example described in lesson #38 (Packages) with jdk v1.1.5. I'm unable to run Package00. It also failed with a message stating that the
Package00 class was not found.

I spent a lot of time and read again carefully the lesson. But it didn't help.

After a while, I discover the following lines in the jdk v1.1.5 release notes:

java INTERPRETER BUG - In previous releases of JDK 1.1, the java interpreter would allow the class file at /a/b/c.class to be invoked from within the /a/b directory by the command "java c", even if the class c was in package a.b.*. This behavior is incorrect and has been fixed in JDK 1.1.4. In JDK 1.1.4, the fully qualified class name must be specified. For example, to invoke the class a.b.c at /a/b/c.class, the command "java a.b.c" could be issued from the parent directory of directory /a.

So to run the Package00 program, the command is: java Combined.Java.Package00.

This command must be started while the current directory is one level up the Combined directory.

This example is working very well now on a Win95 PC with jdk v1.1.5 and on a Digital Unix system with jdk v1.1.5.

This application consists of three separate source files located in three different packages. Together they illustrates the use of package and import directives, along with javac to build a standalone Java application consisting of classes in three separate packages.

(If you want to confirm that they are really in different packages, just make one of the classes referred to by the controlling class non-public and try to compile the program.)

In other words, in this sample program, we create our own package structure and populate it with a set of cooperating class files.

The controlling class named Package00 is stored in the package named Combined.Java.

The class named Package01 is stored in the package named Combined.Java.p1.

The class named Package02 is stored in the package named Combined.Java.p2.

Code in the main() method of the controlling class instantiates objects of the other two classes in different packages. Code in the constructors for these two classes announces that they are being constructed.

The two classes being instantiated are public. Otherwise, it would not be possible to instantiate them from outside their respective packages.

This program was tested using JDK 1.1.3 under Win95

The output from running the program is shown in the comments at the beginning.

/*File Package00.java Copyright 1997, R.G.Baldwin
Illustrates use  of package and import directives to 
build an application consisting of classes in three
separate packages.

The output from running the program follows:
  
Starting Package00
Instantiate obj of public classes in different packages
Constructing Package01 object in folder p1
Constructing Package02 object in folder p2
Back in main of Package00  
**********************************************************/
package Combined.Java; //package directive

//Two import directives
import Combined.Java.p1.Package01;//specific form
import Combined.Java.p2.*; //wildcard form

class Package00{
  public static void main(String[] args){ //main method
    System.out.println("Starting Package00");
    
    System.out.println("Instantiate obj of public " +
                         "classes in different packages");
    new Package01();//Instantiate objects of two classes
    new Package02();// in different packages.

    System.out.println("Back in main of Package00");
    
  }//end main method
}//End Package00 class definition.

.

/*File Package01.java Copyright 1997, R.G.Baldwin
See discussion in file Package00.java
**********************************************************/
package Combined.Java.p1;
public class Package01 {
  public Package01(){//constructor
    System.out.println(
             "Constructing Package01 object in folder p1");
  }//end constructor
}//End Package01 class definition.

.

/*File Package02.java Copyright 1997, R.G.Baldwin
See discussion in file Package00.java
**********************************************************/
package Combined.Java.p2;
public class Package02 {
  public Package02(){//constructor
    System.out.println(
             "Constructing Package02 object in folder p2");
  }//end constructor
}//End Package02 class definition.

-end-