Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

import java.*


September 1999/import java.*

Java packages combine the properties of C/C++ include directories, namespaces, make files, and libraries — often to advantage.


In my July 1999 installment I introduced the four access specifiers for class members in Java, which are as follows, listed in decreasing amount of visibility:

1. Public access, denoted by the keyword public. Public members can be accessed anywhere.

2. Protected access, denoted by the keyword protected. Protected members are accessible in derived-class methods and in methods in the same package.

3. Package access, denoted by the absence of any accompanying keyword. Members with package access are visible anywhere in the package in which they appear.

4. Private access, denoted by the keyword private. As in C++, private members are visible only within their class. There is no concept of friends in Java. Instead of breaking encapsulation you use package access.

Note that items 2-4 mention the word "package." Packages are the fundamental organizational unit of Java applications. Like namespaces in C++, packages allow you to group related types together and use them in an application without name conflicts. In fact, the similarities between Java packages and C++ namespaces are quite striking (see Table 1). The following program from the March 1999 installment of this column shows how to use the Vector class from the java.util package:

import java.util.*;

class UseVector {
   public static void main
      (String[] args) {

      Vector v = new Vector();
      for (int i = 0; i < 10; ++i)
        v.addElement(new Integer(i));
      for (int i = 0;
           i < v.size();
           ++i)
        System.out.print
           (v.elementAt(i) + " ");
    }
}

/* Output:
0 1 2 3 4 5 6 7 8 9
*/

The import statement above is analogous to the C++ using directive:

using namespace java::util;

Like a using directive, importing a package with the ".*" suffix opens a package for name lookup, so when the javac translator can't find a name in the current directory, it looks in the package java.util to find it. If you happen to have a Vector class in the current directory, however, the translator will find that first, and won't even bother with java.util. If you still want to bypass your local Vector and use the one from java.util, you have two options. You can change the import statement to explicitly associate the name Vector with java.util.Vector, as follows:

import java.util.Vector;

which is analogous to the following C++ using declaration:

using java::util::Vector;

or, you can use a fully qualified class name and ignore the import altogether:

