To reference a type defined in a package, use an import statement, the package name, and the type name.
If a requisite .class file is not found during compilation, then its corresponding .java file will be compiled.
// ///// D:\java\test\A.java \\\\\ // package test; // public class A { // public static void aMethod() { System.out.println( "A.aMethod" ); } } ///// D:\java\PackageDemos.java \\\\\ import test.A; public class PackageDemos { public static void main( String[] args ) { A.aMethod(); } } // D:\Java> javac -verbose PackageDemos.java // [parsed PackageDemos.java in 313 ms] // [loaded d:\jdk1.2.1\jre\lib\rt.jar(java/lang/Object.class) in 47 ms] // [checking class PackageDemos] // ... // [parsed .\test\A.java in 0 ms] // [wrote PackageDemos.class] // [checking class test.A] // ... // [wrote .\test\A.class] // [done in 1656 ms] // // D:\Java> java PackageDemos // A.aMethodTwo other options for accessing a type in a package are shown below. An import that uses ".*" is called an "import on demand" declaration. When this form is used, all members in the package are visible.
The last option, is to "fully qualify" each reference to a type with its complete package name.
import test.*; public class PackageDemos { public static void main( String[] args ) { A.aMethod(); } } public class PackageDemos { public static void main( String[] args ) { test.A.aMethod(); java.util.Stack st = new java.util.Stack(); } }The package name must always match the name of the subdirectory that contains the component's .java or .class file. Below, the component is in the current directory, and the compiler insists that it cannot be found. When the component is copied to the directory that corresponds to its package name, the compiler is happy.
// ///// D:\java\A.java \\\\\ // package test; // public class A { // public static void aMethod() { System.out.println( "A.aMethod" ); } } // D:\Java> javac PackageDemos.java // PackageDemos.java:29: Class test.A not found in import. // D:\Java> copy a.java test // 1 file(s) copied. // D:\Java> javac PackageDemos.java // D:\Java> java PackageDemos // A.aMethodThe CLASSPATH environment variable (or the -classpath command line argument) control where the Java compiler and interpreter search for non-system .class files. To "unset" CLASSPATH, use an empty assignment statement.
// D:\Java> set CLASSPATH=\ // D:\Java> javac PackageDemos.java // PackageDemos.java:28: Class test.A not found in import. // D:\Java> java PackageDemos // Exception in thread "main" java.lang.NoClassDefFoundError: PackageDemos // D:\Java> set CLASSPATH=\java // D:\Java> javac PackageDemos.java // D:\Java> java PackageDemos // A.aMethod // D:\Java> set CLASSPATH=\ // D:\Java> javac -classpath \java PackageDemos.java // D:\Java> java -classpath \java PackageDemos // A.aMethod // D:\Java> set CLASSPATH=. // D:\Java> java PackageDemos // A.aMethod // D:> set CLASSPATH= // D:> java PackageDemos // A.aMethodJava types not explicitly assigned to a package, are automatically assigned to the "unnamed package" (also-known-as the "default package"). Java types in the "default package" should not be assigned the public access modifier. This minimizes name resolution conflicts that might occur when the compiler is searching for referenced classes - public classes in the "default package" will always "mask" classes that have been assigned to explicit packages. [Allison, Sep99, p48]
CLASSPATH is actually a list of directories that the compiler and interpreter search to resolve unknown names. The order of the directories listed in CLASSPATH is significant.
// ///// \java\B.java \\\\\ // public class B { // public static void foo() { System.out.println( "/java/B.foo" ); } } // ///// \java\test\B.java \\\\\ // public class B { // public static void foo() { System.out.println( "/java/test/B.foo" ); } } ///// \java\PackageDemos.java \\\\\ public class PackageDemos { public static void main( String[] args ) { B.foo(); } } // D:\Java> set CLASSPATH= // D:\Java> javac PackageDemos.java // D:\Java> java PackageDemos // /java/B.foo // D:\Java> set CLASSPATH=\java\test // D:\Java> javac PackageDemos.java // D:\Java> java PackageDemos // Exception in thread "main" java.lang.NoClassDefFoundError: PackageDemos // D:\Java> set CLASSPATH=\java\test;. // D:\Java> java PackageDemos // /java/test/B.foo // D:\Java> set CLASSPATH=.;\java\test // D:\Java> java PackageDemos // /java/B.fooThe -classpath command line argument, if specified, overrides the CLASSPATH environment variable.
// D:\Java> set CLASSPATH=\java\test;. // D:\Java> java PackageDemos // /java/test/B.foo // D:\Java> java -classpath \java PackageDemos // /java/B.fooThe CLASSPATH environment variable allows multiple development spaces (or configuration sub-trees) to be maintained simultaneously. Below, there is a "test" hierarchy, and a parallel "prod" hierarchy. All that is required to switch between hierarchies is to reset CLASSPATH.
// ///// \java\test\demo\A.java \\\\\ // package demo; // public class A { // public static void aMethod() { // System.out.println( "test.demo.A.aMethod" ); } } // ///// \java\prod\demo\A.java \\\\\ // package demo; // public class A { // public static void aMethod() { // System.out.println( "prod.demo.A.aMethod" ); } } ///// \java\PackageDemos.java \\\\\ import demo.A; public class PackageDemos { public static void main( String[] args ) { A.aMethod(); } } // D:\Java> set CLASSPATH=\java\test // D:\Java> javac -verbose PackageDemos.java // [parsed PackageDemos.java in 361 ms] // [parsed \java\test\demo\A.java in 30 ms] // [loaded d:\jdk1.2.1\jre\lib\rt.jar(java/lang/Object.class) in 60 ms] // [checking class PackageDemos] // ... // [wrote PackageDemos.class] // [checking class demo.A] // ... // [wrote \java\test\demo\A.class] // [done in 1932 ms] // D:\Java> java PackageDemos // test.demo.A.aMethod // D:\Java> set CLASSPATH=.;\java\prod // ///// prod\demo\A.java has not been compiled yet \\\\\ // D:\Java>java PackageDemos // Exception in thread "main" java.lang.NoClassDefFoundError: demo/A // D:\Java> javac PackageDemos.java // D:\Java> java PackageDemos // prod.demo.A.aMethodPackages form a container for names and should routinely insulate the programmer from name collisions. But, it two packages do contain the same name, then a fully-qualified name in the import statement or at the point(s) of reference will resolve the ambiguity.
// ///// D:\java\test\A.java \\\\\ // package test; // public class A { // public static void aMethod() { System.out.println( "test.A.aMethod" ); } } // ///// D:\java\prod\A.java \\\\\ // package prod; // public class A { // public static void aMethod() { System.out.println( "prod.A.aMethod" ); } } ///// \java\PackageDemos.java \\\\\ import test.*; import prod.*; public class PackageDemos { public static void main( String[] args ) { A.aMethod(); } } // D:\Java> javac PackageDemos.java // PackageDemos.java:247: Ambiguous class: test.A and prod.A ///// \java\PackageDemos.java \\\\\ import test.*; import prod.*; public class PackageDemos { public static void main( String[] args ) { prod.A.aMethod(); } } // D:\Java> javac PackageDemos.java // D:\Java> java PackageDemos // prod.A.aMethod ///// \java\PackageDemos.java \\\\\ import test.*; import prod.A; public class PackageDemos { public static void main( String[] args ) { A.aMethod(); } }The CLASSPATH mechanism can also function as a mini "make" utility. If changes are made to any referenced components and the main application is recompiled, the compiler will automatically recompile the components that have been updated. Notice at the end that the B.java file is not recompiled if the B.class file is up to date.
// ///// \java\test\B.java \\\\\ // public class B { // public static void foo() { // System.out.println( "/java/test/B.foo new implementation" ); } } // D:\Java> set CLASSPATH=\java\test;. // D:\Java> javac -verbose PackageDemos.java // [parsed PackageDemos.java in 313 ms] // ... // [checking class PackageDemos] // ... // [parsed \java\test\B.java in 15 ms] // [wrote PackageDemos.class] // [checking class B] // ... // [wrote \java\test\B.class] // [done in 1641 ms] // D:\Java> java PackageDemos // /java/test/B.foo new implementation // D:\Java> javac -verbose PackageDemos.java // [parsed PackageDemos.java in 313 ms] // ... // [checking class PackageDemos] // ... // [wrote PackageDemos.class] // [done in 1562 ms]For more on packages, see
Allison, C/C++ Users Journal, Sep 99, p42 Horstmann, Core Java 2, volume I, p153Mapping all your packages into a directory subtree is nice, but dealing with a subtree can be cumbersome for a stand-alone application; and, it can be unsuitable for an applet that needs to be downloaded across the Internet. JAR files (Java ARchive files) address such issues. These are cross-platform archives that can hold an arbitrary number of directories and files. .class files are the most common files archived, but any kind of file (such as html, image, and audio files) can be archived.
The interface of jar is identical to Unix's "tar" utility. Notice that jar automatically compresses files. It uses the "ZIP" compression format. In fact, jar can be used to manipulate .zip files.
// ///// D:\java\test\A.java \\\\\ // package test; // public class A { // public static void aMethod() { System.out.println( "test.A.aMethod" ); } } ///// D:\java\PackageDemos.java \\\\\ import test.*; public class PackageDemos { public static void main( String[] args ) { A.aMethod(); } } // D:\Java> jar cvf TestPackage.jar test\*.class // added manifest // adding: test/A.class (in=389) (out=269) (deflated 30%) // D:\Java> jar tvf TestPackage.jar // 0 Sun Jan 02 19:16:50 CST 2000 META-INF/ // 66 Sun Jan 02 19:16:50 CST 2000 META-INF/MANIFEST.MF // 389 Sun Jan 02 19:16:00 CST 2000 test/A.class // D:\Java> javac -classpath .;TestPackage.jar PackageDemos.java // D:\Java> java -classpath .;TestPackage.jar PackageDemos // test.A.aMethodYou can also run entire stand-alone applications from a JAR file. The .class file that is hosting the application's main() must be added to the .jar file (the "uv" option below); and, the name of its class must be added to the JAR file's manifest. The manifest resides in a pseudo-directory inside the JAR file named META-INF. It can be updated by creating an ASCII file with the one line of text shown below, and then using the "umv" option below that. Notice how the size of the manifest has increased by 26 bytes. The Java interpreter can then be run with the "-jar" option.
// D:\Java> jar uvf TestPackage.jar PackageDemos.class // adding: PackageDemos.class (in=308) (out=236) (deflated 23%) // // D:\Java> jar tvf TestPackage.jar // 0 Sun Jan 02 21:05:24 CST 2000 META-INF/ // 68 Sun Jan 02 21:05:24 CST 2000 META-INF/MANIFEST.MF // 389 Sun Jan 02 21:00:28 CST 2000 test/A.class // 308 Sun Jan 02 21:05:02 CST 2000 PackageDemos.class // D:\Java> notepad manifest.txt // Main-class: PackageDemos // D:\Java> jar umvf manifest.txt TestPackage.jar // updated manifest // // D:\Java> jar tvf TestPackage.jar // 0 Sun Jan 02 21:05:24 CST 2000 META-INF/ // 94 Sun Jan 02 21:09:36 CST 2000 META-INF/MANIFEST.MF // 389 Sun Jan 02 21:00:28 CST 2000 test/A.class // 308 Sun Jan 02 21:05:02 CST 2000 PackageDemos.class // D:\Java> java -jar TestPackage.jar // test.A.aMethodThe Java 2 standard libraries are packaged in the JAR file \jdk1.2.1\jre\lib\rt.jar. If you inspect that file with the "tf" option, you will find all your favorite classes like java/lang/System.class and java/util/Vector.class. Starting in Java 2, the file rt.jar is automatically searched before CLASSSPATH. For more on JAR files, see
\jdk1.2.1\docs\tooldocs\win32\jar.html http://java.sun.com/docs/books/tutorial/jar/basics/ Horstmann, Core Java 2, volume I, p571