Java ScreenShot
     

Screenshot Core Java 2: Volume I - Fundamentals

Table of Contents
 4.  Objects and Classes


Using Existing Classes

Since 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. A good example of this is 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. Since 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 will 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 Variables

To work with objects, you first construct them and specify their initial state. Then you apply methods to the objects. In the Java coding 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."

Java graphics notes_icon.gif

You may be wondering: Why use classes to represent dates rather than (as in some languages) a built-in type? For example, Visual Basic has a built-in date type and programmers can specify dates in the format #6/1/1995#. On the surface, this sounds convenient—programmers can simply use the built-in date type rather than worrying about classes. But actually, how suitable is the Visual Basic design? In some locales, dates are specified as month/day/year, in others as day/month/year. Are the language designers really equipped to foresee these kinds of issues? If they do a poor job, the language becomes an unpleasant muddle, but unhappy programmers are powerless to do anything about it. By using classes, the design task is offloaded to a library designer. If the class is not perfect, other programmers can easily write their own classes to enhance or replace the system classes.

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 you can keep using them. Simply store the object in a variable:

Date birthday = new Date();


Screenshot-3 shows the object variable birthday which refers to the newly constructed object.

Screenshot-3. Creating a new object

Java graphics 04fig03.gif


There 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 Screenshot-4.)

Screenshot-4. Object variables that refer to the same object

Java graphics 04fig04.gif


It 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 run-time error occurs.

birthday = null;
String s = birthday.toString(); // runtime error!


Local object variables are not automatically initialized to null. You must initialize them, either by calling new or by setting them to null.

Java graphics cplus_icon.gif

Many people mistakenly believe that Java object variables behave like C++ references. But in C++ there are no null references, and references cannot be assigned. You should think of Java object variables as analogous to object pointers in C++. For example,

Date birthday; // Java


is really the same as

Date* birthday; // C++


Once you make this association, everything falls into place. Of course, a Date* pointer isn't initialized until you initialize it with a call to new. The syntax is almost the same in C++ and Java.

Date* birthday = new Date(); // C++


If you copy one variable to another, then both variables refer to the same date—they are pointers to the same object. The equivalent of the Java null reference is the C++ NULL pointer. All Java objects live on the heap. When an object contains another object variable, that variable still contains just a pointer to yet another heap object. In C++, pointers make you nervous because they are so error-prone. It is easy to create bad pointers or to mess up memory management. In Java, these problems simply go away. If you use an uninitialized pointer, the run-time system will reliably generate a run-time error, instead of producing random results. You don't worry about memory management because the garbage collector takes care of it. C++ makes quite an effort, with its support for copy constructors and assignment operators, to allow the implementation of objects that copy themselves automatically. For example, a copy of a linked list is a new linked list with the same contents but with an independent set of links. This makes it possible to design classes with the same copy behavior as the built-in types. In Java, you must use the clone method to get a complete copy of an object.

The GregorianCalendar Class of the Java Library

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

Java graphics notes_icon.gif

Throughout human history, civilizations grappled with the design of calendars that attached names to dates and brought order to the solar and lunar cycles. For a fascinating explanation of calendars around the world, from the French revolutionary calendar to the Mayan long count, see Calendrical Calculations by Nachum Dershowitz and Edward M. Reingold (Oracle, 1997).

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.");


Java graphics notes_icon.gif

Actually, the Date class has methods such as getDay, getMonth, and getYear, but these methods are deprecated. A method is deprecated when a library designer realizes that the method should have never been introduced in the first place. These methods were a part of the Date class before the library designers realized that it makes more sense to supply separate calendar classes. When the calendar classes were introduced, the Date methods were tagged as deprecated. You can still use them in your programs, but you will get unsightly compiler warnings if you do. It is a good idea to stay away from using deprecated methods because they may be removed in a future version of the library.

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 whole point is that this doesn't matter. What matters are the methods that a class exposes.

Mutator and accessor methods

At 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 online documentation or the API notes at the end of this section. We will 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 day = 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, 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, etc., to a given date.

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.

