Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Java How to Program, Fourth Edition - Deitel H., Deitel P.pdf
Скачиваний:
63
Добавлен:
24.05.2014
Размер:
14.17 Mб
Скачать

Chapter 8

Object-Based Programming

411

8.9 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, well-defined, carefully tested, well-documented, portable, widely available components. This kind of software reusability speeds the development of powerful, high-quality software. Rapid applications 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 applications 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, a Java programmer concentrates on learning Java’s graphics capabilities (which are quite substantial and growing) and writes 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 interpreter 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 programmers’ ability to debug and maintain applications. To take advantage of Java’s many capabilities, it is essential that programmers take the time to 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/1.3/docs/api/index.html

In addition, java.sun.com provides many other resources, including tutorials, articles and sites specific to individual Java topics. Java developers should also register (for free) at the Java Developer Connection

developer.java.sun.com

This site provides additional resources that Java developers will find useful, including more tutorials and articles, and links to other Java resources. See Appendix B for a more complete list of Java-related Internet and World Wide Web resources.

Good Programming Practice 8.6

Avoid reinventing the wheel. Study the capabilities of the Java API. If the API already con- tains a class that meets the requirements of your program, use that class rather than creating your own.

In general, to realize the full potential of software reusability, we need to improve cataloging schemes, licensing schemes, protection mechanisms that ensure master copies of classes are not corrupted, description schemes that system designers use to determine if existing objects meet their needs, browsing mechanisms that determine what classes are

© Copyright 1992–2002 by Deitel & Associates, Inc. All Rights Reserved. 7/3/01

412

Object-Based Programming

Chapter 8

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 be solved because the potential value of software reuse is enormous.

8.10 Final Instance Variables

We have emphasized repeatedly the principle of least privilege as one of the most fundamental principles of good software engineering. Let us see one way in which this principle applies to instance variables.

Some instance variables need to be modifiable and some do not. The programmer can use the keyword final to specify that a variable is not modifiable and that any attempt to modify the variable is an error. For example,

private final int INCREMENT = 5;

declares a constant instance variable INCREMENT of type int and initializes it to 5.

Software Engineering Observation 8.17

Declaring an instance variable as final helps enforce the principle of least privilege. If an instance variable should not be modified, declare it to be final to expressly forbid modification.

Testing and Debugging Tip 8.3

Accidental attempts to modify a final instance variable are caught at compile time rather than causing execution-time errors. It is always preferable to get bugs out at compile time, if possible, rather than allowing them to slip through to execution time (where studies have found that the cost of repair is often as much as ten times more expensive).

Common Programming Error 8.9

Attempting to modify a final instance variable after it is initialized is a syntax error.

The applet of Fig. 8.11 creates a final instance variable INCREMENT of type int and initializes it to 5 in its declaration (line 15). A final variable cannot be modified by assignment after it is initialized. Such a variable can be initialized in its declaration or in every constructor of the class.

Common Programming Error 8.10

Not initializing a final instance variable in its declaration or in every constructor of the class is a syntax error.

1// Fig. 8.11: Increment.java

2 // Initializing a final variable

3

4 // Java core packages

5import java.awt.*;

6 import java.awt.event.*;

7

Fig. 8.11 Initializing a final variable (part 1 of 2).

© Copyright 1992–2002 by Deitel & Associates, Inc. All Rights Reserved. 7/3/01

Chapter 8

Object-Based Programming

413

8 // Java extension packages

9 import javax.swing.*;

10

11public class Increment extends JApplet

12implements ActionListener {

13

 

14

private int count = 0, total = 0;

15

private final int INCREMENT = 5; // constant variable

16

 

17

private JButton button;

18

 

19// set up GUI

20public void init()

21{

22Container container = getContentPane();

24button = new JButton( "Click to increment" );

25button.addActionListener( this );

26container.add( button );

27}

28

29// add INCREMENT to total when user clicks button

30public void actionPerformed( ActionEvent actionEvent )

31{

32total += INCREMENT;

33count++;

34showStatus( "After increment " + count +

35": total = " + total );

36}

37

38 } // end class Increment

Fig. 8.11 Initializing a final variable (part 2 of 2).

Figure 8.12 illustrates compiler errors produced for the program of Fig. 8.11 if instance variable INCREMENT is declared final, but is not initialized in the declaration.

Increment.java:11: variable INCREMENT might not have been initialized

public class Increment extends JApplet

^

1 error

Fig. 8.12 Compiler error message as a result of not initializing increment.

© Copyright 1992–2002 by Deitel & Associates, Inc. All Rights Reserved. 7/3/01

414

Object-Based Programming

Chapter 8

8.11 Composition: Objects as Instance Variables of Other

Classes

An AlarmClock class object needs to know when it is supposed to sound its alarm, so why not include a reference to a Time object as a member of the AlarmClock object? Such a capability is called composition. A class can have references to objects of other classes as members.

