Java Fundamentals Tutorial: Packaging

7. Packaging

  • Packaging Java Code
  • Using Packaged Java Libraries

7.1. Why is Packaging Needed?

  • Packaging protects against name collisions.

    • With thousands of Java libraries available, it’s inevitable that class and interface names will be duplicated.
    • Packages allow you to use two different classes or interfaces with the same name.
  • Packages hide implementation that spans multiple classes (multi-class encapsulation).

    • A set of classes might need access to each other, but still be hidden from the outside world.
    • Java packages support access levels other than public and private to support information sharing and class visibility.
  • Packages help organize source code.

    • Having all source files in the same directory becomes hard to manage as the projects grow.
    • Packages allow you to group together related classes and interfaces into common directories.

[Note]Note

In addition to classes and interfaces, a Java package can contain definitions of enumerations and annotations. These Java constructs are discussed later in this class.

7.2. Packages in Java

  • A Java package name consists of a set of name components separated by periods (.).

    • Each name component must be a valid Java identifier.
    • A component name must not start with an upper-case letter.

      [Tip]Tip

      Best practice is for each name component to be all lower-case.

  • The package name corresponds to a directory structure on disk where the classes are stored.

    • For example, the class and interface files for org.xml.sax are located in the directory org/xml/sax, relative to the directory where Java looks for classes (we’ll discuss this later in this module).
  • To add a class or interface to a package:

    • Add package myPackageName; as the first Java statement of the source file
    • In your development directory, store the source file in a directory structure corresponding to your package name.

[Tip]Tip

Java does not require you to store your .java source files in in package-based directories, but it is a common convention. However, once the source files are compiled, the generated .class files are (and must be) stored in the package-based directories.

It’s also common to have separate, parallel directory hierarchies for your .java and .class files reflecting the package structure. Having your source files separate from your class files is convenient for maintaining the source code in a source code control system without having to explicitly filter out the generated class files. Having the class files in their own independent directory hierarchy is also useful for generating JAR files, as discussed later in this module.

Most modern IDEs can create and manage your source and class file directory hierarchies automatically for you.

[Caution]Caution

If you don’t explicitly specify a package, your classes and interfaces end up in an unnamed packaged, also known as the default package. Best practice is not to use the default package for any production code.

7.3. Sub-Packages in Java

  • You can construct hierarchical package names, for example org.xml.sax and org.xml.sax.helpers.

    • However, Java treats these as two independent packages, one with class files in org/xml/sax and the other with class files in org/xml/helpers.
    • The “information sharing” features of Java packages do not apply across these independent packages.
    • Still, hierarchical package names are useful for grouping together related sets of classes.

7.4. Package Naming Conventions

  • To prevent package name collisions, the convention is for organizations to use their reversed Internet domain names to begin their package names.

    • For example, com.marakana
    • If the Internet domain name contains an invalid character, such as a hyphen, the convention is to replace the invalid character with an underscore. For example, com.avia_training
    • If a domain name component starts with a digit or consists of a reserved Java keyword, the convention is to add an underscore to the component name.
  • Organizations often create sub-packages to reflect business units, product lines, etc.

    • For example, com.marakana.android
  • Package names starting with java. and javax. are reserved for Java libraries.

Java does not do any explicit protection of package namespaces. This means that it is possible for an organization ABC to create a class in package com.xyz even though the domain xyz.com belongs to an organization called XYZ. However in practice organizations do not abuse each other’s namespaces.

7.5. Using Package Members: Qualified Names

  • The fully-qualified name for a class or interface is the package name followed by the class/interface name, separated by a period (.).

    • For example, com.marakana.utils.MyClass
  • Code outside the package can reference public classes and interfaces of a package using their fully-qualified names.
  • Code within the package can reference classes and interfaces of the package by their simple names, without package qualification.
  • To execute a Java application whose class is contained within a package, you must use its fully-qualified name.

    • For example, to run ShapeDemo.main() in the package shape you must invoke it as:

      java shape.ShapeDemo

7.6. Importing Package Members

  • Instead of using fully-qualified names for classes and interfaces, you can import the package member.

    • To import a specific member from a package, include an import statement specifying the fully-qualified name of that member. For example:

      import com.marakana.utils.MyClass;

      You can then use the simple (unqualified) name of the member throughout the rest of the source file.

    • You can import all of the members of a package by importing the package with an asterisk (*) wildcard. For example:

      import com.marakana.utils.*;

      You can then use the simple (unqualified) name of all the package members throughout the rest of the source file.

      [Important]Important

      Using the wildcard import does not import any sub-packages or their members.

  • The import statements must appear after any package statement and before all class/interface definitions in your file.
[Note]Note

The java.lang package is imported automatically into all source files.

In case there are two or more classes with the same name but defined in different imported (or assumed) packages, then those classes must be referenced by their fully-qualified-class-name (FQCN).

For example, you could define:

com.mycompany.calc.AdditionOperation,

but someone else could implement:

com.othercompany.calculator.AdditionOperation.

If you wanted to use both addition operations from a class defined in com.mycompany.calc package, then doing

import com.othercompany.calculator.AdditionOperation;

would make AdditionOperation ambiguous. Instead of importing it, you would reference it by its FQCN.

[Caution]Caution

Even though importing an entire package using the asterisk (*) notation is convenient, it is not generally recommended — especially when multiple packages are imported. It makes it harder to track down the classes if you do not know exactly which package they come from. Modern IDEs help with resolving classes (as well as with organizing imports), but you should not assume that everyone will always have an IDE handy.

