This page is a continuation of Before Objects. We will continue to modify the app in Bed.
In the Android Studio
project
view,
the folder
/app/java/edu.nyu.scps.bed
already holds the Java file
MainActivity.java
.
We will add a new
.java
file named
Date.java
to this folder.
Select the folder
/app/java/edu.nyu.scps.bed
in the
project
view
and pull down
File → New → Java Class
Create New Class
Name: Date
Kind: Class
OK
In the Android Studio
project
view,
double-click on your new file
Date.java
.
Edit it to contain the following.
package edu.nyu.scps.bed; public class Date { public static final int JANUARY = 1; public static final int FEBRUARY = 2; public static final int MARCH = 3; public static final int APRIL = 4; public static final int MAY = 5; public static final int JUNE = 6; public static final int JULY = 7; public static final int AUGUST = 8; public static final int SEPTEMBER = 9; public static final int OCTOBER = 10; public static final int NOVEMBER = 11; public static final int DECEMBER = 12; private static final int[] a = { 0, //dummy 31, //January 29, //February: can't distinguish between leap and non-leap 31, //March 30, //April 31, //May 30, //June 31, //July 31, //August 30, //September 31, //October 30, //November 31 //December }; //The three fields (internal organs) inside each Date object. private int year; private int month; //in the range 1 to 12 inclusive private int day; //in the range 1 to number of days in month inclusive //This method is the constructor for class Date. //It creates a new object of this class. //We're not bothering to check for errors. public Date(int month, int day, int year) { this.year = year; //this.year is the field, year is the parameter this.month = month; this.day = day; } //These three methods are "getters". public int getYear() { return year; } public int getMonth() { return month; } public int getDay() { return day; } //Return the length of the month that this Date belongs to. public int length() { return a[month]; } //Return an int in the range 1 to 366 inclusive //telling what day of the year this Date is holding. public int julian() { int sum = day; for (int i = 1; i < month; ++i) { sum += a[i]; //means sum = sum + a[i]; } return sum; } // Move this Date object one day into the future. // If it is already at the last possible date, do nothing. public void next() { if (day < length()) { ++day; return; } day = 1; if (month < 12) { ++month; return; } month = 1; if (year < Integer.MAX_VALUE) { ++year; return; } //Roll back an unsuccessful attempt at a change. //Don't leave this Date object in a half-changed state. month = monthsInYear(); day = length(); } // Move this Date object one day into the past. // If it is already at the earliest possible date, do nothing. public void prev() { if (day > 1) { --day; return; } if (month > 1) { --month; } else if (year > Integer.MIN_VALUE) { month = 12; --year; } else { return; } day = length(); } //Move this Date object count days into the future. //The parameter must be non-negative, but we're not bothering to check for errors. public void next(int count) { for (int i = 0; i < count; ++i) { next(); } } //Return a String in the format 12/31/2015 showing the content of this Date object. @Override public String toString() { return month + "/" + day + "/" + year; } //Return the number of months in a year. public static int monthsInYear() { return a.length - 1; } }
Put the following code between the horizontal lines in the
onCreate
method of class
MainActivity
in the file
MainActivity.java
.
//----------------------------- //Demonstrate the class Date we created in the file Data.java. //Can use the static methods and fields of a class //even when no objects of the class exist. String output = "A year has " + Date.monthsInYear() + " months.\n" + "January is month number " + Date.JANUARY + ".\n" + "December is month number " + Date.DECEMBER + ".\n"; Date d = new Date(12, 31, 2015); //int y = d.year; //won't compile, because year is private. int j = d.julian(); output += d + " is day number " + j + " of the year " + d.getYear() + ".\n"; output += "The day after " + d + " is "; d.next(); output += d + ".\n"; display("Class Date", output); for (;;) { Date e = new Date(12, 31, 2014); int i = getInt("Forward", "How many days forward from " + e + " would you like to go?"); output = "The day that is " + i + " days after " + e + " is "; e.next(i); //Move e i days forward. output += e + ".\n"; e.next(); //Move e one additional day forward. output += "The day after that is " + e + "."; display("Forward", output); } //-----------------------------
Run the app and tell it you want to go 60 days forward from 12/31/2014.
The output will be wrong because our class
Date
can’t tell the difference between a leap year and a non-leap year.
Class Date A year has 12 months. January is month number 1. December is month number 12. 12/31/2015 is day number 365 of the year 2015. The day after 12/31/2015 is 1/1/2016. OK
Forward How many days forward from 12/31/2014 would you like to go? Type answer here and press Done. 60 OK
Forward The day that is 60 days after 12/31/2014 is 2/29/2015. The day after that is 3/1/2015. OK
An object is a big variable that can contain smaller variables inside of it. The smaller variables are called the fields or members of the object. Think of them as the object’s internal organs.
There are many different
classes
(types)
of objects.
In Java, the name of each class begins with an uppercase letter.
For example, we’re about to create a class named
Date
.
Because of the
package
statement at the top of the
Date.java
file, the class’s full name will be
edu.nyu.scps.bed.Date
.
We create this class for demonstration purposes only.
Android already has a class
Date
(its full name is
java.util.Date
)
which is better than the class
Date
we’re about to create.
You can see now why classes have full names:
it allows us to have two classes with the same first name
(edu.nyu.scps.bed.Date
and
java.util.Date
).
Each object that belongs to a given class has the same fields inside of it.
For example, every object of our class
Date
will contain three
int
variables named
year
,
month
,
and
day
.
Different
Date
objects can have different values
in their fields—October 29, 1929 vs. July 20, 1969—but
the three fields must be present in each object.
The code in the file
Date.java
is called a
declaration
for class
Date
.
It acts as a blueprint for the objects of class
Date
we’re about to create.
(Let’s call these objects
“Date
objects”.)
The variables that are declared
in the outermost
{
curly
braces}
of the file—year
,
month
,
day
—will be present as fields (internal organs) in every
Date
object.
The declaration for each class is usually written in a file named after the class.
For example, ghe declaration for class
MainActivity
is written in the file
MainActivity.java
,
and the declaration for class
Date
is written in the file
Date.java
.
The most common way to create a new object is with the Java keyword
new
.
In between the horizontal lines in
MainActivity.java
,
you can see it installing the three numbers
12
,
31
,
and
2015
into the newborn
Date
object.
It looks like we’re storing the new
Date
object into the variable
d
.
In reality, the
Date
object is too big to fit into
d
.
What gets stored into
d
is only the
memory address
of the
Date
object.
The memory address is a number that tells where the
Date
object is located in the phone’s memory.
The address is called a
reference
to the
Date
object,
and
d
is now said to
refer
to the
Date
object.
The
Date
object itself actually has no name,
but it will be convenient to call it “the object
d
”.
You’ll let me get away with this, won’t you?
When we say
d.next()
,
we automatically execute the group of statements inside the
{
curly
braces}
after the
public void next
in
Date.java
.
The name of this group of statements is
next
.
A group of statements with a name,
enclosed in curly braces,
is called a
method.
Recall that our
Date
object
d
contains three fields named
year
,
month
,
and
day
.
When the statements in the body (i.e., within the curly braces) of
next
are executed because we said
d.next()
,
the
year
,
month
,
and
day
mentioned by those statements
are the
year
,
month
,
and
day
fields inside the object
d
.
When the statements in the body of
next
are executed because we said
e.next()
,
the
year
,
month
,
and
day
mentioned by those statements
are the
year
,
month
,
and
day
fields inside the object
e
.
The object before the dot in the statement that executes the method
is called the object to which the method belongs.
For example, when we write
d.next()
,
we are executing the
next
method that belongs to the
Date
object
d
.
When we write
e.next()
,
we are executing the
next
method that belongs to the
Date
object
e
.
But it’s really the same method in both cases.
There is only one copy of this method in the device’s memory,
but by writing a different object each time in front of the dot,
we can make the method manipulate the fields of different objects.
In fact,
all the objects of a given class have the same methods,
just as they all have the same fields.
The class declaration in
Date.java
lists all the fields and methods that belong to each object of class
Date
.
In the body of a method,
the keyword
this
stands for the object that the method belongs to.
Let’s say that one of the statements in
next
mentioned
this
.
If
next
was being executed because we said
d.next()
,
then the
this
would mean
d
.
If
next
was being executed because we said
e.next()
,
then the
this
would mean
e
.
One method of an object can easily call another method of the same object.
For exmple, the one-parameter
next
calls the no-parameter
next
during each iteration of the
for
loop.
The one-parameter
next
and the no-parameter
next
change the content of the
Date
object to which they belong.
But they return no value.
This means that we cannot say
int i = d.next(); //won't compile
Two examples of methods that do return a value are the following.
In the body of each method,
the keyword
return
is followed by the expression that is returned by the method.
int j = d.julian(); //an integer in the range 1 to 366 inclusive String s = d.toString(); //a string in the format "12/31/2015"
An object’s
toString
method can also be called implicitly by using the
+
operator to paste the object onto a string.
//These two statements do the same thing. String s = "The date is " + d.toString() + ".\n"; String s = "The date is " + d + ".\n";
Every object of class
Date
has a method named
next
that takes one
int
parameter and changes the values stored in the three fields
inside of the object.
If the object contained December 31, 2015,
the parameter 60 would change the content of the object to February 29, 2015.
(Objects of our present class
Date
believe that every year is a leap year.)
Every
Date
object also has a no-parameter
next
method that moves the object one day ahead.
The variables
year
,
month
,
and
day
last as long as the
Date
object that contains them.
We never have to worry about a
Date
object outliving its own internal organs.
These variables can be mentioned in all the methods of the class.
The variables
sum
and
i
in the body of the
julian
method last only as long as the method that created them
(julian
)
is executing.
They can be mentioned only within that method.
We also created an
int
variable named
DECEMBER
,
holding the number 12.
Since we marked it as
final
,
the content of this variable can never change.
The Java convention is to give a
final
variable an all-uppercase name.
There’s another way in which the variable
DECEMBER
is special:
it has a last name.
Its last name is
Date
,
the name of our new class.
This would allow the app to have another variable named
DECEMBER
with a different last name.
(See
George Washington and George Clooney in
XML.)
A variable whose last name is the name of a class is called a
static field
of that class.
DECEMBER
is a field of class
Date
because it was declared in
Date.java
within the curly braces of the declaration for class
Date
.
But it differs from
year
,
month
,
and
day
because it was declared with the keyword
static
.
This means that there is only one copy of the variable
DECEMBER
,
no matter how many objects of class
Date
are created.
The single copy is not stored inside of any
Date
object.
We’re lucky that
d
is born containing Deceber 31.
If it was born with November 11,
there would be no way to tell which parameters were the month and year.
Change the
Date d = new Date(12, 31, 2015);to
Date d = new Date(Date.DECEMBER, 31, 2015);In the body of the no-parameter
next
,
change
12
to
DECEMBER
.
The prefix
Date.
is unnecxessary inside of a method of class
Date
(at least, as long as there is no local variable named
DECEMBER
in the method).
The method
monthsInYear
returns the number 12.
Like
DECEMBER
,
monthsInYear
belongs to class
Date
as a whole, not to any particular
Date
object.
That’s why we wrote the name of the class,
not the name of an object,
in front of it when we executed it.
This kind of method is called a
static method
or a
class method.
Add the following file
SmarterDate.java
to the project.
Put it in the folder that’s holding the two existing
.java
files,
MainActivity.java
and
Date.java
.
Class
SmarterDate
is a
subclass
(improved version) of class
Date
.
Conversely, class
Date
is the
superclass
of class
SmarterDate
.
The constructor of the subclass always begins by calling the constructor of the superclass. Often the constructor of the subclass passes its parameters unchanged to the constructor of the superclass. That’s what’s happening here.
The subclass constructor is not the only method of the subclass
that calls the corresponding method of the superclass.
The
length
method of the subclass will call the
length
method of the superclass most of the time.
But in one special case—February in a non-leap year—it will not
call the
length
method of the superclass.
We say that the subclass
length
overrides
the superclass
length
,
and we mark it with the special word
@Override
.
package edu.nyu.scps.bed; public class SmarterDate extends Date { public SmarterDate(int month, int day, int year) { super(month, day, year); } @Override public int length() { if (getMonth() == 2 && !isLeap()) { return 28; } else { return super.length(); } } //Return true if this SmarterDate belongs to a leap year. private boolean isLeap() { int year = getYear(); if (year % 400 == 0) { return true; } else if (year % 100 == 0) { return false; } else if (year % 4 == 0) { return true; } else { return false; } } }
In the
onCreate
method of class
MainActivity
in the file
MainActivity.java
,
change
Date e = new Date(Date.DECEMBER, 31, 2014);to
SmarterDate e = new SmarterDate(Date.DECEMBER, 31, 2014);
Run the app and tell it you want to go 60 days forward from 12/31/2014. The output will be
Class Date A year has 12 months. January is month number 1. December is month number 12. 12/31/2015 is day number 365 of the year 2015. The day after 12/31/2015 is 1/1/2016. OK
Forward How many days forward from 12/31/2014 would you like to go? Type answer here and press Done. 60 OK
Forward The day that is 60 days after 12/31/2014 is 2/28/2015. The day after that is 3/1/2015. OK
The following example no longer uses class
SmarterDate
.
The file
SmarterDate.java
can be removed from the project.
Select the file in the Android Studio
project
view,
choose
Delete…,
and press OK.
In place of class
SmarterDate
,
we will use a class that is exactly the same except that it has no name.
We do this when there is exactly one statement
that creates an object of the class.
(In the following example, the object is
e
.)
If there are two or more statements that create objects of the class,
we should reinstate class
SmarterDate
.
Put the following code between the horizontal lines in the
onCreate
method of class
MainActivity
in the file
MainActivity.java
.
//----------------------------- //Demonstrate the class Date we created in the file Data.java. //Can use the static methods and fields of a class //even when no objects of the class exist. String output = "A year has " + Date.monthsInYear() + " months.\n" + "January is month number " + Date.JANUARY + ".\n" + "December is month number " + Date.DECEMBER + ".\n"; Date d = new Date(12, 31, 2015); int j = d.julian(); output += d + " is day number " + j + " of the year " + d.getYear() + ".\n"; output += "The day after " + d + " is "; d.next(); output += d + ".\n"; display("Class Date", output); for (;;) { Date e = new Date(12, 31, 2014) { @Override public int length() { if (getMonth() == 2 && !isLeap()) { return 28; } else { return super.length(); } } private boolean isLeap() { int year = getYear(); if (year % 400 == 0) { return true; } else if (year % 100 == 0) { return false; } else if (year % 4 == 0) { return true; } else { return false; } } }; int i = getInt("Forward", "How many days forward from " + e + " would you like to go?"); output = "The day that is " + i + " days after " + e + " is "; e.next(i); //Move e i days forward. output += e + ".\n"; e.next(); //Move e one additional day forward. output += "The day after that is " + e + "."; display("Forward", output); } //-----------------------------
In
Data.java
,
change the constructor and no-arg
next
and
prev
to the following.
public Date(int month, int day, int year) { this.year = year; //this.year is the field, year is the parameter if (month < 1 || month > 12) { throw new RuntimeException("bad month " + month); } this.month = month; if (day < 1 || day > length()) { throw new RuntimeException("bad day " + day + " of month " + month); } this.day = day; }
public void next() { if (day < length()) { ++day; return; } day = 1; if (month < 12) { ++month; return; } month = 1; if (year < Integer.MAX_VALUE) { ++year; return; } //Roll back an unsuccessful attempt at a change. //Don't leave this Date object in a half-changed state. month = monthsInYear(); day = length(); throw new RuntimeException(this + " is already the last Date."); }
public void prev() { if (day > 1) { --day; return; } if (month > 1) { --month; } else if (year > Integer.MIN_VALUE) { month = 12; --year; } else { throw new RuntimeException(this + " is already the first Date."); } day = length(); }
//----------------------------- String output = ""; try { Date d = new Date(4, 31, 2015); //There is no April 31st. } catch (RuntimeException runtimeException) { output += runtimeException + "\n"; } try { Date d = new Date(12, 31, Integer.MAX_VALUE); d.next(); } catch (RuntimeException runtimeException) { output += runtimeException + "\n"; } display("Output", output); //-----------------------------
Output java.lang.RuntimeException: bad day 31 of month 4 java.lang.RuntimeException: 12/31/2147483647 is already the last date.
Will the
Date
constructor throw an exception for February 29, 2015?
Will the
SmarterDate
constructor throw an exception for February 29, 2015?
//This file is InformalPerson.java. package edu.nyu.scps.bed; public class InformalPerson { String nickName; InformalPerson(String nickName) { this.nickName = nickName; } @Override public String toString() { return nickName; } }
A double quote inside a double-quoted string must be preceded with a backslash.
//This file is FormalPerson.java. package edu.nyu.scps.bed; public class FormalPerson extends InformalPerson { String lastName; String firstName; FormalPerson(String firstName, String nickName, String lastName) { super(nickName); this.firstName = firstName; this.lastName = lastName; } @Override public String toString() { return firstName + " \"" + super.toString() + "\" " + lastName; } }
//----------------------------- String output = ""; FormalPerson roosevelt = new FormalPerson("Theodore", "Teddy", "Roosevelt"); FormalPerson nixon = new FormalPerson("Richard", "Tricky Dick", "Nixon"); InformalPerson carter = new InformalPerson("Jimmy"); //Some of the objects in this array are of class InformalPerson, //and some of them are of class FormalPerson. InformalPerson[] a = { roosevelt, nixon, carter }; for (int i = 0; i < a.length; ++i) { output += a[i] + "\n"; //means output = output + a[i].toString() + "\n"; } //A downcast converts a reference to a superclass object //down to a reference to a subclass object. FormalPerson n = (FormalPerson)a[1]; display("Presidents", output); //-----------------------------
Presidents Theodore "Teddy" Roosevelt Richard "Tricky Dick" Nixon Jimmy OK