Java graphics cplus_icon.gif

In C++, the const suffix is used to denote accessor methods. A method that is not declared as const is assumed to be a mutator. However, in the Java coding language there is no special syntax to distinguish between accessors and mutators.

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 to convert 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. Since 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 will 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 app.)

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.java
 1. 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. if (day < 10) System.out.print(" ");
30. System.out.print(day);
31.
32. // mark current day with *
33. if (day == today)
34. System.out.print("* ");
35. else
36. System.out.print(" ");
37.
38. // start a new line after every Saturday
39. if (weekday == Calendar.SATURDAY)
40. System.out.println();
41.
42. // advance d to the next day
43. d.add(Calendar.DAY_OF_MONTH, 1);
44. weekday = d.get(Calendar.DAY_OF_WEEK);
45. }
46. while (d.get(Calendar.MONTH) == month);
47. // the loop exits when d is day 1 of the next month
48.
49. // print final end of line if necessary
50. if (weekday != Calendar.SUNDAY)
51. System.out.println();
52. }
53.}


java.util.GregorianCalendar 1.1

Java graphics api_icon.gif
  • GregorianCalendar()

    constructs a calendar object that represents the current time in the default time zone with the default locale.

  • GregorianCalendar(int year, int month, int date)

    constructs a Gregorian calendar with the given date.

    Parameters:

    year

    the year of the date

     

    month

    the month of the date. This value is 0-based; for example, 0 for January

     

    date

    the day of the month

  • GregorianCalendar(int year, int month, int date, int hour, int minutes, int seconds)

    constructs a Gregorian calendar with the given date and time.

    Parameters:

    year

    the year of the date

     

    month

    the month of the date. This value is 0-based; for example, 0 for January

     

    date

    the day of the month

     

    hour

    the hour (between 0 and 23)

     

    minutes

    the minutes (between 0 and 59)

     

    seconds

    the seconds (between 0 and 59)

  • boolean equals(Object when)

    compares this calendar object with when and returns true if the objects represent the same point in time.

  • boolean before(Object when)

    compares this calendar object with when and returns true if it comes before when.

  • boolean after(Object when)

    compares this calendar object with when and returns true if it comes after when.

  • int get(int field)

    gets the value of a particular field.

    Parameters:

    field

    one of Calendar.ERA, Calendar.YEAR, Calendar.MONTH, Calendar.WEEK_OF_YEAR, Calendar.WEEK_OF_MONTH, Calendar.DAY_OF_MONTH, Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH,

       

    Calendar.AM_PM, Calendar.HOUR, Calendar.HOUR_OF_DAY, Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND, Calendar.ZONE_OFFSET, Calendar.DST_OFFSET

  • void set(int field, int value)

    sets the value of a particular field.

    Parameters:

    field

    one of the constants accepted by get

     

    value

    the new value

  • void set(int year, int month, int day)

    sets the date fields to a new date.

    Parameters:

    year

    the year of the date

     

    month

    the month of the date. This value is 0-based; for example, 0 for January

     

    day

    the day of the month

  • void set(int year, int month, int day, int hour, int minutes, int seconds)

    sets the date and time fields to new values.

    Parameters:

    year

    the year of the date

     

    month

    the month of the date. This value is 0-based; for example, 0 for January

     

    day

    the day of the month

     

    hour

    the hour (between 0 and 23)

     

    minutes

    the minutes (between 0 and 59)

     

    seconds

    the seconds (between 0 and 59)

  • void add(int field, int amount)

    is a date arithmetic method. Adds the specified amount of time to the given time field. For example, to add 7 days to the current calendar date, call c.add(Calendar.DAY_OF_MONTH, 7).

    Parameters:

    field

    the field to modify (using one of the constants documented in the get method)

     

    amount

    the amount by which the field should be changed (can be negative)

  • void setTime(Date time)

    sets this calendar to the given point in time.

    Parameters:

    Time

    a point in time

  • Date getTime()

    gets the point in time that is represented by the current value of this calendar object.

Screenshot

Java ScreenShot
     
Top
 

Comments