X

Maximize your Oracle Technology Investment with
Quality Oracle Training & Certification.
Learn Oracle from Oracle

  • October 30, 2017

Understanding Java Packages

Breanne Wilson
Oracle University SEO & Content Strategist

Written by Jeff Friesen, Java technology expert

Java developers group related classes and other reference types into packages.

This Java language construct makes it easier to locate and use types, avoid name conflicts between same-named types, and control access to types.

This article introduces you to packages.

  • You discover what packages are.
  • Learn about the package and import statements.
  • Explore the additional package-oriented topics of protected access and type searches.

What is a Java package and how is it used?

Hierarchical structures organize items according to hierarchical relationships that exist between those items.

For example, a file cabinet organizes drawers of file folders containing files (pieces of paper). A file is a member of a file folder, which is a member of a drawer that is a member of a file cabinet.

Also, a file system might contain an accounts directory with payable and receivable subdirectories. Additionally, a class can declare nested classes along with fields and methods.

Hierarchical structures help to avoid name conflicts.

For example, in a nonhierarchical file system (a single directory), it's not possible to assign the same name to multiple files.

In contrast, a hierarchical file system lets same-named files exist in different directories. Similarly, two enclosing classes can contain same-named nested classes. Name conflicts don't exist because items are partitioned into different namespaces.

Java also supports the partitioning of top-level (non-nested) reference types into multiple namespaces to better organize these types and to prevent name conflicts.

It accomplishes this task via its package language feature, where a package is a unique namespace for storing reference types.

Packages can store classes, interfaces, enums, and annotation types -- and subpackages (packages nested within other packages).

A package has a name, which must be a nonreserved identifier; for example, java. The member access operator (.) separates a package name from a subpackage name and separates a package or subpackage name from a type name.

For example, the two member access operators in java.lang.System separate package namejava from the lang subpackage name and separate subpackage name lang from the System type name.

Reference types must be declared public to be accessible from outside their packages. The same applies to any constants, constructors, methods, and nested types that must be accessible. You'll see examples later in this article.

What is the use of package statement in Java?

This statement appears at the top of a source file and identifies the package to which the source file's types belong. It must conform to the following syntax:

package identifier[.identifier]*;

A package statement starts with reserved word package and continues with an identifier, which is optionally followed by a period-separated sequence of identifiers. A semicolon (;) terminates this statement.

The first (left-most) identifier names the package and each subsequent identifier names a subpackage. For example, in package a.b;, all types declared in the source file belong to the b subpackage of the a package.

By convention, a package/subpackage name is expressed in lowercase. When the name consists of multiple words, each word except for the first word is capitalized; for example, acctsPayable.

A sequence of packages names must be unique, to avoid compilation problems.

For example, suppose you create two different graphics packages, and suppose that each graphics package contains a Rectangle class with a different interface. When the Java compiler encounters, for example, Rectangle r = new Rectangle (1, 20, 30, 40); in the source code, it needs to verify that the Rectangle(int, int, int, int) constructor exists.

The compiler will search all accessible packages (the search algorithm is discussed later) until it finds a graphics package that contains a Rectangle class. If the found package contains the appropriate Rectangle class with a Rectangle(int, int, int, int) constructor, everything is fine.

Otherwise, if the found Rectangle class doesn't have a Rectangle(int, int, int, int) constructor, the compiler reports an error.

This scenario illustrates the importance of choosing unique package name sequences.

The convention in choosing a unique name sequence is to reverse your Internet domain name and use it as a prefix for the sequence.

For example, I would choose ca.javajeff as my prefix because javajeff.ca is my domain name. I would then specify ca.javajeff.graphics.Rectangle to access Rectangle.

Domain name components are not always valid package names. One or more component names might start with a digit (9.com), contain a hyphen (-) or other illegal character (ab-z.com), or be one of Java's reserved words (float.com).

Convention dictates that you prefix the digit with an underscore (com._9), replace the illegal character with an underscore (com.ab_z), and suffix the reserved word with an underscore (com.float_).

To avoid additional problems with the package statement, there are a couple of rules that you need to follow:

  • You can declare only one package statement in a source file.
  • You cannot precede the package statement with anything apart from comments.

The first rule, which is a special case of the second rule, exists because it doesn't make sense to store a reference type in multiple packages.

