As you know, you can declare your own constructor to specify how objects of a class should be initialized. Next, we demonstrate a class with several overloaded constructors that enable objects of that class to be initialized in different ways. To overload constructors, simply provide multiple constructor declarations with different signatures. Recall from Section 6.12 that the compiler differentiates signatures by the number and types of the parameters in each signature.
Class Time2 with Overloaded Constructors
The default constructor for class Time1 (Fig. 8.1) initialized hour, minute and second to their default 0 values (which is midnight in universal time). The default constructor does not enable the class's clients to initialize the time with specific non-zero values. Class Time2 (Fig. 8.5) contains five overloaded constructors that provide convenient ways to initialize objects of the new class Time2. Every object each constructor initializes begins in a consistent state. In this program, four of the constructors invoke a fifth constructor, which, in turn, calls method setTime to ensure that the value supplied for hour is in the range 0 to 23 and that the values for minute and second are each in the range 0 to 59. If a value is out of range, it is set to zero by setTime (once again ensuring that each instance variable remains in a consistent state). The compiler invokes the appropriate constructor by matching the number and types of the arguments specified in the constructor call with the number and types of the parameters specified in each constructor declaration. Note that class Time2 also provides set and get methods for each instance variable.
Figure 8.5. Time2 class with overloaded constructors.
(This item is displayed on pages 366 - 368 in the print version)
1 // Fig. 8.5: Time2.java 2 // Time2 class declaration with overloaded constructors. 3 4 public class Time2 5 { 6 private int hour; // 0 - 23 7 private int minute; // 0 - 59 8 private int second; // 0 - 59 9 10 // Time2 no-argument constructor: initializes each instance variable 11 // to zero; ensures that Time2 objects start in a consistent state 12 public Time2() 13 { 14 this( 0, 0, 0 ); // invoke Time2 constructor with three arguments 15 } // end Time2 no-argument constructor 16 17 // Time2 constructor: hour supplied, minute and second defaulted to 0 18 public Time2( int h ) 19 { 20 this ( h, 0, 0 ); // invoke Time2 constructor with three arguments 21 } // end Time2 one-argument constructor 22 23 // Time2 constructor: hour and minute supplied, second defaulted to 0 24 public Time2( int h, int m ) 25 { 26 this( h, m, 0 ); // invoke Time2 constructor with three arguments 27 } // end Time2 two-argument constructor 28 29 // Time2 constructor: hour, minute and second supplied 30 public Time2( int h, int m, int s ) 31 { 32 setTime( h, m, s ); // invoke setTime to validate time 33 } // end Time2 three-argument constructor 34 35 // Time2 constructor: another Time2 object supplied 36 public Time2( Time2 time ) 37 { 38 // invoke Time2 three-argument constructor 39 this( time.getHour(), time.getMinute(), time.getSecond() ); 40 } // end Time2 constructor with a Time2 object argument 41 42 // Set Methods 43 // set a new time value using universal time; ensure that 44 // the data remains consistent by setting invalid values to zero 45 public void setTime( int h, int m, int s ) 46 { 47 setHour( h ); // set the hour 48 setMinute( m ); // set the minute 49 setSecond( s ); // set the second 50 } // end method setTime 51 52 // validate and set hour 53 public void setHour( int h ) 54 { 55 hour = ( ( h >= 0 && h < 24 ) ? h : 0 ); 56 } // end method setHour 57 58 // validate and set minute 59 public void setMinute( int m ) 60 { 61 minute = ( ( m >= 0 && m < 60 ) ? m : 0 ); 62 } // end method setMinute 63 64 // validate and set second 65 public void setSecond( int s ) 66 { 67 second = ( ( s >= 0 && s < 60 ) ? s : 0 ); 68 } // end method setSecond 69 70 // Get Methods 71 // get hour value 72 public int getHour() 73 { 74 return hour; 75 } // end method getHour 76 77 // get minute value 78 public int getMinute() 79 { 80 return minute; 81 } // end method getMinute 82 83 // get second value 84 public int getSecond() 85 { 86 return second; 87 } // end method getSecond 88 89 // convert to String in universal-time format (HH:MM:SS) 90 public String toUniversalString() 91 { 92 return String.format( 93 "%02d:%02d:%02d", getHour(), getMinute(), getSecond() ); 94 } // end method toUniversalString 95 96 // convert to String in standard-time format (H:MM:SS AM or PM) 97 public String toString() 98 { 99 return String.format( "%d:%02d:%02d %s", 100 ( (getHour() == 0 || getHour() == 12) ? 12 : getHour() % 12 ), 101 getMinute(), getSecond(), ( getHour() < 12 ? "AM" : "PM" ) ); 102 } // end method toString 103 } // end class Time2 |
Class Time2's Constructors
Lines 1215 declare a so-called no-argument constructorthat is, a constructor invoked without arguments. Such a constructor simply initializes the object as specified in the constructor's body. In the body, we introduce a use of the this reference that is allowed only as the first statement in a constructor's body. Line 14 uses this in method-call syntax to invoke the Time2 constructor that takes three arguments (lines 3033). The no-argument constructor passes values of 0 for the hour, minute and second to the constructor with three parameters. Using the this reference as shown here is a popular way to reuse initialization code provided by another of the class's constructors rather than defining similar code in the no-argument constructor's body. We use this syntax in four of the five Time2 constructors to make the class easier to maintain and modify. If we need to change how objects of class Time2 are initialized, only the constructor that the class's other constructors call would need to be modified. In fact, even that constructor might not need modification in this example. That constructor simply calls the setTime method to perform the actual initialization, so it is possible that the changes the class might require would be localized to the set methods.
Common Programming Error 8.3
It is a syntax error when this is used in a constructor's body to call another constructor of the same class if that call is not the first statement in the constructor. It is also a syntax error when a method attempts to invoke a constructor directly via this. |
Lines 1821 declare a Time2 constructor with a single int parameter representing the hour, which is passed with 0 for the minute and second to the constructor at lines 3033. Lines 2427 declare a Time2 constructor that receives two int parameters representing the hour and minute, which are passed with 0 for the second to the constructor at lines 3033. Like the no-argument constructor, each of these constructors invokes the constructor at lines 3033 to minimize code duplication. Lines 3033 declare the Time2 constructor that receives three int parameters representing the hour, minute and second. This constructor calls setTime to initialize the instance variables to consistent values.
Common Programming Error 8.4
A constructor can call methods of the class. Be aware that the instance variables might not yet be in a consistent state, because the constructor is in the process of initializing the object. Using instance variables before they have been initialized properly is a logic error. |
Lines 3640 declare a Time2 constructor that receives a Time2 reference to another Time2 object. In this case, the values from the Time2 argument are passed to the three-argument constructor at lines 3033 to initialize the hour, minute and second. Note that line 39 could have directly accessed the hour, minute and second values of the constructor's argument time with the expressions time.hour, time.minute and time.secondeven though hour, minute and second are declared as private variables of class Time2. This is due to a special relationship between objects of the same class.
Software Engineering Observation 8.4
When one object of a class has a reference to another object of the same class, the first object can access all the second object's data and methods (including those that are private). |
Notes Regarding Class Time2's Set and Get Methods and Constructors
Note that Time2's set and get methods are called throughout the body of the class. In particular, method setTime calls methods setHour, setMinute and setSecond in lines 4749, and methods toUniversalString and toString call methods getHour, getMinute and getSecond in line 93 and lines 100101, respectively. In each case, these methods could have accessed the class's private data directly without calling the set and get methods. However, consider changing the representation of the time from three int values (requiring 12 bytes of memory) to a single int value representing the total number of seconds that have elapsed since midnight (requiring only 4 bytes of memory). If we make such a change, only the bodies of the methods that access the private data directly would need to changein particular, the individual set and get methods for the hour, minute and second. There would be no need to modify the bodies of methods setTime, toUniversalString or toString because they do not access the data directly. Designing the class in this manner reduces the likelihood of programming errors when altering the class's implementation.
Similarly, each Time2 constructor could be written to include a copy of the appropriate statements from method setTime. Doing so may be slightly more efficient, because the extra constructor call and call to setTime are eliminated. However, duplicating statements in multiple methods or constructors makes changing the class's internal data representation more difficult. Having the Time2 constructors call the constructor with three arguments (or even call setTime directly) requires any changes to the implementation of setTime to be made only once.
Software Engineering Observation 8.5
When implementing a method of a class, use the class's set and get methods to access the class's private data. This simplifies code maintenance and reduces the likelihood of errors. |
Using Class Time2's Overloaded Constructors
Class Time2Test (Fig. 8.6) creates six Time2 objects (lines 813) to invoke the overloaded Time2 constructors. Line 8 shows that the no-argument constructor (lines 1215 of Fig. 8.5) is invoked by placing an empty set of parentheses after the class name when allocating a Time2 object with new. Lines 913 of the program demonstrate passing arguments to the other Time2 constructors. Java invokes the appropriate overloaded constructor by matching the number and types of the arguments specified in the constructor call with the number and types of the parameters specified in each constructor declaration. Line 9 invokes the constructor at lines 1821 of Fig. 8.5. Line 10 invokes the constructor at lines 2427 of Fig. 8.5. Lines 1112 invoke the constructor at lines 3033 of Fig. 8.5. Line 13 invokes the constructor at lines 3640 of Fig. 8.5. The application displays the String representation of each initialized Time2 object to confirm that it was initialized properly.
Figure 8.6. Overloaded constructors used to initialize Time2 objects.
(This item is displayed on pages 370 - 371 in the print version)
1 // Fig. 8.6: Time2Test.java 2 // Overloaded constructors used to initialize Time2 objects. 3 4 public class Time2Test 5 { 6 public static void main( String args[] ) 7 { 8 Time2 t1 = new Time2(); // 00:00:00 9 Time2 t2 = new Time2( 2 ); // 02:00:00 10 Time2 t3 = new Time2( 21, 34 ); // 21:34:00 11 Time2 t4 = new Time2( 12, 25, 42 ); // 12:25:42 12 Time2 t5 = new Time2( 27, 74, 99 ); // 00:00:00 13 Time2 t6 = new Time2( t4 ); // 12:25:42 14 15 System.out.println( "Constructed with:" ); 16 System.out.println( "t1: all arguments defaulted" ); 17 System.out.printf( " %s ", t1.toUniversalString() ); 18 System.out.printf( " %s ", t1.toString() ); 19 20 System.out.println( 21 "t2: hour specified; minute and second defaulted" ); 22 System.out.printf( " %s ", t2.toUniversalString() ); 23 System.out.printf( " %s ", t2.toString() ); 24 25 System.out.println( 26 "t3: hour and minute specified; second defaulted" ); 27 System.out.printf( " %s ", t3.toUniversalString() ); 28 System.out.printf( " %s ", t3.toString() ); 29 30 System.out.println( "t4: hour, minute and second specified" ); 31 System.out.printf( " %s ", t4.toUniversalString() ); 32 System.out.printf( " %s ", t4.toString() ); 33 34 System.out.println( "t5: all invalid values specified" ); 35 System.out.printf( " %s ", t5.toUniversalString() ); 36 System.out.printf( " %s ", t5.toString() ); 37 38 System.out.println( "t6: Time2 object t4 specified" ); 39 System.out.printf( " %s ", t6.toUniversalString() ); 40 System.out.printf( " %s ", t6.toString() ); 41 } // end main 42 } // end class Time2Test
|
Introduction to Computers, the Internet and the World Wide Web
Introduction to Java Applications
Introduction to Classes and Objects
Control Statements: Part I
Control Statements: Part 2
Methods: A Deeper Look
Arrays
Classes and Objects: A Deeper Look
Object-Oriented Programming: Inheritance
Object-Oriented Programming: Polymorphism
GUI Components: Part 1
Graphics and Java 2D™
Exception Handling
Files and Streams
Recursion
Searching and Sorting
Data Structures
Generics
Collections
Introduction to Java Applets
Multimedia: Applets and Applications
GUI Components: Part 2
Multithreading
Networking
Accessing Databases with JDBC
Servlets
JavaServer Pages (JSP)
Formatted Output
Strings, Characters and Regular Expressions
Appendix A. Operator Precedence Chart
Appendix B. ASCII Character Set
Appendix C. Keywords and Reserved Words
Appendix D. Primitive Types
Appendix E. (On CD) Number Systems
Appendix F. (On CD) Unicode®
Appendix G. Using the Java API Documentation
Appendix H. (On CD) Creating Documentation with javadoc
Appendix I. (On CD) Bit Manipulation
Appendix J. (On CD) ATM Case Study Code
Appendix K. (On CD) Labeled break and continue Statements
Appendix L. (On CD) UML 2: Additional Diagram Types
Appendix M. (On CD) Design Patterns
Appendix N. Using the Debugger
Inside Back Cover