Time Class Case Study: Creating Packages
Time Class Case Study Creating Packages
We have seen in almost every example in the text that classes from preexisting libraries, such as the Java API, can be imported into a Java program. Each class in the Java API belongs to a package that contains a group of related classes. As applications become more complex, packages help programmers manage the complexity of application components. Packages also facilitate software reuse by enabling programs to import classes from other packages (as we have done in most examples). Another benefit of packages is that they provide a convention for unique class names, which helps prevent class-name conflicts (discussed later in this section). This section introduces how to create your own packages.
Steps for Declaring a Reusable Class
Before a class can be imported into multiple applications, it must be placed in a package to make it reusable. Figure 8.18 shows how to specify the package in which a class should be placed. Figure 8.19 shows how to import our packaged class so that it can be used in an application. The steps for creating a reusable class are:
1. |
Declare a public class. If the class is not public, it can be used only by other classes in the same package.
|
2. |
Choose a package name and add a package declaration to the source-code file for the reusable class declaration. There can be only one package declaration in each Java source-code file, and it must precede all other declarations and statements in the file. Note that comments are not statements, so comments can be placed before a package statement in a file.
|
3. |
Compile the class so that it is placed in the appropriate package directory structure.
|
4. |
Import the reusable class into a program and use the class.
|
Figure 8.18. Packaging class Time1 for reuse.
(This item is displayed on page 392 in the print version)
1 // Fig. 8.18: Time1.java 2 // Time1 class declaration maintains the time in 24-hour format. 3 package com.deitel.jhtp6.ch08; 4 5 public class Time1 6 { 7 private int hour; // 0 - 23 8 private int minute; // 0 - 59 9 private int second; // 0 - 59 10 11 // set a new time value using universal time; perform 12 // validity checks on the data; set invalid values to zero 13 public void setTime( int h, int m, int s ) 14 { 15 hour = ( ( h >= 0 && h < 24 ) ? h : 0 ); // validate hour 16 minute = ( ( m >= 0 && m < 60 ) ? m : 0 ); // validate minute 17 second = ( ( s >= 0 && s < 60 ) ? s : 0 ); // validate second 18 } // end method setTime 19 20 // convert to String in universal-time format (HH:MM:SS) 21 public String toUniversalString() 22 { 23 return String.format( "%02d:%02d:%02d", hour, minute, second ); 24 } // end method toUniversalString 25 26 // convert to String in standard-time format (H:MM:SS AM or PM) 27 public String toString() 28 { 29 return String.format( "%d:%02d:%02d %s", 30 ( ( hour == 0 || hour == 12 ) ? 12 : hour % 12 ), 31 minute, second, ( hour < 12 ? "AM" : "PM" ) ); 32 } // end method toString 33 } // end class Time1 |
Figure 8.19. Time1 object used in an application.
(This item is displayed on page 394 in the print version)
1 // Fig. 8.19: Time1PackageTest.java 2 // Time1 object used in an application. 3 import com.deitel.jhtp6.ch08.Time1; // import class Time1 4 5 public class Time1PackageTest 6 { 7 public static void main( String args[] ) 8 { 9 // create and initialize a Time1 object 10 Time1 time = new Time1(); // calls Time1 constructor 11 12 // output string representations of the time 13 System.out.print( "The initial universal time is: " ); 14 System.out.println( time.toUniversalString() ); 15 System.out.print( "The initial standard time is: " ); 16 System.out.println( time.toString() ); 17 System.out.println(); // output a blank line 18 19 // change time and output updated time 20 time.setTime( 13, 27, 6 ); 21 System.out.print( "Universal time after setTime is: " ); 22 System.out.println( time.toUniversalString() ); 23 System.out.print( "Standard time after setTime is: " ); 24 System.out.println( time.toString() ); 25 System.out.println(); // output a blank line 26 27 // set time with invalid values; output updated time 28 time.setTime( 99, 99, 99 ); 29 System.out.println( "After attempting invalid settings:" ); 30 System.out.print( "Universal time: " ); 31 System.out.println( time.toUniversalString() ); 32 System.out.print( "Standard time: " ); 33 System.out.println( time.toString() ); 34 } // end main 35 } // end class Time1PackageTest
|
Steps 1 and 2: Creating a public Class and Adding the package Statement
For Step 1, we modify the public class Time1 declared in Fig. 8.1. The new version is shown in Fig. 8.18. No modifications have been made to the implementation of the class, so we will not discuss its implementation details again here.
For Step 2, we add a package declaration (line 3) that declares a package named com.deitel.jhtp6.ch08. Placing a package declaration at the beginning of a Java source file indicates that the class declared in the file is part of the specified package. Only package declarations, import declarations and comments can appear outside the braces of a class declaration. A Java source-code file must have the following order:
- a package declaration (if any),
- import declarations (if any), then
- class declarations.
Only one of the class declarations in a particular file can be public. Other classes in the file are placed in the package and can be used only by the other classes in the package. Non-public classes are in a package to support the reusable classes in the package.
In an effort to provide unique names for every package, Sun Microsystems specifies a convention for package naming that all Java programmers should follow. Every package name should start with your Internet domain name in reverse order. For example, our domain name is deitel.com, so our package names begin with com.deitel. For the domain name yourcollege.edu, the package name should begin with edu.yourcollege. After the domain name is reversed, you can choose any other names you want for your package. If you are part of a company with many divisions or a university with many schools, you may want to use the name of your division or school as the next name in the package. We chose to use jhtp6 as the next name in our package name to indicate that this class is from Java How to Program, Sixth Edition. The last name in our package name specifies that this package is for Chapter 8 (ch08).
Step 3: Compiling the Packaged Class
Step 3 is to compile the class so that it is stored in the appropriate package. When a Java file containing a package declaration is compiled, the resulting class file is placed in the directory specified by the package declaration. The package declaration in Fig. 8.18 indicates that class Time1 should be placed in the directory
com deitel jhtp6 ch08
The directory names in the package declaration specify the exact location of the classes in the package.
When compiling a class in a package, the javac command-line option -d causes the javac compiler to create appropriate directories based on the class's package declaration. The option also specifies where the directories should be stored. For example, in a command window, we used the compilation command
javac -d . Time1.java
to specify that the first directory in our package name should be placed in the current directory. The period (.) after -d in the preceding command represents the current directory on the Windows, UNIX and Linux operating systems (and several others as well). After executing the compilation command, the current directory contains a directory called com, com contains a directory called deitel, deitel contains a directory called jhtp6 and jhtp6 contains a directory called ch08. In the ch08 directory, you can find the file Time1.class. [Note: If you do not use the -d option, then you must copy or move the class file to the appropriate package directory after compiling it.]
The package name is part of the fully qualified class name, so the name of class Time1 is actually com.deitel.jhtp6.ch08.Time1. You can use this fully qualified name in your programs, or you can import the class and use its simple name (the class name by itselfTime1) in the program. If another package also contains a Time1 class, the fully qualified class names can be used to distinguish between the classes in the program and prevent a name conflict (also called a name collision).
Step 4: Importing the Reusable Class
Once the class is compiled and stored in its package, the class can be imported into programs (Step 4). In the Time1PackageTest application of Fig. 8.19, line 3 specifies that class Time1 should be imported for use in class Time1PackageTest. Class Time1PackageTest is in the default package because the class's .java file does not contain a package declaration. Since the two classes are in different packages, the import at line 3 is required so that class Time1PackageTest can use class Time1.
Line 3 is known as a single-type-import declarationthat is, the import declaration specifies one class to import. When your program uses multiple classes from the same package, you can import those classes with a single import declaration. For example, the import declaration
import java.util.*; // import classes from package java.util
uses an asterisk (*) at the end of the import declaration to inform the compiler that all classes from the java.util package are available for use in the program. This is known as a type-import-on-demand declaration. Only the classes from package java.util that are used in the program are loaded by the JVM. The preceding import allows you to use the simple name of any class from the java.util package in the program. Throughout this book, we use single-type-import declarations for clarity.
Common Programming Error 8.12
Using the import declaration import java.*; causes a compilation error. You must specify the exact name of the package from which you want to import classes. |
Specifying the Classpath During Compilation
When compiling Time1PackageTest, javac must locate the .class file for Time1 to ensure that class Time1PackageTest uses class Time1 correctly. The compiler uses a special object called a class loader to locate the classes it needs. The class loader begins by searching the standard Java classes that are bundled with the JDK. Then it searches for optional packages. Java provides an extension mechanism that enables new (optional) packages to be added to Java for development and execution purposes. [Note: The extension mechanism is beyond the scope of this book. For more information, visit java.sun.com/j2se/5.0/docs/guide/extensions.] If the class is not found in the standard Java classes or in the extension classes, the class loader searches the classpath, which contains a list of locations in which classes are stored. The classpath consists of a list of directories or archive files, each separated by a directory separatora semicolon (;) on Windows or a colon (:) on UNIX/Linux/Mac OS X. Archive files are individual files that contain directories of other files, typically in a compressed format. For example, the standard classes used by your programs are contained in the archive file rt.jar, which is installed with the JDK. Archive files normally end with the .jar or .zip file-name extensions. The directories and archive files specified in the classpath contain the classes you wish to make available to the Java compiler and the JVM.
By default, the classpath consists only of the current directory. However, the classpath can be modified by
- providing the -classpath option to the javac compiler or
- setting the CLASSPATH environment variable (a special variable that you define and the operating system maintains so that applications can search for classes in the specified locations).
For more information on the classpath, visit java.sun.com/j2se/5.0/docs/tooldocs/tools.html. The section entitled "General Information" contains information on setting the classpath for UNIX/Linux and Windows.
Common Programming Error 8.13
Specifying an explicit classpath eliminates the current directory from the classpath. This prevents classes in the current directory (including packages in the current directory) from loading properly. If classes must be loaded from the current directory, include a dot (.) in the classpath to specify the current directory. |
Software Engineering Observation 8.16
In general, it is a better practice to use the -classpath option of the compiler, rather than the CLASSPATH environment variable, to specify the classpath for a program. This enables each application to have its own classpath. |
Error-Prevention Tip 8.3
Specifying the classpath with the CLASSPATH environment variable can cause subtle and difficult-to-locate errors in programs that use different versions of the same package. |
For the example of Fig. 8.18 and Fig. 8.19, we did not specify an explicit classpath. Thus, to locate the classes in the com.deitel.jhtp6.ch08 package from this example, the class loader looks in the current directory for the first name in the packagecom. Next, the class loader navigates the directory structure. Directory com contains the subdirectory deitel. Directory deitel contains the subdirectory jhtp6. Finally, directory jhtp6 contains subdirectory ch08. In the ch08 directory is the file Time1.class, which is loaded by the class loader to ensure that the class is used properly in our program.
Specifying the Classpath When Executing an Application
When you execute an application, the JVM must be able to locate the classes used in that application. Like the compiler, the java command uses a class loader that searches the standard classes and extension classes first, then searches the classpath (the current directory by default). The classpath for the JVM can be specified explicitly by using either of the techniques discussed for the compiler. As with the compiler, it is better to specify an individual program's classpath via command-line options to the JVM. You can specify the classpath in the java command via the -classpath or -cp command-line options, followed by a list of directories or archive files separated by semicolons (;) on Microsoft Windows or by colons (:) on UNIX/Linux/Mac OS X. Again, if classes must be loaded from the current directory, be sure to include a dot (.) in the classpath to specify the current directory.