Software Engineering Observation 8.18

One form of software reuse is composition, in which a class has references to objects of other classes as members.

The next program contains three classes—Date (Fig. 8.13), Employee (Fig. 8.14) and EmployeeTest (Fig. 8.15). Class Employee contains instance variables firstName, lastName, birthDate and hireDate. Members birthDate and hireDate are references to Dates that contain instance variables month, day and year. This demonstrates that a class can contain references to objects of other classes. Class EmployeeTest instantiates an Employee and initializes and displays its instance variables. The Employee constructor (Fig. 8.14, lines 12–20) takes eight arguments—first, last, birthMonth, birthDay, birthYear, hireMonth, hireDay and hireYear. Arguments birthMonth, birthDay and birthYear are passed to the Date constructor (Fig. 8.13, lines 13–28) to initialize the birthDate object and hireMonth, hireDay and hireYear are passed to the Date constructor to initialize the hireDate object.

A member object does not need to be initialized immediately with constructor arguments. If an empty argument list is provided when a member object is created, the object’s default constructor (or no-argument constructor if one is available) will be called automatically. Values, if any, established by the default constructor (or no-argument constructor) can then be replaced by set methods.

Performance Tip 8.2

Initialize member objects explicitly at construction time by passing appropriate arguments to the constructors of the member objects. This eliminates the overhead of initializing member objects twice—once when the member object’s default constructor is called and again when set methods are used to provide initial values for the member object.

Note that both class Date (Fig. 8.13) and class Employee (Fig. 8.14) are defined as part of the package com.deitel.jhtp4.ch08 as specified on line 3 of each file. Because they are in the same package (i.e., the same directory), class Employee does not need to import class Date to use it. When the compiler searches for the file Date.class, the compiler knows to search the directory where Employee.class is located. Classes in a package never need to import other classes from the same package.

1// Fig. 8.13: Date.java

2 // Declaration of the Date class.

3 package com.deitel.jhtp4.ch08;

4

