Date
we discussed in the
"Dates" example.
Here is the Java code for the class Date
:
class Date { private int day,month,year; public Date(int d,int m,int y) throws DateException { day=d; month=m; year=y; if(month<1||month>12||day<1) throw new DateException(toString()); else if((month==4||month==6||month==9||month==11)&&day>30) throw new DateException(toString()); else if(month==2) { if(year%4==0) { if(day>29) throw new DateException(toString()); } else if(day>28) throw new DateException(toString()); } else if(day>31) throw new DateException(toString()); } public String toString() { return day+"/"+month+"/"+year; } public boolean lessThan(Date d) { if(year<d.year) return true; else if(year==d.year) if(month<d.month) return true; else if(month==d.month) if(day<d.day) return true; return false; } }This means that every
Date
object has three fields,
day
, month
and year
. A new date object
is created by using the expression new
Date(x,y,z)
where x
, y
and z
are each integer
expressions. The constructor is written so that if these do not form a valid
date, a DateException
is thrown. The method toString()
enables objects of type Date
to be printed in the
day/month/year
format, with month
being an
integer from 1
to 12
. The method
lessThan(Date d)
enables us to compare dates to see which is the
earliest.
With this code, objects of type Date
are immutable.
That means that once you have created one with new
, it is not
possible to change the values stored in its fields. This is often a
valuable property: if you know it is impossible to change the data stored
in some object, you know that it is impossible to have the sort of
programming error caused by some method somewhere unexpectedly changing
an object's data.
Date
public. This can be done by replacing the word private
with
public
where the fields are declared, making it:
public int day,month,year;In fact, having nothing but
int day,month,year;will have the same effect, at least in Java as we have seen it in this course (the difference only comes into effect if we move on to the Java concept of packages, which we have not considered yet). Then we can freely use the fields of some
Date
object as if they
were variables, reading and writing to them. For example, suppose we
want to change the value stored in some date object called
myDate
so that it represents the day after the day it originally
represented. If the fields are public this could, you might think, be done
by:
myDate.day++;but suppose the date happened to represent the last day of the month. If the date stored in variable
myDate
was the 31st October 1998, we
would end up with myDate
storing the non-existent 32nd October
1998. If we are changing a date by one day, but the date is the last day of the
month, we need to set the day
field to 1
and change
the month
field to the next month (or set the month
and day
field to 1
and add one to the year
field if the date we are updating
happens to be New Year's Eve). Of course we can adjust our code to do all this:
myDate.day++; if(myDate.day==32) { if(myDate.month==12) { myDate.month=1; myDate.year++; } else myDate.month++; myDate.day=1; } else if(myDate.day==31&& (myDate.month==4||myDate.month==6||myDate.month==9||myDate.month=11)) { myDate.month++; myDate.day=1; } else if(myDate.month==2) if(myDate.year%4==0&&myDate.day==30) { myDate.month=3; myDate.day=1; } else if(myDate.day==29) { myDate.month=3; myDate.day=1; }This almost works, only it misses out dealing with years ending in 00 not always being leap years. However, that is besides the point. If it is possible to change a field in a
Date
object to anything,
it is possible to change the data stored to something that is no longer a
valid date. Although this would be a programming error, it would be better
to make sure such errors couldn't happen than rely on programmers always
making sure to avoid them.
a
sends message
m
to b
" is one way of talking about what happens
when the method call b.m
is executed inside one of
a
's methods). In this way, objects have complete control of
their own data and can ensure it does not get changed in any unexpected
way. So with dates, for example, the data within a Date
object
should only be changed by a method attached to the Date
object.
We would not allow the day
, month
and
year
field to be accessible by other objects, but they could
change them indirectly, say by calling an addDay()
method.
A method attached to an object in this way, which allows other objects to
change their values but in a controlled way, is called a mutator
method. Below is a class called Calendar
which extends the
Date
class by adding mutator methods to it. The mutator
methods allow the date in a Calendar
object to be put
forward or back by a day, a month or a year.
class Calendar extends Date { public Calendar(int d,int m,int y) throws DateException { super(d,m,y); } public void addDay() { day++; if(day==32) { day=1; month++; } else if(day==31&&(month==4||month==6||month==9||month==11)) { day=1; month++; } else if(day==30&&month==2) { day=1; month=3; } else if(day==29&&month==2&&year%4!=0) { day=1; month=3; } if(month==13) { month=1; year++; } } public void subDay() { day--; if(day==0) { month--; if(month==0) { month=12; day=31; year--; } else if(month==4||month==6||month==9||month==11) day=30; else if(month==2) if(year%4==0) day=29; else day=28; else day=31; } } public void addMonth() { month++; if(month==13) { month=1; year++; } else if(day==31&&(month==4||month==6||month==9||month==11)) day=30; else if(month==2) if(year%4==0) { if(day>29) day=29; } else if(day>28) day=28; } public void subMonth() { month--; if(month==0) { month=12; year--; } else if(day==31&&(month==4||month==6||month==9||month==11)) day=30; else if(month==2) if(year%4==0) { if(day>29) day=29; } else if(day>28) day=28; } public void addYear() { year++; if(month==2&&day==29) day=28; } public void subYear() { year--; if(month==2&&day==29) day=28; } }The code to change the month by one is complicated by the fact that months have different lengths, so for example if we're adding a month to the 31st August, we have to be careful to get the 30th September rather than the non-existent 31st September. Even changing a year requires attention to the fact that we need to change the day if we happen to be adding or taking a year from 29th February on a leap year.
A file containing this code may be found in the shared directory:
/import//teaching/BSc/1st/ItP/calendarsA new version of the code for class
Date
is also there.
The only change is that the word private
on the third
line is changed to protected
. The reason for this is that
fields declared as private
are too restricted for the
use we require here. Access to them is restricted only to the
actual methods in the class itself, so methods in classes that
inherit from them cannot access them. A protected
field declaration, however, means the field can be freely accessed
and changed in classes which inherit it, although nowhere else.
This is what is required in Calendar
: it has to be
able to access and change the values of the day
,
month
and year
fields it inherits, but
code that uses objects of type Calendar
must not.
A simple program that uses the Calendar
class is put in
the directory with it, in the file CalendarChange.java
.
Note this program uses the Text
class, so you must
have the file Text.class
in your directory to run it
(you may find a copy in /import/teaching/BSc/1st/ItP
).
Here is an example of the program in CalendarChange.java
working:
Enter day: 28 Enter month: 2 Enter year: 1975 Enter + or - to add or subtract followed by d, w, m, or y (or q to quit) > +d 1/3/1975 > +y 1/3/1976 > -d 29/2/1976 > -m 29/1/1976 > -m 29/12/1975 > q 29/12/1975A version of this is given which adds a graphical user interface element in its display of dates. The class
Calendar1
extends Calendar
further by adding a graphical display
field. The code to deal with the graphical display field is found
in file CalendarGraphic.java
. As we are not covering
graphics in this course you need not be concerned with how that code
works, you just need a copy of it (or rather the version compiled
into Java byte code) in your directory. A version of the
CalendarChange.java
program which uses Calendar1
objects rather than Calendar
objects can be found in
file CalendarChange1.java
.
As a good example, although we think of dates as having a day, month
and year part, it can make a lot of sense to store them in a
different way - as the number of days past some fixed date.
For example, if we take 1st January 1900 as day 1, then 29th October
1998 is day 36096 - thirty-six thousand and ninety-five days later.
If we use this representation of dates, then some operations are
very simple to program. For example, finding whether one date is less
than another involves simply comparing two numbers. Adding a day to
a date involves simply adding one to a number. However, if we want to
print a date out which is stored in this form, it involves some
complex calculations in order to bring it back to the day/month/year
format. Here is a version of Date
which stores dates in this way:
class Date { protected int days; public Date(int day,int month,int year) throws DateException { if(month<1||month>12||day<1) throw new DateException(toString()); else if((month==4||month==6||month==9||month==11)&&day>30) throw new DateException(toString()); else if(month==2) { if(year%4==0) { if(day>29) throw new DateException(toString()); } else if(day>28) throw new DateException(toString()); } else if(day>31) throw new DateException(toString()); days=(year-1900)*365+(year-1901)/4; days+=day; if(month>=2) { days+=31; if(month>=3) { if(year%4==0&&year!=1900) days+=29; else days+=28; switch(month) { case 3: break; case 4: days+=31; break; case 5: days+=61; break; case 6: days+=92; break; case 7: days+=122; break; case 8: days+=153; break; case 9: days+=184; break; case 10: days+=214; break; case 11: days+=245; break; case 12: days+=275; } } } } public String toString() { int year=days/365; int day=days%365; int month; if(year!=0) day-=(year-1)/4; if(day<=0) { if((year-1)%4==0) day+=366; else day+=365; year--; } if(day<=31) month=1; else if(day<=59) { month=2; day-=31; } else if(year%4==0&&day==60&&year!=0) { month=2; day=29; } else { if(year%4==0&&year!=0) day-=60; else day-=59; if(day<=31) month=3; else if(day<=61) { month=4; day-=31; } else if(day<=92) { month=5; day-=61; } else if(day<=122) { month=6; day-=92; } else if(day<=153) { month=7; day-=122; } else if(day<=184) { month=8; day-=153; } else if(day<=214) { month=9; day-=184; } else if(day<=245) { month=10; day-=214; } else if(day<=275) { month=11; day-=245; } else { month=12; day-=275; } } year+=1900; return day+"/"+month+"/"+year; } public boolean lessThan(Date d) { return (days<d.days); } }As you can see, converting dates to and from the day/month/year format is a little complex, due to the varying number of dates in a year, and the extra complexity brought in by leap year. But the
lessThan
operator is very simple. The protected
field days
stores the actual representation of a date.
You could experiment by compiling this version of Date
and showing that most of the previous programs which use the class
Date
still run and give exactly the same result. The
only difference (which you won't see as the programs are not big enough
to make it noticeable) is that dates stored in this way take less
space, and the timing of the operations is different (lessThan
can be done more quickly, but reading and writing dates is slower).
Programs using the class Calendar
won't work if we change
the internal representation in class Date
though.
This is because class Calendar
does rely on the
internal representation of dates having a day
, a
month
and a year
field.
Code for dates stored in this new way is given in the directory
/import/teaching/BSc/1st/ItP/calendars
, though it is
given as a separate type, Date1
. A version of the
Calendar
type is also given, called Calendar2
,
adding the method addDate(int n)
. Here it is:
class Calendar2 extends Date1 { public Calendar2(int d,int m,int y) throws DateException { super(d,m,y); } public void addDay(int n) { days+=n; } }The method
addDate(int n)
enables you to add a number of days to
a date (the number may be negative so it also enables you to take days from a
date). As you can see, it is very easy to implement given the representation
of dates as just a number of days. Methods
to add months or years would be complex given the day representation,
but this method would be useful if we had a program where a common
operation was to give a date a certain number of days after another.
The program in CalendarChange2.java
makes use of it.
Here is an example of it in operation:
Enter day: 29 Enter month: 10 Enter year: 1998 29/10/1998 Enter + to add a day, - to subtract a day followed by a number for multiple days Or enter q to quit > +1 30/10/1998 > +1 31/10/1998 > +1 1/11/1998 > -1000 5/2/1996So it easily calculates that the day one thousand days before 1st November 1998 was 5th February 1996. Doing the calculation on the representation merely involved subtracting 1000 from 36099, the complicated bit was finding the day/month/year represented by day 35099. The program in
CalendarChange3.java
works
similarly, making use of the Date1
representation of dates,
but has the graphics display we used previously in
CalendarChange1.java
. Graphics are added to objects
of type Calendar2
by Calendar3
.
Date
which is the superclass of Calendar
considered here, as
well as BirthDate
considered in the
"Dates": part 2 example. Calendar1
in turn has Calendar
as its superclass, giving us the
diagram:
Date / \ BirthDate Calendar | Calendar1We have also had
Calendar2
inheriting from
Date1
and Calendar3
inheriting from
Calendar2
, giving us:
Date1 | Calendar2 | Calendar3In fact in Java all objects which don't inherit from any other class have a predefined class called
Object
as their superclass, so the
inheritance diagram can be written as:
Object / \ Date1 Date / / \ Calendar2 BirthDate Calendar / \ Calendar3 Calendar1We shall see later where
Object
can be useful.
These notes were produced as part of the course Introduction to Programming as it was given in the Department of Computer Science at Queen Mary, University of London during the academic years 1998-2001.
Last modified: 29 Oct 1998