Java 101: Packages organize classes and interfaces

Create packages, import packaged classes and interfaces into your programs, move packages, and encapsulate them in jar files

Why reinvent the wheel? That cliche applies to software development where some developers frequently rewrite the same code for different programs. Two disadvantages with that approach are:

  1. It wastes time
  2. It introduces the potential for bugs in debugged code

As an alternative to rewriting the same code, many software development environments provide a library tool that organizes frequently used code. Once developers finish debugging some reusable code, they use the tool to store that code in a library—one or more files that contain frequently used code for use in various programs. During program-building, the compiler or library tool accesses the library to connect the program's library-referenced code to the program.

Libraries are fundamental to Java. They allow, in part, the JVM's classloader to locate classfiles. (I will explore classloaders in a future article.) For that reason, Java's libraries are commonly known as class libraries. However, Java refers to class libraries as packages.

This article explores packages; I show you how to create packages of classes and interfaces, how to import (that is, bring into a program) packaged classes and interfaces, how to move packages on the hard drive, and how to use jar files to encapsulate packages.

Note
This article's single package experiment is Microsoft Windows-specific. You should be able to easily extrapolate this experiment to non-Windows platforms.

What are packages?

A package is a collection of classes and interfaces. Each package has its own name and organizes its top-level (that is, nonnested) classes and interfaces into a separate namespace, or name collection. Although same-named classes and interfaces cannot appear in the same package, they can appear in different packages because a separate namespace assigns to each package.

From an implementation perspective, equating a package with a directory proves helpful, as does equating a package's classes and interfaces with a directory's classfiles. Keep in mind other approaches—such as the use of databases—to implementing packages, so do not get into the habit of always equating packages with directories. But because many JVMs use directories to implement packages, this article equates packages with directories. The Java 2 SDK organizes its vast collection of classes and interfaces into a tree-like hierarchy of packages within packages, which is equivalent to directories within directories. That hierarchy allows Sun Microsystems to easily distribute (and you to easily work with) those classes and interfaces. Examples of Java's packages include:

  • java.lang: A collection of language-related classes, such as Object and String, organized in the java package's lang subpackage
  • java.lang.ref: A collection of reference-related language classes, such as SoftReference and ReferenceQueue, organized in the ref sub-subpackage of the java package's lang subpackage
  • javax.swing: A collection of Swing-related component classes, such as JButton, and interfaces, such as ButtonModel, organized in the javax package's swing subpackage

Period characters separate package names. For example, in javax.swing, a period character separates package name javax from subpackage name swing. A period character is the platform-independent equivalent of forward slash characters (/), backslash characters (\), or other characters to separate directory names in a directory-based package implementation, database branches in a hierarchical database-based package implementation, and so on.

Tip
Just as you cannot store both a file and a directory with identical names in the same directory, you cannot store a class or interface and a package with identical names in the same package. For example, given a package named accounts, you cannot store both a package and a class named payable in accounts. To avoid conflicting names, uppercase the first letter of class and interface names, and lowercase the first letter of package names. Using the previous example, store class Payable in package accounts as accounts.Payable and package payable in package accounts as accounts.payable. Learn more about this and other naming conventions from Sun's Code Conventions for the Java Programming Language.

Create a package of classes and interfaces

Every source file's classes and interfaces organize into a package. In the package directive's absence, those classes and interfaces belong to the unnamed package (the directory the JVM regards as the current directory—the directory where a Java program begins its execution via the Windows java.exe, or OS-equivalent, program—and contains no subpackages). But if the package directive appears in a source file, that directive names the package for those classes and interfaces. Use the following syntax to specify a package directive in source code:

'package' packageName [ '.' subpackageName ... ] ';'

A package directive begins with the package keyword. An identifier that names a package, packageName, immediately follows. If classes and interfaces are to appear in a subpackage (at some level) within packageName, one or more period-separated subpackageName identifiers appear after packageName. The following code fragment presents a pair of package directives:

package game;
package game.devices;

The first package directive identifies a package named game. All classes and interfaces appearing in that directive's source file organize in the game package. The second package directive identifies a subpackage named devices, which resides in a package named game. All classes and interfaces appearing in that directive's source file organize in the game package's devices subpackage. If a JVM implementation maps package names to directory names, game.devices maps to a game\devices directory hierarchy under Windows and a game/devices directory hierarchy under Linux or Solaris.

Caution
Only one package directive can appear in a source file. Furthermore, the package directive must be the first code (apart from comments) in that file. Violating either rule causes Java's compiler to report an error.

To help you get comfortable with packages, I've prepared an example that spans all topics in this article. In this section, you learn how to create the example's package. In later sections, you will learn how to import a class and an interface from this package, how to move this package to another location on your hard drive and still access the package from a program, and how to store the package in a jar file. Listing 1 presents the package's source code:

Listing 1. A.java

// A.java
package testpkg;
public class A
{
   int x = 1;
   public int y = 2;
   protected int z = 3;
   int returnx ()
   {
      return x;
   }
   public int returny ()
   {
      return y;
   }
   protected int returnz ()
   {
      return z;
   }
   public interface StartStop
   {
      void start ();
      void stop ();
   }
}
class B
{
   public static void hello ()
   {
      System.out.println ("hello");
   }
}