Although a package can store multiple types, a type can belong to only one package.

When a source file doesn't declare a package statement, the source file's types are said to belong to the unnamed package.

Non-trivial reference types are typically stored in their own packages and avoid the unnamed package.

Java implementations map package and subpackage names to same-named directories (or folders, if you prefer).

For example, an implementation would map graphicsto a directory named graphics. In a.ba would map to a directory named a and b would map to a b subdirectory of a. The compiler stores the class files that implement the package's types in the corresponding directory.

Note that the unnamed package corresponds to the current directory.

Packaging an image library

A practical example will help you fully grasp the package statement. I'm demonstrating package in the context of an image library that lets you read image files and obtain image data. For brevity, only a skeletal version of the library is presented.

The image library currently consists of only three classes: ImageImageException, and PngReaderImage describes an image and is the library's main class. Listing 1 presents its source code.

package ca.javajeff.image;

 
public final class Image
{
   private int[] argb;
   private int width, height;

 
   Image(int[] argb, int width, int height)
   {
      // argb is an array of alpha, red, blue, and green values.

 
      this.argb = argb;
      this.width = width;
      this.height = height;
   }

 
   public int[] getARGB()
   {
      return argb;
   }

 
   public int getHeight()
   {
      return height;
   }

 
   public int getWidth()
   {
      return width;
   }

 
   public static Image newImage(String filename) throws ImageException
   {
      if (filename.toLowerCase().endsWith(".png"))
         return PngReader.read(filename);
      else
         throw new ImageException("unsupported format");
   }
}

Listing 1: Image.java

Listing 1's Image class is stored in file Image.java. This listing begins with a package statement that identifies ca.javajeff.image as the class's package.

Image is declared public so that it can be referenced from outside of its package. Also, it's declared final so that it cannot be extended (subclassed).

Image declares private argbwidth, and height fields to store image data. These fields are initialized to the values passed to Image's constructor.

Image's constructor is declared package-private (the constructor isn't declared publicprivate, or protected) so that this class cannot be instantiated from outside of its package.

Image presents getARGB()getHeight(), and getWidth() methods for returning an image's pixel values, height, and width. Each method is declared public so that it can be called from outside of Image's package.

Image concludes with a public and static newImage() factory method for returning an Image object corresponding to the filename argument. If the image cannot be obtained, ImageException (see Listing 2) is thrown.

package ca.javajeff.image;

 
public final class ImageException extends Exception
{
   public ImageException(String message)
   {
      super(message);
   }
}

Listing 2: ImageException.java

Listing 2's ImageException class is located in the same ca.javajeff.image package as Image, and is declared public so that it's accessible beyond this package. Also, ImageException extends the Exception class in the default java.lang package, and provides an appropriate constructor that forwards the string passed to message to the Exception(String message) constructor.

newImage() compares filename's extension with .png (only PNG images are currently supported). If they match, return PngReader.read(filename) is executed (return an Image object with PNG image data). Listing 3 describes PngReader.

package ca.javajeff.image;

 
final class PngReader
{
   static Image read(String filename) throws ImageException
   {
      // Read the contents of filename's file and process it 
      // into argb, width, and height values. Throw 
      // ImageException if there is an I/O error.

 
      return new Image(new int[0], 0, 0);
   }
}

Listing 3: PngReader.java

PngReader is intended to read a PNG file's contents into an Image object. (The class will eventually be larger with additional private fields and/or methods.) Notice that this class isn't declared public, which makes PngReader accessible to Image but not to code outside of the ca.javajeff.image package. Think of PngReader as a helper class whose only reason for existence is to serve Image.