5 public class Date extends Object {

6private int month; // 1-12

Fig. 8.13 Date class (part 1 of 2).

© Copyright 1992–2002 by Deitel & Associates, Inc. All Rights Reserved. 7/3/01

Chapter 8

Object-Based Programming

415

 

 

 

 

7

private int day;

// 1-31 based on month

 

8

private int year;

// any year

 

9

 

 

 

10// Constructor: Confirm proper value for month;

11// call method checkDay to confirm proper

12// value for day.

13public Date( int theMonth, int theDay, int theYear )

14{

15if ( theMonth > 0 && theMonth <= 12 ) // validate month

16month = theMonth;

17else {

18

month = 1;

 

19

System.out.println( "Month " + theMonth +

20

" invalid. Set to month 1." );

21

}

 

22

 

 

23

year = theYear;

// could validate year

24

day = checkDay( theDay );

// validate day

25

 

 

26

System.out.println(

 

27"Date object constructor for date " + toString() );

28}

29

30// Utility method to confirm proper day value

31// based on month and year.

32private int checkDay( int testDay )

33{

34int daysPerMonth[] =

35

{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

36

 

37// check if day in range for month

38if ( testDay > 0 && testDay <= daysPerMonth[ month ] )

39

return testDay;

40

 

41// check for leap year

42if ( month == 2 && testDay == 29 &&

43

( year % 400

==

0

||

44

( year % 4

==

0

&& year % 100 != 0 ) ) )

45

return testDay;

 

 

 

46

 

 

 

 

47

System.out.println( "Day " + testDay +

48

" invalid. Set to

day 1." );

49

 

 

 

 

50return 1; // leave object in consistent state

51}

52

53// Create a String of the form month/day/year

54public String toString()

55{

56return month + "/" + day + "/" + year;

57}

58

59 } // end class Date

Fig. 8.13 Date class (part 2 of 2).

© Copyright 1992–2002 by Deitel & Associates, Inc. All Rights Reserved. 7/3/01

416

Object-Based Programming

Chapter 8

1// Fig. 8.14: Employee.java

2 // Definition of class Employee.

3 package com.deitel.jhtp4.ch08;

4

5 public class Employee extends Object {

6 private String firstName;

7 private String lastName;

8 private Date birthDate;

9 private Date hireDate;

10

11// constructor to initialize name, birth date and hire date

12public Employee( String first, String last,

13int birthMonth, int birthDay, int birthYear,

14int hireMonth, int hireDay, int hireYear )

15{

16firstName = first;

17lastName = last;

18birthDate = new Date( birthMonth, birthDay, birthYear );

19hireDate = new Date( hireMonth, hireDay, hireYear );

20}

21

22// convert Employee to String format

23public String toString()

24{

25return lastName + ", " + firstName +

26

" Hired: " + hireDate.toString() +

27" Birthday: " + birthDate.toString();

28}

29

30 } // end class Employee

Fig. 8.14 Employee class with member object references.

1// Fig. 8.15: EmployeeTest.java

2 // Demonstrating an object with a member object.

3

4// Java extension packages

5 import javax.swing.JOptionPane;

6

7// Deitel packages

8 import com.deitel.jhtp4.ch08.Employee;

9

10 public class EmployeeTest {

11

12// test class Employee

13public static void main( String args[] )

14{

15Employee employee = new Employee( "Bob", "Jones",

16

7, 24, 49, 3, 12, 88 );

17

 

Fig. 8.15 Demonstrating an object with a member object reference (part 1 of 2).

© Copyright 1992–2002 by Deitel & Associates, Inc. All Rights Reserved. 7/3/01

Chapter 8

Object-Based Programming

417

 

 

 

18

JOptionPane.showMessageDialog( null,

 

19

employee.toString(), "Testing Class Employee",

 

20

JOptionPane.INFORMATION_MESSAGE );

 

21

 

 

22System.exit( 0 );

23}

24

25 } // end class EmployeeTest

Date object constructor for date 7/24/1949

Date object constructor for date 3/12/1988

Fig. 8.15 Demonstrating an object with a member object reference (part 2 of 2).

8.12 Package Access

When no member access modifier is provided for a method or variable when it is defined in a class, the method or variable is considered to have package access. If your program consists of one class definition, this has no specific effects on the program. However, if your program uses multiple classes from the same package (i.e., a group of related classes), these classes can access each other’s package-access methods and data directly through a reference to an object.

Performance Tip 8.3

Package access enables objects of different classes to interact without the need for set and get methods that provide access to data, thus eliminating some of the method call overhead.

Let us consider a mechanical example of package access. The application of Fig. 8.16 contains two classes—the PackageDataTest application class (lines 8–34) and the PackageData class (lines 37–54). In the PackageData class definition, lines 38–39 declare the instance variables number and string with no member access modifiers; therefore, these are package access instance variables. The PackageDataTest application’s main method creates an instance of the PackageData class (line 13) to demonstrate the ability to modify the PackageData instance variables directly (as shown on lines 20–21). The results of the modification can be seen in the output window.

When you compile this program, the compiler produces two separate files—a .class file for class PackageData and a .class file for class PackageDataTest. Every Java class has its own .class file. These two .class files are placed in the same directory by the compiler automatically and are considered to be part of the same package (they are certainly related by the fact that they are in the same file). Because they are part of the same package, class PackageDataTest is allowed to modify the package access data of objects of class PackageData.

© Copyright 1992–2002 by Deitel & Associates, Inc. All Rights Reserved. 7/3/01

418

Object-Based Programming

Chapter 8

1// Fig. 8.16: PackageDataTest.java

2// Classes in the same package (i.e., the same directory) can

3 // use package access data of other classes in the same package.

4

5// Java extension packages

6 import javax.swing.JOptionPane;

7

8 public class PackageDataTest {

9

10// Java extension packages

11public static void main( String args[] )

12{

13PackageData packageData = new PackageData();

15// append String representation of packageData to output

16String output =

17

"After instantiation:\n" + packageData.toString();

18

 

19// change package access data in packageData object

20packageData.number = 77;

21packageData.string = "Good bye";

22

23// append String representation of packageData to output

24output += "\nAfter changing values:\n" +

25

packageData.toString();

26

 

27

JOptionPane.showMessageDialog( null, output,

28

"Demonstrating Package Access",

29

JOptionPane.INFORMATION_MESSAGE );

30

 

31System.exit( 0 );

32}

33

34 } // end class PackageDataTest

35

36// class with package access instance variables

37class PackageData {

38

int number;

//

package access instance variable

39

String string;

//

package access instance variable

40

 

 

 

41// constructor

42public PackageData()

43{

44number = 0;

45string = "Hello";

46}

47

48// convert PackageData object to String representation

49public String toString()

50{

51return "number: " + number + " string: " + string;

52}

53

Fig. 8.16 Package access to members of a class (part 1 of 2).

© Copyright 1992–2002 by Deitel & Associates, Inc. All Rights Reserved. 7/3/01

Chapter 8

Object-Based Programming

419

54 } // end class PackageData

Fig. 8.16 Package access to members of a class (part 2 of 2).

Software Engineering Observation 8.19

Some people in the OOP community feel that package access corrupts information hiding and weakens the value of the object-oriented design approach, because the programmer must assume responsibility for error checking and data validation in any code that manipulates the package access data members.

8.13 Using the this Reference

When a method of a class references another member of that class for a specific object of that class, how does Java ensure that the proper object is referenced? The answer is that each object has access to a reference to itself—called the this reference.

The this reference is used implicitly to refer to both the instance variables and methods of an object. We begin with a simple example of using the this reference explicitly; a subsequent example shows some substantial and subtle examples of using this.

Performance Tip 8.4

Java conserves storage by maintaining only one copy of each method per class; this method is invoked by every object of that class. Each object, on the other hand, has its own copy of the class’s instance variables.

The application of Fig. 8.17 demonstrates implicit and explicit use of the this reference to enable the main method of class ThisTest to display the private data of a

SimpleTime object.

1// Fig. 8.17: ThisTest.java

2 // Using the this reference to refer to

3 // instance variables and methods.

4

5// Java core packages

6 import java.text.DecimalFormat;

7

8 // Java extension packages

9 import javax.swing.*;

10

Fig. 8.17 Using the this reference implicitly and explicitly (part 1 of 3).

© Copyright 1992–2002 by Deitel & Associates, Inc. All Rights Reserved. 7/3/01

420

Object-Based Programming

Chapter 8

11 public class ThisTest {

12

13// test class SimpleTime

14public static void main( String args[] )

15{

16SimpleTime time = new SimpleTime( 12, 30, 19 );

18

JOptionPane.showMessageDialog( null, time.buildString(),

19

"Demonstrating the \"this\" Reference",

20

JOptionPane.INFORMATION_MESSAGE );

21

 

22System.exit( 0 );

23}

24

25 } // end class ThisTest

26

27// class SimpleTime demonstrates the "this" reference

28class SimpleTime {

29private int hour, minute, second;

30

31// constructor uses parameter names identical to instance

32// variable names, so "this" reference required to distinguish

33// between instance variables and parameters

34public SimpleTime( int hour, int minute, int second )

35{

36

this.hour = hour;

// set "this" object's hour

37this.minute = minute; // set "this" object's minute

38this.second = second; // set "this" object's second

39}

40

41// call toString explicitly via "this" reference, explicitly

42// via implicit "this" reference, implicitly via "this"

43public String buildString()

44{

45return "this.toString(): " + this.toString() +

46

"\ntoString(): " + toString() +

47"\nthis (with implicit toString() call): " + this;

48}

49

50// convert SimpleTime to String format

51public String toString()

52{

53DecimalFormat twoDigits = new DecimalFormat( "00" );

55// "this" not required, because toString does not have

56// local variables with same names as instance variables

57return twoDigits.format( this.hour ) + ":" +

58

twoDigits.format( this.minute ) + ":" +

59twoDigits.format( this.second );

60}

61

62 } // end class SimpleTime

Fig. 8.17 Using the this reference implicitly and explicitly (part 2 of 3).

© Copyright 1992–2002 by Deitel & Associates, Inc. All Rights Reserved. 7/3/01

Chapter 8

Object-Based Programming

421

 

 

 

 

 

 

Fig. 8.17 Using the this reference implicitly and explicitly (part 3 of 3).

Class SimpleTime (lines 8–60) defines three private instance variables—hour, minute and second. The constructor (lines 34–39) receives three int arguments to initialize a SimpleTime object. Notice that the parameter names for the constructor are the same as the instance variable names. Remember that a local variable of a method with the same name as an instance variable of a class hides the instance variable in the scope of the method. For this reason, we use the this reference to refer explicitly to the instance variables on lines 36–38.

Common Programming Error 8.11

In a method in which a method parameter has the same name as one of the class members, use this explicitly if you want to access the class member; otherwise, you will incorrectly reference the method parameter.

Good Programming Practice 8.7

Avoid using method parameter names that conflict with class member names.

Method buildString (lines 43–48) returns a String created with a statement that uses the this reference three ways. Line 45 explicitly invokes the class’s toString method via this.toString(). Line 46 implicitly uses the this reference to perform the same task. Line 47 appends this to the string that will be returned. Remember that the this reference is a reference to an object—the current SimpleTime object being manipulated. As before, any reference added to a String results in a call to the toString method for the referenced object. Method buildString is invoked at line 18 to display the results of the three calls to toString. Note that the same time is displayed on all three lines of the output because all three calls to toString are for the same object.

Another use of the this reference is in enabling concatenated method calls (also called cascaded method calls or method call chaining). Figure 8.18 illustrates returning a reference to a Time4 object to enable method calls of class Time4 to be concatenated. Methods setTime (lines 50–57), setHour (lines 60–65), setMinute (line 68–74) and setSecond (lines 77–83) each have a return type of Time4 and each has as its last statement

return this;

to indicate that a reference to the Time4 object being manipulated should be returned to the caller of the method.

© Copyright 1992–2002 by Deitel & Associates, Inc. All Rights Reserved. 7/3/01

422

Object-Based Programming

Chapter 8

1 // Fig. 8.18: Time4.java

2// Time4 class definition

3 package com.deitel.jhtp4.ch08;

4

5// Java core packages

6 import java.text.DecimalFormat;

7

8public class Time4 extends Object {

9

private int hour;

// 0

- 23

10

private

int

minute;

//

0

-

59

11

private

int

second;

//

0

-

59

12

 

 

 

 

 

 

 

13// Time4 constructor initializes each instance variable

14// to zero. Ensures that Time object starts in a

15// consistent state.

16public Time4()

17{

18this.setTime( 0, 0, 0 );

19}

20

21// Time4 constructor: hour supplied, minute and second

22// defaulted to 0

23public Time4( int hour )

24{

25this.setTime( hour, 0, 0 );

26}

27

28// Time4 constructor: hour and minute supplied, second

29// defaulted to 0

30public Time4( int hour, int minute )

31{

32this.setTime( hour, minute, 0 );

33}

34

35// Time4 constructor: hour, minute and second supplied

36public Time4( int hour, int minute, int second )

37{

38this.setTime( hour, minute, second );

39}

40

41// Time4 constructor: another Time4 object supplied.

42public Time4( Time4 time )

43{

44this.setTime( time.getHour(), time.getMinute(),

45time.getSecond() );

46}

47

48// Set Methods

49// set a new Time value using universal time

50public Time4 setTime( int hour, int minute, int second )

51{

52

this.setHour( hour );

// set hour

Fig. 8.18 Class Time4 using this to enable chained method calls (part 1 of 3).

© Copyright 1992–2002 by Deitel & Associates, Inc. All Rights Reserved. 7/3/01

Chapter 8

Object-Based Programming

423

53this.setMinute( minute ); // set minute

54this.setSecond( second ); // set second

56return this; // enables chaining

57}

58

59// validate and set hour

60public Time4 setHour( int hour )

61{

62this.hour = ( hour >= 0 && hour < 24 ? hour : 0 );

64return this; // enables chaining

65}

66

67// validate and set minute

68public Time4 setMinute( int minute )

69{

70this.minute =

71

( minute >= 0 && minute < 60 ) ? minute : 0;

72

 

73return this; // enables chaining

74}

75

76// validate and set second

77public Time4 setSecond( int second )

78{

79this.second =

80

( second >= 0 && second < 60 ) ? second : 0;

81

 

82return this; // enables chaining

83}

84

85// Get Methods

86// get value of hour

87public int getHour() { return this.hour; }

89// get value of minute

90public int getMinute() { return this.minute; }

92// get value of second

93public int getSecond() { return this.second; }

95// convert to String in universal-time format

96public String toUniversalString()

97{

98DecimalFormat twoDigits = new DecimalFormat( "00" );

100 return twoDigits.format( this.getHour() ) + ":" +

101 twoDigits.format( this.getMinute() ) + ":" +

102twoDigits.format( this.getSecond() );

103}

104

Fig. 8.18 Class Time4 using this to enable chained method calls (part 2 of 3).

© Copyright 1992–2002 by Deitel & Associates, Inc. All Rights Reserved. 7/3/01

424

Object-Based Programming

Chapter 8

105// convert to String in standard-time format

106public String toString()

107{

108DecimalFormat twoDigits = new DecimalFormat( "00" );

110 return ( this.getHour() == 12 || this.getHour() == 0 ? 111 12 : this.getHour() % 12 ) + ":" +

112 twoDigits.format( this.getMinute() ) + ":" +

113 twoDigits.format( this.getSecond() ) +

114( this.getHour() < 12 ? " AM" : " PM" );

115}

116

117 } // end class Time4

Fig. 8.18 Class Time4 using this to enable chained method calls (part 3 of 3).

The example again demonstrates the explicit use of the this reference inside the body of a class. In class Time4, every use of an instance variable of the class and every call to another method in class Time4 uses the this reference explicitly. Most programmers prefer not to use the this reference unless it is required or helps clarify a piece of code.

Good Programming Practice 8.8

Explicitly using this can increase program clarity in some contexts in which this is optional.

In application class TimeTest6 (Fig. 8.19), line 18 and line 26 both demonstrate method call chaining. Why does the technique of returning the this reference work? Let us discuss line 18. The dot operator (.) associates from left to right, so the expression

time.setHour( 18 ).setMinute( 30 ).setSecond( 22 );

first evaluates time.setHour( 18 ), then returns a reference to object time as the result of this method call. Any time you have a reference in a program (even as the result of a method call), the reference can be followed by a dot operator and a call to one of the methods of the reference type. Thus, the remaining expression is interpreted as

time.setMinute( 30 ).setSecond( 22 );

The time.setMinute( 30 ) call executes and returns a reference to time. The remaining expression is interpreted as

time.setSecond( 22 );

When the statement is complete, the time is 18 for the hour, 30 for the minute and 22 from the second.

Note that the calls on line 26

time.setTime( 20, 20, 20 ).toString();

also use the concatenation feature. These method calls must appear in this order in this expression because toString as defined in the class does not return a reference to a Time4 object. Placing the call to toString before the call to setTime causes a syntax error. Note that toString returns a reference to a String object. Therefore, a method of class String could be concatenated to the end of line 26.

© Copyright 1992–2002 by Deitel & Associates, Inc. All Rights Reserved. 7/3/01

Chapter 8

Object-Based Programming

425

1// Fig. 8.19: TimeTest6.java

2 // Chaining method calls together with the this reference

3

4 // Java extension packages

5 import javax.swing.*;

6

7// Deitel packages

8 import com.deitel.jhtp4.ch08.Time4;

9

10 public class TimeTest6 {

11

12// test method call chaining with object of class Time4

13public static void main( String args[] )

14{

15Time4 time = new Time4();

16

17// chain calls to setHour, setMinute and setSecond

18time.setHour( 18 ).setMinute( 30 ).setSecond( 22 );

20// use method call chaining to set new time and get

21// String representation of new time

22String output =

23

"Universal time: " + time.toUniversalString() +

24

"\nStandard time: " + time.toString() +

25

"\n\nNew standard time: " +

26

time.setTime( 20, 20, 20 ).toString();

27

 

28

JOptionPane.showMessageDialog( null, output,

29

"Chaining Method Calls",

30

JOptionPane.INFORMATION_MESSAGE );

31

 

32System.exit( 0 );

33}

34

35 } // end class TimeTest6

Fig. 8.19 Concatenating method calls.

Note that the purpose of the example in Fig. 8.18 and Fig. 8.19 is to demonstrate the mechanics of concatenated method calls. Many Java methods return references to objects that can be used in the manner shown here. It is important to understand concatenated method calls as they appear frequently in Java programs.

Good Programming Practice 8.9

For program clarity, avoid using concatenated method calls.

© Copyright 1992–2002 by Deitel & Associates, Inc. All Rights Reserved. 7/3/01

426

Object-Based Programming

Chapter 8

8.14 Finalizers

We have seen that constructor methods are capable of initializing data in an object of a class when the class is created. Often, constructors acquire various system resources such as memory (when the new operator is used). We need a disciplined way to give resources back to the system when they are no longer needed to avoid resource leaks. The most common resource acquired by constructors is memory. Java performs automatic garbage collection of memory to help return memory back to the system. When an object is no longer used in the program (i.e., there are no references to the object), the object is marked for garbage collection. The memory for such an object can be reclaimed when the garbage collector executes. Therefore, memory leaks that are common in other languages like C and C++ (because memory is not automatically reclaimed in those languages) are less likely to happen in Java. However, other resource leaks can occur.

Performance Tip 8.5

Extensive use of local variables that refer to objects can degrade performance. When a local variable is the only reference to an object, the object is marked for garbage collection as the local variable goes out of scope. If this happens frequently in short time periods, large numbers of objects could be marked for garbage collection, thus placing a performance burden on the garbage collector.

Every class in Java can have a finalizer method that returns resources to the system. The finalizer method for an object is guaranteed to be called to perform termination housekeeping on the object just before the garbage collector reclaims the memory for the object. A class’s finalizer method always has the name finalize, receives no parameters and returns no value (i.e., its return type is void). A class should have only one finalize method that takes no arguments. Method finalize is defined originally in class Object as a placeholder that does nothing. This guarantees that every class has a finalize method for the garbage collector to call.

Good Programming Practice 8.10

The last statement in a finalize method should always be super.finalize(); to en- sure that the superclass’s finalize method is called.

Software Engineering Observation 8.20

The garbage collector is not guaranteed to execute; therefore, an object’s finalize meth- od is not guaranteed to get called. You should not architect classes that rely on the garbage collector calling an object’s finalize method to deallocate resources.

Finalizers have not been provided for the classes presented so far. Actually, finalizers are rarely used in industrial Java applications. We will see a sample finalize method and discuss the garbage collector further in Figure 8.20.

Testing and Debugging Tip 8.4

Several professional Java developers who reviewed this book indicated that method finalize is not useful in industrial Java applications, because it is not guaranteed to get called. For this reason, you should search your Java programs for any use of method finalize to ensure that the program does not rely on calls to method finalize for proper deallocation of resources. In fact, you might consider removing method finalize entirely and

© Copyright 1992–2002 by Deitel & Associates, Inc. All Rights Reserved. 7/3/01

Chapter 8

Object-Based Programming

427

using other techniques to ensure proper resource deallocation. We present one such technique in Chapter 14, Exception Handling.

8.15 Static Class Members

Each object of a class has its own copy of all the instance variables of the class. In certain cases, only one copy of a particular variable should be shared by all objects of a class. A static class variable is used for these and other reasons. A static class variable represents class-wide information—all objects of the class share the same piece of data. The declaration of a static member begins with the keyword static.

Let us motivate the need for static class-wide data with a video game example. Suppose we have a video game with Martians and other space creatures. Each Martian tends to be brave and willing to attack other space creatures when the Martian is aware that there are at least five Martians present. If there are fewer than five Martians present, each Martian becomes cowardly. So each Martian needs to know the martianCount. We could endow class Martian with martianCount as instance data. If we do this, then every Martian will have a separate copy of the instance data and every time we create a new Martian we will have to update the instance variable martianCount in every Martian. This wastes space with the redundant copies and wastes time in updating the separate copies. Instead, we declare martianCount to be static. This makes martianCount class-wide data. Every Martian can see the martianCount as if it were instance data of the Martian, but only one copy of the static martianCount is maintained by Java. This saves space. We save time by having the Martian constructor increment the static martianCount. Because there is only one copy, we do not have to increment separate copies of martianCount for each Martian object.

Performance Tip 8.6

Use static class variables to save storage when a single copy of the data will suffice.

Although static class variables may seem like global variables, static class variables have class scope. A class’s public static class members can be accessed through a reference to any object of that class, or they can be accessed through the class name by using the dot operator (e.g., Math.random()). A class’s private static class members can be accessed only through methods of the class. Actually, static class members exist even when no objects of that class exist—they are available as soon as the class is loaded into memory at execution time. To access a public static class member when no objects of the class exist, simply prefix the class name and the dot operator to the class member. To access a private static class member when no objects of the class exist, a public static method must be provided and the method must be called by prefixing its name with the class name and dot operator.

Our next program defines two classes—Employee (Fig. 8.20) and EmployeeTest (Fig. 8.21). Class Employee defines a private static class variable and a public static method. The class variable count (Fig. 8.20, line 6) is initialized to zero by default. Class variable count maintains a count of the number of objects of class Employee that have been instantiated and currently reside in memory. This includes objects that have already been marked for garbage collection but have not yet been reclaimed.

© Copyright 1992–2002 by Deitel & Associates, Inc. All Rights Reserved. 7/3/01

the number of Employee

428

Object-Based Programming

Chapter 8

1// Fig. 8.20: Employee.java

2// Employee class definition.

3 public class Employee extends Object {

4 private String firstName;

5private String lastName;

6

private static int count; // number of objects in memory

7

 

8// initialize employee, add 1 to static count and

9 // output String indicating that constructor was called

10public Employee( String first, String last )

11{

12firstName = first;

13lastName = last;

14

15++count; // increment static count of employees

16System.out.println( "Employee object constructor: " +

17firstName + " " + lastName );

18}

19

20// subtract 1 from static count when garbage collector

21// calls finalize to clean up object and output String

22// indicating that finalize was called

23protected void finalize()

24{

25--count; // decrement static count of employees

26System.out.println( "Employee object finalizer: " +

27firstName + " " + lastName + "; count = " + count );

28}

29

30// get first name

31public String getFirstName()

32{

33return firstName;

34}

35

36// get last name

37public String getLastName()

38{

39return lastName;

40}

41

42// static method to get static count value

43public static int getCount()

44{

45return count;

46}

47

48 } // end class Employee

Fig. 8.20 Employee class that uses a static class variable to maintain a count of objects in memory.

When objects of class Employee exist, member count can be used in any method of an Employee object—in this example, count is incremented (line 15) by the con-

© Copyright 1992–2002 by Deitel & Associates, Inc. All Rights Reserved. 7/3/01

Chapter 8

Object-Based Programming

429

structor and decremented (line 25) by the finalizer. When no objects of class Employee exist, member count can still be referenced, but only through a call to public static method getCount (lines 43–46), as in:

Employee.getCount()

which determines the number of Employee objects currently in memory. Note that when there are no objects instantiated in the program, the Employee.getCount() method call is issued. However, when there are objects instantiated, method getCount can also be called through a reference to one of the objects, as in

e1.getCount()

Good Programming Practice 8.11

Always invoke static methods by using the class name and the dot operator (.). This em- phasizes to other programmers reading your code that the method being called is a static method.

Notice that the Employee class has a finalize method (lines 23–28). This method is included to show when it is called by the garbage collector in a program. Method finalize normally is declared protected so it is not part of the public services of a class. We will discuss the protected access modifier in detail in Chapter 9.

Method main of the EmployeeTest application class (Fig. 8.21) instantiates two Employee objects (lines 16–17). When each Employee object’s constructor is invoked, lines 12–13 of Fig. 8.20 store references to that Employee’s first name and last name String objects. Note that these two statements do not make copies of the original Strings arguments. Actually, String objects in Java are immutable—they cannot be modified after they are created (class String does not provide any set methods). Because a reference cannot be used to modify a String, it is safe to have many references to one String object in a Java program. This is not normally the case for most other classes in Java. If Java String objects are immutable, why are we able to use the + and += operators to concatenate Strings? As we discuss in Chapter 10, Strings and Characters, the String concatenation operations actually result in the creation of new String objects containing the concatenated values. The original String objects actually are not modified.

When main is done with the two Employee objects, the references e1 and e2 are set to null at lines 37–38. At this point references e1 and e2 no longer refer to the objects that were instantiated on lines 16–17. This marks the objects for garbage collection because there are no more references to the objects in the program.

1// Fig. 8.21: EmployeeTest.java

2 // Test Employee class with static class variable,

3 // static class method, and dynamic memory.

4 import javax.swing.*;

5

Fig. 8.21 Using a static class variable to maintain a count of the number of objects of a class (part 1 of 3).

© Copyright 1992–2002 by Deitel & Associates, Inc. All Rights Reserved. 7/3/01

430

Object-Based Programming

Chapter 8

6 public class EmployeeTest {

7

8// test class Employee

9public static void main( String args[] )

10{

11// prove that count is 0 before creating Employees

12String output = "Employees before instantiation: " +

13

Employee.getCount();

14

 

15// create two Employees; count should be 2

16Employee e1 = new Employee( "Susan", "Baker" );

17Employee e2 = new Employee( "Bob", "Jones" );

19// Prove that count is 2 after creating two Employees.

20// Note: static methods should be called only via the

21// class name for the class in which they are defined.

22output += "\n\nEmployees after instantiation: " +

23

"\nvia e1.getCount(): " +

e1.getCount() +

24

"\nvia e2.getCount(): " +

e2.getCount() +

25

"\nvia Employee.getCount(): " + Employee.getCount();

26

 

 

27// get names of Employees

28output += "\n\nEmployee 1: " + e1.getFirstName() +

29

" " + e1.getLastName() + "\nEmployee 2: " +

30

e2.getFirstName() + " " + e2.getLastName();

31

 

32// If there is only one reference to each employee (as

33// on this example), the following statements mark

34// those objects for garbage collection. Otherwise,

35// these statement simply decrement the reference count

36// for each object.

37e1 = null;

38e2 = null;

39

40 System.gc(); // suggest call to garbage collector

41

42// Show Employee count after calling garbage collector.

43// Count displayed may be 0, 1 or 2 depending on

44// whether garbage collector executed immediately and

45// number of Employee objects it collects.

46output += "\n\nEmployees after System.gc(): " +

47

Employee.getCount();

48

 

49

JOptionPane.showMessageDialog( null, output,

50

"Static Members and Garbage Collection",

51

JOptionPane.INFORMATION_MESSAGE );

52

 

53System.exit( 0 );

54}

55

56 } // end class EmployeeTest

Fig. 8.21 Using a static class variable to maintain a count of the number of objects of a class (part 2 of 3).

© Copyright 1992–2002 by Deitel & Associates, Inc. All Rights Reserved. 7/3/01

Chapter 8

Object-Based Programming

431

 

 

 

 

 

 

Employee object constructor: Susan Baker

Employee object constructor: Bob Jones

Employee object finalizer: Susan Baker; count = 1

Employee object finalizer: Bob Jones; count = 0

Fig. 8.21 Using a static class variable to maintain a count of the number of objects of a class (part 3 of 3).

Eventually, the garbage collector reclaims the memory for these objects (or the memory is reclaimed by the operating system when the program terminates). Because it is not guaranteed when the garbage collector will execute, we make an explicit call to the garbage collector with line 40, which uses public static method gc from class System (java.lang package) to indicate that the garbage collector immediately should make a best effort attempt to collect objects that have been marked for garbage collection. However, this is just a best effort—it is possible that no objects or a subset of the garbage objects will be collected. In our example, the garbage collector did execute before lines 49–51 displayed the results of the program. The last line of the output indicates that the number of Employee objects in memory is 0 after the call to System.gc(). Also, the last two lines of the command window output show that the Employee object for Susan Baker was finalized before the Employee object for Bob Jones. Remember, the garbage collector is not guaranteed to execute when System.gc() is invoked nor is it guaranteed to collect objects in a specific order, so it is possible that the output of this program on your system may differ.

[Note: A method declared static cannot access nonstatic class members. Unlike nonstatic methods, a static method has no this reference because, static class variables and static class methods exist independent of any objects of a class and before any objects of the class have been instantiated.]

Common Programming Error 8.12

Referring to the this reference in a static method is a syntax error.

Common Programming Error 8.13

It is a syntax error for a static method to call an instance method or to access an instance variable.

© Copyright 1992–2002 by Deitel & Associates, Inc. All Rights Reserved. 7/3/01