Listing 1 introduces the source code to your first named package. The package testpkg; directive names that package testpkg. Within testpkg are classes A and B. Within A are three field declarations, three method declarations, and an inner interface declaration. Within B is a single method declaration. The entire source code stores in A.java because A is a public class. Our task: Turn this source code into a package that consists of two classes and an inner interface (or a directory that contains three classfiles). The following Windows-specific steps accomplish that task:

  1. Open a Windows command window and ensure you are in the c: drive's root directory (the main directory—represented by an initial backslash (\) character). To do that, type the c: command followed by the cd \ command. (If you use a different drive, replace c: with your chosen drive. Also, do not forget to press the Enter key after typing a command.)
  2. Create a testpkg directory by typing md testpkg. Note: When following this article's steps, do not type periods after the commands.
  3. Make testpkg the current directory by typing cd testpkg.
  4. Use an editor to enter Listing 1's source code and save that code to an A.java file in testpkg.
  5. Compile A.java by typing javac A.java. You should see classfiles A$StartStop.class, A.class, and B.class appear in the testpkg directory.

Figure 1 illustrates Steps 3 through 5.

Figure 1. The testpkg directory with its three classfiles equates to a testpkg package with classes A and B, and A's inner interface StartStop

Congratulations! You have just created your first package. Think of this package as containing two classes (A and B) and A's single inner interface (StartStop). You can also think of this package as a directory containing three classfiles: A$StartStop.class, A.class, and B.class.

Note
To minimize package name conflicts (especially among commercial packages), Sun has established a convention in which a company's Internet domain name reverses and prefixes a package name. For example, a company with x.com as its Internet domain name and a.b as a package name (a) followed by a subpackage name (b) prefixes com.x to a.b, resulting in com.x.a.b. My article does not follow this convention because the testpkg package is a throw-away designed for teaching purposes only.

Import a package's classes and interfaces

Once you have a package, you will want to import classes and/or interfaces—actually, class and/or interface names—from that package to your program, so it can use those classes and/or interfaces. One way to accomplish that task is to supply the fully qualified package name (the package name and all subpackage names) in each place where the reference type name (the class or interface name) appears, as Listing 2 demonstrates:

Listing 2. Usetestpkg1.java

// Usetestpkg1.java
class Usetestpkg1 implements testpkg.A.StartStop
{
   public static void main (String [] args)
   {
      testpkg.A a = new testpkg.A ();
      System.out.println (a.y);
      System.out.println (a.returny ());
      Usetestpkg1 utp = new Usetestpkg1 ();
      utp.start ();
      utp.stop ();
   }
   public void start ()
   {
      System.out.println ("Start");
   }
   public void stop ()
   {
      System.out.println ("Stop");
   }
}

By prefixing testpkg. to A, Usetestpkg1 accesses testpkg's class A in two places and A's inner interface StartStop in one place. Complete the following steps to compile and run Usetestpkg1:

  1. Open a Windows command window and make sure you are in the c: drive's root directory.
  2. Ensure the classpath environment variable does not exist by executing set classpath=. (I discuss classpath later in this article.)
  3. Use an editor to enter Listing 2's source code and save that code to a Usetestpkg1.java file in the root directory.
  4. Compile Usetestpkg1.java by typing javac Usetestpkg1.java. You should see classfile Usetestpkg1.class appear in the root directory.
  5. Type java Usetestpkg1 to run this program.

Figure 2 illustrates Steps 3 through 5 and shows the program's output.

Figure 2. The root directory contains Usetestpkg1.class and the testpkg directory that corresponds to the testpkg package

According to Usetestpkg1's output, the main() method's thread successfully accesses testpkg.A's y field and calls the returny() method. Furthermore, the output shows a successful implementation of the testpkg.A.StartStop inner interface.

For Usetestpkg1, prefixing testpkg. to A in three places doesn't seem a big deal. But who wants to specify a fully qualified package name prefix in a hundred places? Fortunately, Java supplies the import directive to import a package's public reference type name(s), so you do not have to enter fully qualified package name prefixes. Express an import directive in source code via the following syntax:

'import' packageName [ '.' subpackageName ... ] '.' ( referencetypeName | '*' ) ';'

An import directive consists of the import keyword immediately followed by an identifier that names a package, packageName. An optional list of subpackageName identifiers follows to identify the appropriate subpackage (if necessary). The directive concludes with either a referencetypeName identifier that identifies a specific class or interface from the package, or an asterisk (*) character. If referencetypeName appears, the directive is a single-type import directive. If an asterisk character appears, the directive is a type-on-demand import directive.

Caution
As with the package directive, import directives must appear before any other code, with three exceptions: a package directive, other import directives, or comments.

The single-type import directive imports the name of a single public reference type from a package, as the following code fragment demonstrates:

import java.util.Date;

The previous single-type import directive imports class name Date into source code. As a result, you specify Date instead of java.util.Date in each place that class name appears in source code. For example, when creating a Date object, specify Date d = new Date (); instead of java.util.Date d = new java.util.Date ();.

Exercise care with single-type import directives. If the compiler detects a single-type import directive that specifies a reference type name also declared in a source file, the compiler reports an error, as the following code fragment demonstrates:

import java.util.Date;
class Date {}

The compiler regards the code fragment as an attempt to introduce two reference types with the same Date name:

1 2 3 Page 1
Page 1 of 3