Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

AhmadLang / Java, How To Program, 2004

.pdf
Скачиваний:
630
Добавлен:
31.05.2015
Размер:
51.82 Mб
Скачать

[Page 388 (continued)]

8.14. Software Reusability

Java programmers concentrate on crafting new classes and reusing existing classes. Many class libraries exist, and others are being developed worldwide. Software is then constructed from existing, welldefined, carefully tested, well-documented, portable, widely available components. This kind of software reusability speeds the development of powerful, high-quality software. Rapid application development (RAD) is of great interest today.

Java programmers now have thousands of classes in the Java API from which to choose to help them implement Java programs. Indeed, Java is not just a programming language. It is a framework in which Java developers can work to achieve true reusability and rapid application development. Java programmers can focus on the task at hand when developing their programs and leave the lower-level details to the classes of the Java API. For example, to write a program that draws graphics, a Java programmer does not require knowledge of graphics on every computer platform where the program will execute. Instead, the programmer can concentrate on learning Java's graphics capabilities (which are quite substantial and growing) and write a Java program that draws the graphics, using Java's API classes, such as Graphics. When the program executes on a given computer, it is the job of the JVM to translate Java commands into commands that the local computer can understand.

The Java API classes enable Java programmers to bring new applications to market faster by using preexisting, tested components. Not only does this reduce development time, it also improves the programmer's ability to debug and maintain applications. To take advantage of Java's many capabilities, it is essential that programmers familiarize themselves with the variety of packages and classes in the Java API. There are many Web-based resources at java.sun.com to help you with this task. The primary resource for learning about the Java API is the Java API documentation, which can be found at

java.sun.com/j2se/5.0/docs/api/index.html

[Page 389]

We overview how to use the documentation in Appendix G, Using the Java API Documentation. You can download the API documentation from

java.sun.com/j2se/5.0/download.html

In addition, java.sun.com provides many other resources, including tutorials, articles and sites specific to individual Java topics.

Good Programming Practice 8.2

Avoid reinventing the wheel. Study the capabilities of the Java API. If the API contains a class that meets your program's requirements, use that class rather than create your own.

To realize the full potential of software reusability, we need to improve cataloging schemes, licensing schemes, protection mechanisms which ensure that master copies of classes are not corrupted, description schemes that system designers use to determine whether existing objects meet their needs, browsing mechanisms that determine what classes are available and how closely they meet software developer requirements, and the like. Many interesting research and development problems have been solved and many more need to be solved. These problems will likely be solved because the potential value of increased software reuse is enormous.

[Page 389 (continued)]

8.15. Data Abstraction and Encapsulation

Classes normally hide the details of their implementation from their clients. This is called information hiding. As an example, let us consider the stack data structure introduced in Section 6.6. Recall that a stack is a last-in, first-out (LIFO) data structurethe last item pushed (inserted) on the stack is the first item popped (removed) from the stack.

Stacks can be implemented with arrays and with other data structures, such as linked lists. (We discuss stacks and linked lists in Chapter 17, Data Structures, and in Chapter 19, Collections.) A client of a stack class need not be concerned with the stack's implementation. The client knows only that when data items are placed in the stack, they will be recalled in last-in, first-out order. The client cares about what functionality a stack offers, not about how that functionality is implemented. This concept is referred to as data abstraction. Although programmers might know the details of a class's implementation, they should not write code that depends on these details. This enables a particular class (such as one that implements a stack and its operations, push and pop) to be replaced with another version without affecting the rest of the system. As long as the public services of the class do not change (i.e., every original method still has the same name, return type and parameter list in the new class declaration), the rest of the system is not affected.

Most programming languages emphasize actions. In these languages, data exists to support the actions that programs must take. Data is "less interesting" than actions. Data is "crude." Only a few primitive types exist, and it is difficult for programmers to create their own types. Java and the object-oriented style of programming elevate the importance of data. The primary activities of object-oriented programming in Java are the creation of types (e.g., classes) and the expression of the interactions among objects of those types. To create languages that emphasize data, the programming-languages community needed to formalize some notions about data. The formalization we consider here is the notion of abstract data types (ADTs), which improve the program-development process.

[Page 390]

Consider primitive type int, which most people would associate with an integer in mathematics. Rather, an int is an abstract representation of an integer. Unlike mathematical integers, computer ints are fixed in size. For example, type int in Java is limited to the range2,147,483,648 to +2,147,483,647. If the result of a calculation falls outside this range, an error occurs, and the computer responds in some machine-dependent manner. It might, for example, "quietly" produce an incorrect result, such as a value too large to fit in an int variable (commonly called arithmetic overflow). Mathematical integers do not have this problem. Therefore, the notion of a computer int is only an approximation of the notion of a real-world integer. The same is true of float and other built-in types.

We have taken the notion of int for granted until this point, but we now consider it from a new perspective. Types like int, float, and char are all examples of abstract data types. They are representations of real-world notions to some satisfactory level of precision within a computer system.

An ADT actually captures two notions: A data representation and the operations that can be performed on that data. For example, in Java, an int contains an integer value (data) and provides addition, subtraction, multiplication, division and remainder operationsdivision by zero is undefined. Java programmers use classes to implement abstract data types.

Software Engineering Observation 8.15