// Note: no import statement
class UseVector {
   public static void main
      (String[] args) {

      java.util.Vector v =
         new java.util.Vector();
      (etc.)

The differences between C++ namespaces and Java packages are more than merely syntactic, however. Java package storage conventions dictate a scheme that radically simplifies building and deploying applications.

Creating a Package

A package consists of one or more .class files that occupy a single directory. The Java source for each class must appear in a file that contains a package statement as its first non-comment statement, and the name of the package must be identical to its directory name. Suppose, for example, that you want to create a package named MyPackage that contains two classes, MyClass and MyOtherClass. If you want both classes to be accessible to clients, then each class must be declared as public in its own file of the same name, in a directory named MyPackage. The MyPackage directory can be a subdirectory of any other directory, however. For this example, place the files MyClass.java and MyOtherClass.java in c:\MyPackage, as follows:

// c:\MyPackage\MyClass.java
package MyPackage;

public class MyClass {
   public static void announce() {
      System.out.println
         ("c:\\MyPackage\\MyClass");
   }
}

// c:\MyPackage\MyOtherClass.java
package MyPackage;

public class MyOtherClass {
   public static void announce() {
      System.out.println
         ("c:\\MyPackage\\MyOtherClass");
    }
}

The package statement indicates that each source file contains type definitions that belong to MyPackage. At most one class in each file can be public; if other classes appear, they are not callable by clients of the package.

Although this scheme might appear a little restrictive at first, it actually simplifies program construction. Go to any other directory now, and build the following test file:

//<any other dir.>\MyPackageTest.java
import MyPackage.*;

class MyPackageTest {
   public static void main
      (String[] args) {

      MyClass.announce();
      MyOtherClass.announce();
   }
}

Now enter the following command:

javac -verbose -classpath c:\;. MyPackageTest.java

Among other things, you should see something like the following:

[checking class MyPackageTest]
...
[wrote MyPackageTest.class]
[checking class MyPackage.MyClass]
...
[wrote c:\MyPackage\MyClass.class]
[checking class MyPackage.MyOtherClass]
[wrote c:\MyPackage\MyOtherClass.class]

The -verbose option flag causes javac to report all the classes it loads. The -classpath option tells javac where to look for non-system packages. In this case it will first look in c:\ and then in the current directory. Since javac finds the directory c:\MyPackage, it searches there for MyClass.class and MyOtherClass.class. Since they don't yet exist, it finds the corresponding .java files and compiles them automatically.

To run the program, you must enter:

java -classpath c:\;. MyPackageTest

and the output is:

c:\MyPackage\MyClass
c:\MyPackage\MyOtherClass

The current directory (".") must always be in the classpath when you execute or the runtime system won't be able to legally load the starting class's main method. (You actually could have left it off in the compile step.) Since it can be tedious to always include the -classpath option on the command line, you can define the CLASSPATH environment variable instead:

C:>set CLASSPATH c:\;.

The -classpath option, if present, overrides the CLASSPATH environment variable. If you don't define any classpath at all, the system behaves as if you had defined it as ".", making the current directory the classpath. You can determine the classpath at runtime by calling System.getpath("java.class.path").

The classpath mechanism also has the handy feature of acting as a mini-make utility. If you make changes to either of the source files in c:\MyPackage, they are automatically recompiled when you recompile MyPackageTest.java.

Since the runtime environment searches the classpath in order, if you have a class named MyPackage in the current directory, and another class named MyPackage in a subdirectory of the same name, the runtime system will still find the package in c:\Mypackage first. But if you switch the directories in the classpath to be ".;c:\", then the local Mypackage is found. This way you can use the classpath as a cheap versioning tool by specifying different directories as needed [1].

But be careful! If you place the files MyClass.java and MyOtherClass.java in the current directory or the root directory, the compiler will find them, but complain with the following error, because they are not in a subdirectory named MyPackage:

error: file ./MyClass.java does not contain the type MyClass as expected. Please adjust the classpath so that the file does not appear in the unnamed package.

Remember: a package name must always match the name of the subdirectory that contains the corresponding class files. I explain the unnamed package in the section "The Default Package" below.

Avoiding Name Collisions

Since packages form a container for names, as it were, (hence the C++ term "namespace"), you rarely have to worry about name collisions in programs. When you import two packages with the ".*" suffix that contain the same name, you encounter a problem only if you try to use that name, in which case you can use a fully-qualified name to resolve the ambiguity. But you still have to worry about collisions of package names! Ideally we would have some centralized arbitrator with whom we could register package names, so we would never have to worry about conflicting with someone else's when distributing our code. But who wants to bother with the complexity and delay of such a scheme? Certainly not your everyday developer!

Fortunately, there is a de facto clearing house in place already, which is trivial to access for anyone who belongs to an organization that has an Internet domain. When you register your domain, Internic, the Internet domain name authority, forces you to choose a name that is currently not in use. My domain name, for example, is freshsources.com. This easily maps to a directory path name, and since the .com is the more general component, it maps nicely to the directory \com\freshsources. Since no one else in the world has this domain, I can install my packages there on any machine and be assured that no other packages reside there (provided that everyone else follows these rules). To successfully move MyPackage to com\freshsources\MyPackage, I also have to rewrite the package statement in each file as follows:

// Package name must match
// directory name
package com.freshsources.MyPackage;

and the import statement in the client program must also match:

import com.freshsources.MyPackage.*;

Don't forget to keep the parent directory in the classpath or Java won't find your package.

The Default Package

The types defined in a Java source file that does not have a package statement belong to the unnamed package (or as I and some others prefer to call it, the default package). While you usually think of the default package as the current directory, any .class files in a classpath directory are also part of the default package. To see how this works, place the following file in the root directory:

// c:\FindMe.java
class FindMe {
   static void foo() {
      System.out.println
         ("Found Me!");
    }
}

Now go to any other directory and create this file:

// <any other dir.>\Search.java
class Search {
   public static void main
      (String[] args) {
      FindMe.foo();
   }
}

Neither of these files belong to a named package, so they will belong to the default package if their respective directories are in the classpath. To compile, enter the following from the current directory (where you defined Search.java):

javac -classpath c:\ Search.java

This tells the compiler that it can find FindMe.class (or FindMe.java, if necessary) in "c:\". To load and execute Search.class, enter:

java -classpath c:\;. Search

For this invocation of the Java virtual machine the default package includes "c:\" and ".", so FindMe.class loads successfully and the output is "Found me!", as expected.

Package Access

Note that neither Search nor FindMe were declared as public, nor was FindMe.foo, and yet I was able to access FindMe.foo from within Search. Why? Because the two classes are in the same package. Any definition lacking an explicit access specifier has package access, which means that it is visible in any method within its package. Since classes such as Search and FindMe keep no secrets from each other, they are said to be "friendly." This arrangement explains why the friend keyword is not necessary in Java — if you need to break traditional encapsulation boundaries, put the related classes in the same package.

The C++ program in Listing 1 illustrates how a controlled resource can select its clients. Since the Resource class has a private constructor, Resource objects can be allocated only in methods of the Resource class itself, or in friend methods. To achieve the same thing in Java, simply put Consumer and Resource in the same package and omit any access specifiers for Resource, as I did in Listing 2. The two classes don't have to be in the same .java file, of course, nor even in the same directory — just in the same package.

There is an important distinction between friends in C++ and friendly classes in Java. In addition to being more fine-grained (i.e., allowing you to grant friendship on a member function basis as well as a class basis), C++ friendship is a one-way proposition — for example, Resource has no access to members of the Consumer class as it does in Java, although its methods could access any friendly methods Consumer might have. This is not a problem, of course — you can simply use the private keyword as needed.

In previous installments of this column I've only used the default package in the current directory. I've also adhered to the policy of having exactly one public class per file, with the same name as the file. I did this to illustrate the connection between file names and public class names in Java, and to encourage habits that will make the transition to using named packages as smooth as possible. But there is a good reason not to make classes in the default package public. Actually, there are two reasons:

1. Because it's not necessary! Package access is all you need in the default package, and it's always a good idea to minimize the amount of visibility you give a class.

2. To minimize name resolution conflicts.

To illustrate the second point, recall the error that occurred above when I put MyClass and MyOtherClass in the root directory. Since they weren't in a directory appropriately named for their package, there was a problem. Suppose for the moment that from some previous experimentation I had placed MyClass and MyOtherClass in the root directory but in the default package. As long as I give them only package access, the compiler will bypass them on its way to find the class files in c:\MyPackage. It's only when the classes are public that you have a problem. So, from now on, I will no longer give classes in the default package public access, and neither should you.

JAR Files

So far it looks like that the only way you can access a package is to search a directory with the appropriate name. This is okay for local applications, but what about executing applets on the web? It turns out that a considerable amount of overhead incurs from establishing an HTTP connection to a host. If you have need for several class files for an applet to run, your browser will likely spend more time just opening and closing connections than in downloading code. Although I won't discuss applets in this article (nor for a long time to come), I'll discuss the solution to this inconvenience: Java Archive files, or JAR files. JAR files are cross-platform archives that can hold an arbitrary number of files, and are used mostly to store .class files and auxiliary files such as image and audio files. The java tools understand JAR file format and automatically extract what they need. To create a JAR file, use the jar command, which is reminiscent of the Unix tar command, and the following options as needed:

c        create jar file
t        list table of contents
x        extract files
u        update
v        verbose output
f        use file instead of standard
         input/output
m        read manifest file
0        (the numeral zero) turn off
         compression (for faster
         loading)
M        skip manifest
C        change directory (don't
         include path names in output)

To create a JAR file for MyPackage files in c:\MyPackage, you would execute the following command from c:\:

jar cvf MyPackage.jar MyPackage\*.class

The output would be:

added manifest
adding: MyPackage/MyClass.class (in=417) (out=288) (deflated 30%)
adding: MyPackage/MyOtherClass.class (in=432) (out=294) (deflated 31%)

As you can see, the jar command compresses files by default. (It uses the popular ZIP compression format). Now that MyPackage is in the root directory, return to the directory containing MyPackageTest.java and execute the following command:

javac -classpath .;c:\MyPackage.jar MyPackageTest.java

Because the jar file is in the classpath, the compiler searches it and finds MyPackage.MyClass and MyPackage.MyOtherClass. It's not enough to just list the directory where the jar file resides; you must place the full path of the JAR file in the classpath. Since the package-to-directory name associations still apply, it's important that the .class files in the JAR file contain the prefix MyPackage/, which is why I created it from the root directory. It is equally important that the JAR file reside in a classpath directory.

You can even run whole applications from a JAR file. All you need to do is store the name of the starting class in the JAR file's manifest. The manifest is a special JAR file entry that contains meta-information. If you list the JAR file's table of contents, you can see evidence that the manifest exists in the first two lines of output:

jar tf MyPackage.jar

/* output:
META-INF
META-INF/MANIFEST.MF
MyPackage/MyClass.class
MyPackage/MyOtherClass.class
*/

The manifest resides in a pseudo-directory inside the JAR file named META-INF in the file MANIFEST.MF. To add the starting class to the manifest you use the "m" option. To illustrate, let's just put a "Hello, world" program in a JAR file. First create the source, compile it, and then archive it, as follows:

// Hello.java
class Hello {
   public static void main (String[] args) {
      System.out.println
         ("Hello, world");
   }
}

javac Hello.java
jar -cf Hello.jar Hello.class

To enter the Hello class as the main class for the JAR file, create a text file of any name (manifest.txt, say) that contains the following line:

Main-class: Hello

To update the manifest, use this jar command:

jar umf manifest.txt Hello.jar

Alternatively, you could have entered the manifest entry when you created the JAR file in the first place, as in (broken here to fit the column):

jar cmf manifest.txt Hello.jar
   Hello.class

Now you can run the JAR file as follows [2]:

java -jar Hello.jar

To read more on JAR files, visit http://java.sun.com/docs/books/tutorial/jar/basics/.

Standard Packages

The standard Java library comes in packages, such as java.lang, java.util, java.io, etc. Since packages must have associated directories, you might expect your installation to have subdirectories such as java\lang, java\util, and java\io. That would work fine, but why bother when the Java tools can read JAR files? If you have a Java 2 JDK installation, you'll find the library packages in \jdk1.2\jre\lib\rt.jar, as the following jar command reveals:

jar tf \jdk1.2\jre\lib\rt.jar

/* Output:
META-INF/
META-INF/MANIFEST.MF
com/
com/sun/
com/sun/CORBA/
com/sun/CORBA/ClientGIOP.class
com/sun/CORBA/ClientRequest.class
etc.
*/

Inspect the full output and you'll see all your favorites, like java/lang/System.class and java/util/Vector.class.

But how do the compiler and runtime system find rt.jar if it's not in the classpath? Starting in Java 2, the system path is searched separately, before the classpath. (And as you know, java.lang is automatically imported.) In previous versions of Java, the installation process created a default classpath that referred to the library archive, such as (broken here to fit the column):


CLASSPATH=.;c:\jdk1.1.7\lib\
   classes.zip

(Yes, Java can read ZIP files too!)

Summary

The tools and conventions of Java give you a lot of help in organizing your applications. Packages, like namespaces in C++, allow you to group related types together and provide a layering mechanism to effectively manage name lookup. Packages go beyond what C++ offers, however, in facilitating automatic program construction and delivery.

Notes

[1] While this works as advertised when executing Java programs, when compiling, things are not so simple. You must be sure to delete all imported .class files from a previous compile for javac to find the new files when you change the classpath.

[2] This syntax is new to Java 2. In Java 1.1 you used the jre command, as in jre -classpath Hello.jar Hello. The manifest is new in Java 2.

Chuck Allison is Consulting Editor and a columnist with CUJ. He is the owner of Fresh Sources, a company specializing in object-oriented software development, training, and mentoring. He has been a contributing member of J16, the C++ Standards Committee, since 1991, and is the author of C and C++ Code Capsules: A Guide for Practitioners, Prentice-Hall, 1998. You can email Chuck at [email protected].


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.