Because you can't do anything in Java without classes, you have already seen several classes at work. Unfortunately, many of these are quite anomalous in the Java scheme of things. Take, for example, the Math class. You have seen that you can use methods of the Math class, such as Math.random, without needing to know how they are implemented all you need to know is the name and parameters (if any). That is the point of encapsulation and will certainly be true of all classes. Unfortunately, the Math class only encapsulates functionality; it neither needs nor hides data. Because there is no data, you do not need to worry about making objects and initializing their instance fields there aren't any! In the next section, we look at a more typical class, the Date class. You will see how to construct objects and call methods of this class. Objects and Object VariablesTo work with objects, you first construct them and specify their initial state. Then you apply methods to the objects. In the Java programming language, you use constructors to construct new instances. A constructor is a special method whose purpose is to construct and initialize objects. Let us look at an example. The standard Java library contains a Date class. Its objects describe points in time, such as "December 31, 1999, 23:59:59 GMT". NOTE
Constructors always have the same name as the class name. Thus, the constructor for the Date class is called Date. To construct a Date object, you combine the constructor with the new operator, as follows: new Date() This expression constructs a new object. The object is initialized to the current date and time. If you like, you can pass the object to a method: System.out.println(new Date()); Alternatively, you can apply a method to the object that you just constructed. One of the methods of the Date class is the toString method. That method yields a string representation of the date. Here is how you would apply the toString method to a newly constructed Date object. String s = new Date().toString(); In these two examples, the constructed object is used only once. Usually, you will want to hang on to the objects that you construct so that you can keep using them. Simply store the object in a variable: Date birthday = new Date(); Figure 4-3 shows the object variable birthday that refers to the newly constructed object. Figure 4-3. Creating a new objectThere is an important difference between objects and object variables. For example, the statement Date deadline; // deadline doesn't refer to any object defines an object variable, deadline, that can refer to objects of type Date. It is important to realize that the variable deadline is not an object and, in fact, does not yet even refer to an object. You cannot use any Date methods on this variable at this time. The statement s = deadline.toString(); // not yet would cause a compile-time error. You must first initialize the deadline variable. You have two choices. Of course, you can initialize the variable with a newly constructed object: deadline = new Date(); Or you can set the variable to refer to an existing object: deadline = birthday; Now both variables refer to the same object (see Figure 4-4). Figure 4-4. Object variables that refer to the same objectIt is important to realize that an object variable doesn't actually contain an object. It only refers to an object. In Java, the value of any object variable is a reference to an object that is stored elsewhere. The return value of the new operator is also a reference. A statement such as Date deadline = new Date(); has two parts. The expression new Date() makes an object of type Date, and its value is a reference to that newly created object. That reference is then stored in the deadline variable. You can explicitly set an object variable to null to indicate that it currently refers to no object. deadline = null; . . . if (deadline != null) System.out.println(deadline); If you apply a method to a variable that holds null, then a runtime error occurs. birthday = null; String s = birthday.toString(); // runtime error! Variables are not automatically initialized to null. You must initialize them, either by calling new or by setting them to null. C++ NOTE
The GregorianCalendar Class of the Java LibraryIn the preceding examples, we used the Date class that is a part of the standard Java library. An instance of the Date class has a state, namely a particular point in time. Although you don't need to know this when you use the Date class, the time is represented by the number of milliseconds (positive or negative) from a fixed point, the so-called epoch, which is 00:00:00 UTC, January 1, 1970. UTC is the Coordinated Universal Time, the scientific time standard that is, for practical purposes, the same as the more familiar GMT or Greenwich Mean Time. But as it turns out, the Date class is not very useful for manipulating dates. The designers of the Java library take the point of view that a date description such as "December 31, 1999, 23:59:59" is an arbitrary convention, governed by a calendar. This particular description follows the Gregorian calendar, which is the calendar used in most places of the world. The same point in time would be described quite differently in the Chinese or Hebrew lunar calendars, not to mention the calendar used by your customers from Mars. NOTE
The library designers decided to separate the concerns of keeping time and attaching names to points in time. Therefore, the standard Java library contains two separate classes: the Date class, which represents a point in time, and the GregorianCalendar class, which expresses dates in the familiar calendar notation. In fact, the GregorianCalendar class extends a more generic Calendar class that describes the properties of calendars in general. In theory, you can extend the Calendar class and implement the Chinese lunar calendar or a Martian calendar. However, the standard library does not contain any calendar implementations besides the Gregorian calendar. Separating time measurement from calendars is good object-oriented design. In general, it is a good idea to use separate classes to express different concepts. The Date class has only a small number of methods that allow you to compare two points in time. For example, the before and after methods tell you if one point in time comes before or after another. if (today.before(birthday)) System.out.println("Still time to shop for a gift."); NOTE
The GregorianCalendar class has many more methods than the Date class. In particular, it has several useful constructors. The expression new GregorianCalendar() constructs a new object that represents the date and time at which the object was constructed. You can construct a calendar object for midnight on a specific date by supplying year, month, and day: new GregorianCalendar(1999, 11, 31) Somewhat curiously, the months are counted from 0. Therefore, 11 is December. For greater clarity, there are constants like Calendar.DECEMBER. new GregorianCalendar(1999, Calendar.DECEMBER, 31) You can also set the time: new GregorianCalendar(1999, Calendar.DECEMBER, 31, 23, 59, 59) Of course, you will usually want to store the constructed object in an object variable: GregorianCalendar deadline = new GregorianCalendar(. . .); The GregorianCalendar has encapsulated instance fields to maintain the date to which it is set. Without looking at the source code, it is impossible to know the representation that the class uses internally. But, of course, the point of encapsulation is that this doesn't matter. What matters are the methods that a class exposes. Mutator and Accessor MethodsAt this point, you are probably asking yourself: How do I get at the current day or month or year for the date encapsulated in a specific GregorianCalendar object? And how do I change the values if I am unhappy with them? You can find out how to carry out these tasks by looking at the on-line documentation or the API notes at the end of this section. We go over the most important methods in this section. The job of a calendar is to compute attributes, such as the date, weekday, month, or year, of a certain point in time. To query one of these settings, you use the get method of the GregorianCalendar class. To select the item that you want to get, you pass a constant defined in the Calendar class, such as Calendar.MONTH or Calendar.DAY_OF_WEEK: GregorianCalendar now = new GregorianCalendar(); int month = now.get(Calendar.MONTH); int weekday = now.get(Calendar.DAY_OF_WEEK); The API notes list all the constants that you can use. You change the state with a call to the set method: deadline.set(Calendar.YEAR, 2001); deadline.set(Calendar.MONTH, Calendar.APRIL); deadline.set(Calendar.DAY_OF_MONTH, 15); There is also a convenience method to set the year, month, and day with a single call: deadline.set(2001, Calendar.APRIL, 15); Finally, you can add a number of days, weeks, months, and so on, to a given calendar object. deadline.add(Calendar.MONTH, 3); // move deadline by 3 months If you add a negative number, then the calendar is moved backwards. There is a conceptual difference between the get method on the one hand and the set and add methods on the other hand. The get method only looks up the state of the object and reports on it. The set and add methods modify the state of the object. Methods that change instance fields are called mutator methods, and those that only access instance fields without modifying them are called accessor methods. C++ NOTE
A common convention is to prefix accessor methods with the prefix get and mutator methods with the prefix set. For example, the GregorianCalendar class has methods getTime and setTime that get and set the point in time that a calendar object represents. Date time = calendar.getTime(); calendar.setTime(time); These methods are particularly useful for converting between the GregorianCalendar and Date classes. Here is an example. Suppose you know the year, month, and day and you want to make a Date object with those settings. Because the Date class knows nothing about calendars, first construct a GregorianCalendar object and then call the getTime method to obtain a date: GregorianCalendar calendar = new GregorianCalendar(year, month, day); Date hireDay = calendar.getTime(); Conversely, if you want to find the year, month, or day of a Date object, you construct a GregorianCalendar object, set the time, and then call the get method: GregorianCalendar calendar = new GregorianCalendar(); calendar.setTime(hireDay); int year = calendar.get(Calendar.YEAR); We finish this section with a program that puts the GregorianCalendar class to work. The program displays a calendar for the current month, like this: Sun Mon Tue Wed Thu Fri Sat 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19* 20 21 22 23 24 25 26 27 28 29 30 31 The current day is marked with an *, and the program knows how to compute the days of the week. Let us go through the key steps of the program. First, we construct a calendar object that is initialized with the current date and time. (We don't actually care about the time for this application.) GregorianCalendar d = new GregorianCalendar(); We capture the current day and month by calling the get method twice. int today = d.get(Calendar.DAY_OF_MONTH); int month = d.get(Calendar.MONTH); Then we set d to the first of the month and get the weekday of that date. d.set(Calendar.DAY_OF_MONTH, 1); int weekday = d.get(Calendar.DAY_OF_WEEK); The variable weekday is set to 1 (or Calendar.SUNDAY) if the first day of the month is a Sunday, to 2 (or Calendar.MONDAY) if it is a Monday, and so on. Next, we print the header and the spaces for indenting the first line of the calendar. For each day, we print a space if the day is < 10, then the day, and then a * if the day equals the current day. Each Saturday, we print a new line. Then we advance d to the next day: d.add(Calendar.DAY_OF_MONTH, 1); When do we stop? We don't know whether the month has 31, 30, 29, or 28 days. Instead, we keep iterating while d is still in the current month. do { . . . } while (d.get(Calendar.MONTH) == month); Once d has moved into the next month, the program terminates. Example 4-1 shows the complete program. As you can see, the GregorianCalendar class makes it simple to write a calendar program that takes care of complexities such as weekdays and the varying month lengths. You don't need to know how the GregorianCalendar class computes months and weekdays. You just use the interface of the class the get, set, and add methods. The point of this example program is to show you how you can use the interface of a class to carry out fairly sophisticated tasks without ever having to know the implementation details. Example 4-1. CalendarTest.java1. import java.util.*; 2. 3. public class CalendarTest 4. { 5. public static void main(String[] args) 6. { 7. // construct d as current date 8. GregorianCalendar d = new GregorianCalendar(); 9. 10. int today = d.get(Calendar.DAY_OF_MONTH); 11. int month = d.get(Calendar.MONTH); 12. 13. // set d to start date of the month 14. d.set(Calendar.DAY_OF_MONTH, 1); 15. 16. int weekday = d.get(Calendar.DAY_OF_WEEK); 17. 18. // print heading 19. System.out.println("Sun Mon Tue Wed Thu Fri Sat"); 20. 21. // indent first line of calendar 22. for (int i = Calendar.SUNDAY; i < weekday; i++ ) 23. System.out.print(" "); 24. 25. do 26. { 27. // print day 28. int day = d.get(Calendar.DAY_OF_MONTH); 29. System.out.printf("%3d", day); 30. 31. // mark current day with * 32. if (day == today) 33. System.out.print("*"); 34. else 35. System.out.print(" "); 36. 37. // start a new line after every Saturday 38. if (weekday == Calendar.SATURDAY) 39. System.out.println(); 40. 41. // advance d to the next day 42. d.add(Calendar.DAY_OF_MONTH, 1); 43. weekday = d.get(Calendar.DAY_OF_WEEK); 44. } 45. while (d.get(Calendar.MONTH) == month); 46. // the loop exits when d is day 1 of the next month 47. 48. // print final end of line if necessary 49. if (weekday != Calendar.SUNDAY) 50. System.out.println(); 51. } 52. } NOTE
java.util.GregorianCalendar 1.1
|