Programmers create types through the class mechanism. New types can be designed to be convenient to use as the built-in types. This marks Java as an extensible language. Although the language is easy to extend via new types, the programmer cannot alter the base language itself.

Another abstract data type we discuss is a queue, which is similar to a "waiting line." Computer

systems use many queues internally. A queue offers well-understood behavior to its clients: Clients place items in a queue one at a time via an enqueue operation, then get them back one at a time via a dequeue operation. A queue returns items in first-in, first-out (FIFO) order, which means that the first item inserted in a queue is the first item removed from the queue. Conceptually, a queue can become infinitely long, but real queues are finite.

The queue hides an internal data representation that keeps track of the items currently waiting in line, and it offers operations to its clients (enqueue and dequeue). The clients are not concerned about the implementation of the queuethey simply depend on the queue to operate "as advertised." When a client enqueues an item, the queue should accept that item and place it in some kind of internal FIFO data structure. Similarly, when the client wants the next item from the front of the queue, the queue should remove the item from its internal representation and deliver it in FIFO order (i.e., the item that has been in the queue the longest should be the next one returned by the next dequeue operation).

The queue ADT guarantees the integrity of its internal data structure. Clients cannot manipulate this data structure directlyonly the queue ADT has access to its internal data. Clients are able to perform only allowable operations on the data representationthe ADT rejects operations that its public interface does not provide.

[Page 390 (continued)]

8.16. 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.

[Page 391]

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

 

 

 

5public 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

13public 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)

21public String toUniversalString()

22{

23return 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)

27public String toString()

28{

29return String.format( "%d:%02d:%02d %s",

30

( ( hour == 0 || hour == 12 ) ? 12 : hour % 12 ),

31minute, 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

 

 

5public class Time1PackageTest

6{

7public static void main( String args[] )

8{

9// create and initialize a Time1 object

10Time1 time = new Time1(); // calls Time1 constructor

12// output string representations of the time

13System.out.print( "The initial universal time is: " );

14System.out.println( time.toUniversalString() );

15System.out.print( "The initial standard time is: " );

16System.out.println( time.toString() );

17System.out.println(); // output a blank line

19// change time and output updated time

20time.setTime( 13, 27, 6 );

21System.out.print( "Universal time after setTime is: " );

22System.out.println( time.toUniversalString() );

23System.out.print( "Standard time after setTime is: " );

24System.out.println( time.toString() );

25System.out.println(); // output a blank line

27// set time with invalid values; output updated time

28time.setTime( 99, 99, 99 );

29System.out.println( "After attempting invalid settings:" );

30System.out.print( "Universal time: " );

31System.out.println( time.toUniversalString() );

32System.out.print( "Standard time: " );

33System.out.println( time.toString() );

34} // end main

35} // end class Time1PackageTest

The initial universal time is: 00:00:00

The initial standard time is: 12:00:00 AM

Universal time after setTime is: 13:27:06

Standard time after setTime is: 1:27:06 PM

After attempting invalid settings:

Universal time: 00:00:00

Standard time: 12:00:00 AM

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:

1.a package declaration (if any),

2.import declarations (if any), then

3.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).

[Page 392]

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

[Page 393]

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.

[Page 394]

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.

[Page 395]

By default, the classpath consists only of the current directory. However, the classpath can be modified by

1.providing the -classpath option to the javac compiler or

2.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.

[Page 396]

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.

[Page 396 (continued)]

8.17. Package Access

If no access modifier (public, protected or privateprotected is discussed in Chapter 9) is specified for a method or variable when it is declared in a class, the method or variable is considered to have package access. In a program that consists of one class declaration, this has no specific effect. However, if a program uses multiple classes from the same package (i.e., a group of related classes), these classes can access each other's package-access members directly through references to objects of the appropriate classes.

The application in Fig. 8.20 demonstrates package access. The application contains two classes in one source-code filethe PackageDataTest application class (lines 521) and the PackageData class (lines 2441). When you compile this program, the compiler produces two separate .class

filesPackageDataTest.class and PackageData.class. The compiler places the two .class files in the same directory, so the classes are considered to be part of the same package. Since they are part of the same package, class PackageDataTest is allowed to modify the package-access data of PackageData objects.

Figure 8.20. Package-access members of a class are accessible by other classes in the same package.

(This item is displayed on page 397 in the print version)

1

// Fig. 8

.20: PackageDataTest.java

2

//

Package-access members of a class are accessible by other classes

3

//

in the

same package.

4

 

 

 

5public class PackageDataTest

6{

7public static void main( String args[] )

8{

9PackageData packageData = new PackageData();

11// output String representation of packageData

12System.out.printf( "After instantiation:\n%s\n", packageData );

14// change package access data in packageData object

15packageData.number = 77;

16packageData.string = "Goodbye";

18// output String representation of packageData

19System.out.printf( "\nAfter changing values:\n%s\n", packageData );

20} // end main

21} // end class PackageDataTest

23// class with package access instance variables

24class PackageData

25{

26int number; // package-access instance variable

27String string; // package-access instance variable

29// constructor

30public PackageData()

31{

32number = 0;

33string = "Hello";

34} // end PackageData constructor

36// return PackageData object String representation

37public String toString()

38{

39return String.format( "number: %d; string: %s", number, string );

40} // end method toString

41} // end class PackageData