7.7. Static Imports

  • Prior to Java 5, you always had to access static fields and methods qualified by their class name.

    • For example:

      double c= Math.PI * Math.pow(r, 2.0);
  • Java 5 introduced import static to import static fields and methods.

    • For example:

      import static java.lang.Math.PI;    // A single field
      import static java.lang.Math.*;     // All static fields and methods
    • Once imported, you can use them as if they were defined locally:

      double c= PI * pow(r, 2.0);

7.8. Access Modifiers and Packages

  • The the last module introduced the concept of the public and private access modifiers that control access to fields, methods, constructors, classes, and interfaces.

    • There are two additional access levels:
Access ModifierDescription

public

Accessible from any class

protected

Accessible from all classes in the same package or any child classes regardless of the package

default (no modifier)

Accessible only from the classes in the same package (also known as friendly)

private

Accessible only from within the same class (or any nested classes)

7.9. The Class Path

  • The class path is the path that the Java runtime environment searches for classes and other resource files.
  • By default, Java looks for classes in only the present working directory (.).
  • To access classes at runtime outside the present working directory, there are three options:

    • Set an environmental variable named CLASSPATH with a colon-separated list of class directories, JAR files, and/or Zip files. (This should be a semicolon-separated list on Windows.)
    • Use use the -classpath option (-cp is an alias) with java and javac to override the class path with a colon/semicolon-separated list of class directories, JAR files, and/or Zip files.
    • Copy classes to $JAVA_HOME/lib/ext/ (this is typically used only for Java extensions).
[Important]Important

The class path should include only the root directory of a package directory hierarchy, not the individual package directories.

For example, to run calc.CalculatorDemo located in /home/me/classes/calc/CalculatorDemo.class, you could do:

cd /home/me/classes
java calc.CalculatorDemo

or (from any directory):

export CLASSPATH=/home/me/classes
java calc.CalculatorDemo

or (from any directory):

java -classpath /home/me/classes calc.CalculatorDemo

or (from any directory):

cp -r /home/me/classes/calc $JAVA_HOME/lib/ext/.
java calc.CalculatorDemo

It is important to notice the distinction between class path locations and package directories. Package-based directories should never be included within the class path and vice-versa.

This means that these would not work:

java -classpath /home/me/classes/calc CalculatorDemo
java -classpath /home/me classes.calc.CalculatorDemo

7.10. Java Archive (JAR) Files

  • A Java Archive (JAR) file packages multiple classes in a single compressed file.

    • Based on the ZIP file format
    • Easier and more efficient management of binaries
    • Also known as Java library (.jar extension)
    • Must preserve package directory structure
  • A JAR file can include additional meta-data in a manifest file at the pathname META-INF/MANIFEST.MF.

    • The manifest is a text file containing name: value formatted entries.
    • For example, you can make an executable JAR by including a Main-Class entry whose value is the fully-qualified name of a class containing a main() method. You could then execute the JAR as:

      java -jar <file>.jar

JAR files internally must look like package directories. For example, a class com.myco.calc.CalculatorDemo would be stored within calculator.jar file just like it would be stored on disk: com/myco/calc/CalculatorDemo.class.

JAR files are referenced in the CLASSPATH by their actual name, not by the name of the directory in which they are contained.

To run com.myco.calc.CalculatorDemo you would do:

java -classpath calculator.jar com.myco.calc.CalculatorDemo

To make calculator.jar executable:

  1. Create a file: META-INF/MANIFEST.MF
  2. Add a line:

    Main-Class: com.myco.calc.CalculatorDemo
  3. Include META-INF/MANIFEST.MF in calculator.jar
  4. Run with

    java -jar calculator.jar

7.11. The jar Command-Line Tool Examples

  • Create a JAR file

    cd classes; jar -cvf shape.jar shape
    jar -cvf shape.jar -C classes shape
  • View the contents of a JAR file

    jar -tvf shape.jar
  • Extract the contents of JAR file

    jar -xvf shape.jar
    jar -xvf shape.jar META-INF/MANIFEST.MF
  • Update the JAR file (e.g., add to the manifest)

    jar -uvmf MainClass.txt shape.jar

You will find that the jar command has a usage similar to the Unix tar command:

Usage: jar {ctxui}[vfm0Me] [jar-file] [manifest-file] [entry-point] [-C dir] files ...
Options:
    -c  create new archive
    -t  list table of contents for archive
    -x  extract named (or all) files from archive
    -u  update existing archive
    -v  generate verbose output on standard output
    -f  specify archive file name
    -m  include manifest information from specified manifest file
    -e  specify application entry point for stand-alone application
        bundled into an executable jar file
    -0  store only; use no ZIP compression
    -M  do not create a manifest file for the entries
    -i  generate index information for the specified jar files
    -C  change to the specified directory and include the following file
If any file is a directory then it is processed recursively.
The manifest file name, the archive file name and the entry point name are
specified in the same order as the 'm', 'f' and 'e' flags.

Example 1: to archive two class files into an archive called classes.jar:
       jar cvf classes.jar Foo.class Bar.class
Example 2: use an existing manifest file 'mymanifest' and archive all the
           files in the foo/ directory into 'classes.jar':
       jar cvfm classes.jar mymanifest -C foo/ .
[Note]Note

You can use any utility with support for ZIP files (e.g. WinZip) to create, view, extract, and manage your JAR files.