Complete the following steps to build this library:

  1. Select a suitable location in your file system as the current directory.
  2. Create a ca/javajeff/image subdirectory hierarchy within the current directory.
  3. Copy Listings 1, 2, and 3 to files Image.javaImageException.java, and PngReader.java, respectively; and store these files in the image subdirectory.
  4. Assuming that the current directory contains the ca subdirectory, execute javac ca/javajeff/image/*.java to compile the three source files in ca/javajeff/image. If all goes well, you should discover Image.classImageException.class, and PngReader.class files in the image subdirectory. (Alternatively, for this example, you could switch to the image subdirectory and execute javac *.java.)

Now that you've created the image library, you'll want to use it.

Later, I'll present a small Java application that property demonstrates this library, but first you need to learn about the import statement.

The import statement

Imagine having to repeatedly specify ca.javajeff.graphics.Rectangle for each occurrence of Rectangle in source code.

Java provides a convenient alternative for omitting lengthy package details. This alternative is the import statement.

The import statement imports types from a package by telling the compiler where to look for unqualified (no package prefix) type names during compilation. It appears near the top of a source file and must conform to the following syntax:

import identifier[.identifier]*.(typeName | *);

An import statement starts with reserved word import and continues with an identifier, which is optionally followed by a period-separated sequence of identifiers. A type name or asterisk (*) follows, and a semicolon terminates this statement.

The syntax reveals two forms of the import statement.

  • First, you can import a single type name, which is identified via typeName.
  • Second, you can import all types, which is identified via the asterisk.

The * symbol is a wildcard that represents all unqualified type names. It tells the compiler to look for such names in the right-most package of the import statement's package sequence, unless the type name is found in a previously searched package.

Note that using the wildcard doesn't have a performance penalty or lead to code bloat. However, it can lead to name conflicts, which you will see.

For example, import ca.javajeff.graphics.Rectangle; tells the compiler that an unqualified Rectangle class exists in the ca.javajeff.graphics package. Similarly, import ca.javajeff.graphics.*; tells the compiler to look in this package when it encounters a Rectangle name, a Circle name, or even an Accountname (if Account hasn't already been found).

When working on a multi-developer project, avoid using the * wildcard so that other developers can easily see which types are used in your source code.

You can run into name conflicts when using the wildcard version of the import statement because any unqualified type name matches the wildcard.

For example, you have graphics and geometry packages that each contain a Rectangle class, the source code begins with import geometry.*; and import graphics.*;statements, and it also contains an unqualified occurrence of Rectangle.

Because the compiler doesn't know if Rectangle refers to geometry's Rectangle class or tographicsRectangle class, it reports an error. You can fix this problem by qualifying Rectangle with the correct package name (graphics.Rectangle orgeometry.Rectangle).

To avoid additional problems with the import statement, there are a couple of rules that you need to follow:

  • Because Java is case sensitive, package and subpackage names specified in an import statement must be expressed in the same case as that used in the package statement.
  • You cannot precede the import statement with anything apart from comments, a package statement, other import statements, and static import statements (which have nothing to do with packages).

The compiler automatically imports types from the java.lang library package. As a result, you don't have to specify import java.lang.System; (import java.lang's System class) or similar import statements in your source code.

Because Java implementations map package and subpackage names to same-named directories, an import statement is equivalent to loading a reference type's class file from the directory sequence corresponding to the package sequence.

Importing types from the image library

I've created a DemoImage application that demonstrates the image library. Listing 4 presents this application's source code.

import ca.javajeff.image.Image;
import ca.javajeff.image.ImageException;

 
public final class DemoImage
{
   public static void main(String[] args) throws ImageException
   {
      if (args.length != 1)
      {
         System.err.println("usage: java DemoImage filename");
         return;
      }

 
      Image image = Image.newImage(args[0]);
      System.out.println("ARGB values");
      for (int i = 0; i < image.getARGB().length; i++)
         System.out.print(image.getARGB()[i] + " ");
      System.out.println();
      System.out.printf("Width: %d%n", image.getWidth());
      System.out.printf("Height: %d%n", image.getHeight());
   }
}

Listing 4: DemoImage.java

Listing 4 doesn't begin with a package statement because simple applications are typically not stored in packages. Instead, it begins with two import statements for importing the image library's Image and ImageException classes.

The main() method first verifies that a single command-line argument has been specified. If verification succeeds, it passes this argument to Image.newImage() and assigns the returned Image object's reference to a local variable named imagemain() then proceeds to interrogate this Image object, outputting the image's ARGB values along with its width and height.

Copy Listing 4 to a file named DemoViewer.java and place this file in the same directory as the ca directory that you previously created. Then, execute the following command to compile DemoImage.java:

javac DemoImage.java

If all goes well, you should observe DemoImage.class in the current directory.

Execute the following command to run DemoImage against a fictitious PNG file named image.png:

java DemoImage image.png

You should observe the following output:

ARGB values

 
Width: 0
Height: 0

Suppose that DemoImage.java wasn't located in the same directory as ca. How would you compile this source file and run the resulting application? The answer is to use the classpath.

The classpath is a sequence of packages that the Java virtual machine (JVM) searches for reference types. It's specified via the -classpath (or -cp) option used to start the JVM or, when not present, the CLASSPATH environment variable.

Whether you use the -classpath/-cp option or the CLASSPATH environment variable to specify a classpath, there is a specific format that must be followed. Under Windows, this format is expressed as path1;path2;..., where path1path2, and so on are the locations of package directories. Under Mac OS X, Unix, and Linux, this format changes to path1:path2:....

Suppose (on a Windows platform) that the image library is stored in C:\image and that DemoImage.java is stored in C:\DemoImage, which is current. Specify the following commands to compile the source code and run the application:

javac -cp ../image DemoImage.java
java -cp ../image;. DemoImage image.png

The period character in the java-prefixed command line represents the current directory. It must be specified so that the JVM can locate DemoImage.class.

Additional Java package topics

The Java language includes a protected keyword that's useful in a package context. Also, the JVM follows a specific search order when searching packages for reference types. This section explores these topics.

Protected access

The protected keyword assigns the protected access level to a class member, such as a field or method (e.g., protected void clear()). Declaring a class member protected makes the member accessible to all code in any class located in the same package and to subclasses regardless of their packages.

Joshua Bloch explains the rationale for giving class members protected access in "Item 17: Design and document for inheritance or else prohibit it" of his book, Effective Java Second Edition.

They are hooks into a class's internal workings to let programmers "write efficient subclasses without undue pain." Check out this book for more information.

Searching packages for reference types

Newcomers to Java packages often become frustrated by "no class definition found" and other errors. This frustration can be partly avoided by understanding how the JVM looks for reference types.

To understand this process, you must realize that the compiler is a special Java application that runs under the control of the JVM. Also, there are two forms of search.

Compile-time search

When the compiler encounters a type expression (such as a method call) in source code, it must locate that type's declaration to verify that the expression is legal (a method exists in the type's class whose parameter types match the types of the arguments passed in the method call, for example).

The compiler first searches the Java platform packages, which contain Java's standard class library types (such as java.lang's System class).

It then searches extension packages for extension types. If the -sourcepath option is specified when starting the JVM (via the javac compiler tool), the compiler searches the indicated path's source files.

Java platform packages are stored in rt.jar and a few other important Java ARchive (JAR) files. (A JAR file is a Zip file with a .jar extension that includes a META-INF directory containing a manifest.mf file and possibly additional content.) Extension packages are stored in a special extensions directory named ext.

This directory is located in the lib subdirectory of the Java home directory's jre subdirectory.

  • Otherwise, the compiler searches the classpath (in left-to-right order) for the first class file or source file containing the type.
  • If no classpath is present, the current directory is searched.
  • If no package matches or the type still cannot be found, the compiler reports an error.

Otherwise, it records the package information in the class file.

Runtime search

When the compiler or any other Java application runs, the JVM will encounter types and must load their associated class files via special code known as a classloader.

The JVM will use the previously stored package information that's associated with the encountered type in a search for that type's class file.

The JVM searches the Java platform packages, followed by extension packages, followed by the classpath or current directory (when there is no classpath) for the first class file that contains the type.

If no package matches or the type cannot be found, a "no class definition found" error is reported. Otherwise, the class file is loaded into memory.

Conclusion

Now that you've learned how to construct libraries with the help of the package statement and how to import library types into your applications with the help of the import statement, consider creating your own useful library and application, to gain more experience with packages.

You might find it helpful to store your library's class files in a JAR file for more convenient distribution of the library.

About the Author

Jeff Friesen teaches Java technology (including Android) to everyone via articles, books, blogs and software. In addition to writing Java books for Apress, Jeff has written numerous articles on Java and other technologies for JavaWorld, InformIT, Java.net and SitePoint. Jeff can be reached via his website at JavaJeff.ca.

Join the discussion

Comments ( 1 )
  • Vishal Sunday, December 30, 2018
    Thank you so much!!!
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha