User-Defined CLASSES in Java

TOPICS


·         Java Classes

·         Java User-Defined Classes

o   Class Definition, Syntax

o   Class Constructors

o   Variable Declaration and Object Instantiation

o   Accessing Class Members for an Object

o   Built-In Operations Valid on Class Objects

o   Shallow Copy vs Deep Copy

o   Class Scope

o   Some Common Methods in Java

§  Classes and the Method toString

§  Classes and the Method equals

o   The Reference this

o   Creating your own packages

·         Programming Samples using User-Defined Classes:

o   Class Time

o   Class Fraction

 

OUTLINE


Java Classes

So far, the focus of this class was on procedural decomposition (breaking complex tasks into smaller subtasks - structured programming using procedural techniques). Java also provides object-oriented programming (OOP) techniques. An object-oriented programming language  = a software system as a set of interacting objects (rather than a set of actions).

OOP Terminology:

You already know how to use classes and the objects created from them, and how to invoke their methods because you already interacted with objects from Java classes you didn't actually write: String, Scanner, Random, File, PrintStream, etc. Before starting to write your own classes it helps to remember how you used these pre-existing Java classes. In such cases, you were acting as a client of these classes and, as a client, you needed to know what methods are available in each class and how to invoke them. The details of how each method works were not your concern and actually these details were hidden.

Reminder: Every Java class is part of a package, which is a collection of related classes that have been released as a coherent unit (Example: the Random class and the Scanner class are part of the java.util package, which is a collection of general utility classes.) Whenever you refer directly to a class, you must import the package in which it belongs. Java class libraries hold more than 3000 pre-existing classes.
What if you need to create your own (user-defined) classes? You will learn how to define your own classes and their methods, and how to create your own objects from them.

Java User-Defined Classes

Classes are used to create user-defined data types and implement Abstract Data Types (ADTs). An ADT is an abstraction of a commonly appearing data structure, along with a set of defined operations on the data structure.  Historically, the concept of ADT in computer programming developed as a way of abstracting the common data structure and the associated operations. Along the way, ADT provided information hiding. That is, ADT hides the implementation details of the operations and the data from the users of the ADT. Users can use the operations of an ADT without knowing how the operations are implemented.
REMINDER: In Java classes are defined in files with the .java extension. The name of the file must match the name of the class defined within it.

Here we will focus on user-created data types and understanding the structure of a class is easiest in the context of specific examples. Suppose that we want to create new user-defined data types to represent the time of day, points in a xy coordinate system, and complex numbers.

TYPE

 Time

STATE (DOMAIN)

 Each Time value is a time in hours, minutes, and seconds.

BEHAVIOR (OPERATIONS)

 Set the time
 Return hours
 Return minutes
 Return seconds
 Display the time in standard format
 Display the time in military format
 Increment by one second
 Compare 2 times for equality
 compare 2 times for < (determine if one time is “less than” another)
 Copy the time
 Get a copy of the time

 

TYPE

 Point

STATE (DOMAIN)

 Each point is an ordered pair of real numbers (x, y) representing  the x and y coordinates

BEHAVIOR (OPERATIONS)

 Initialize the point
 Return the x coordinate
 Return the y coordinate
 Display the point
 Calculate distance from origin
 Compare 2 points for equality
 Shift location by a given amount (translate)
Copy the point

TYPE

 ComplexNumber

STATE (DOMAIN)

 Each complex number is an ordered pair of real numbers (a, b) representing  a + bi, where i = sqrt(-1).

BEHAVIOR (OPERATIONS)

 Initialize the complex number
 Return the real part
 Return the imaginary part
 Display the complex number
 Add
 Subtract
 Multiply
 Divide
 Determine the absolute value of a complex number
 Compare 2 complex numbers for equality
 Copy the complex number

Definitions, Syntax:

A class is a collection of a fixed number of components.  The components of a class are called the members of the class.  The general syntax for defining a class is

modifier(s) class ClassName modifier(s){
     classMembers
}

where:

public

All classes in the program have access to any public entry. The public entries in a class are said to be exported by that class. If a member of a class is public, you can access it outside the class.

private

Access to entries declared as private is limited to the class itself, making that entry completely invisible outside the class. If a member of a class is private, you cannot access it outside the class. 

protected

Protected entries are restricted to the class that defines them, along with any of its subclasses or any classes in the same package. 

(no modifier)

If the access keyword is missing, the entry is visible only to classes in the same package. Such entries are called package-private. 

NOTE #1: It is good practice to make as many instance variables and methods private as is possible (information hiding)
NOTE #2: In keeping with the modern object-oriented conventions, in general, instance variables are declared as private and instance methods are declared as public.
NOTE #3: In general, when you write the definition of a method of a class and the method uses an object of that class, then within the definition of the method the object can access its private data members (in fact, any private member of the class.)
NOTE #4: Naming conventions reminder (Java): By convention, method names should be a verb in lowercase or a multi-word name that begins with a verb in lowercase, followed by adjectives, nouns, etc. In multi-word names, the first letter of each of the second and following words should be capitalized. Here are some examples: getStudentInfo, initializeMatrix, compareTo, setMinutes, isEmpty, isFull, printArray.
NOTE #5: Typically, a method has a unique name within its class. However, a method might have the same name as other methods due to method overloading (see class constructors). Overloaded methods should be used sparingly, as they can make code much less readable.

Class Constructors
 

·  In addition to the methods necessary to implement operations, every class has a special type of method called constructors. A constructor has the same name as the class, and it executes automatically when an object of that class is created. Constructors are used to guarantee that the instance variables of the class initialize to specific values.

·  A class constructor is a member method whose purpose is to initialize the private data members of a class object.

·  A constructor is implicitly invoked whenever a class object is declared/created (never called!). Invocation of constructors: AUTOMATIC, according to the parameters passed.

·  RULES for designing and using class constructors:

  1. The name of a constructor is the same as the name of the class.
  2. A constructor has no return type (not even void!). Constructors cannot return a value.
  3. Constructors execute automatically when class objects are instantiated. Because they have no types, they cannot be called like other methods. COMMON ERROR: Trying to reset an object by calling a constructor. Constructors are NEVER called!
  4. Classes may include several constructors --> Constructors of a class can be overloaded.
  5. Any two constructors must have different signatures (different parameter lists.) If there are multiple constructors, the constructor that executes depends on the type of values passed to the class object when the class object is instantiated.
  6. Constructor with no parameters = default constructor (implicitly invoked.)
  7. The parameter list appears after declaration -- if there are parameters, their values are listed in parentheses in the declaration. Example:

someClass someClassObject = new someClass(parameter1, parameter2 … )

 

NOTE #6: If you do not include ANY constructor in a class, then Java automatically provides the default constructor. If you provide at least one alternate constructor without providing the default constructor, then Java will NOT automatically provide the default constructor.
NOTE #7: In a class definition, it is a common practice to list all the instance variables, named constants, other data members, or variable declarations first, then the constructors, and then the methods.

Variable Declaration and Object Instantiation: by using ordinary variable declaration with the operator new (an object is an instance of a class and the operator new is used to instantiate the object).

Reminder: 2 types of variables in Java --- primitive type variables (store data directly into their own memory space) and reference variables (store the address of the object containing the data).
Important NOTE: The operator new causes the system to allocate memory space of a specific type, store specific data in that memory space, and return the address of that memory space. In Java, reference variables store the address of a memory space. In Java, any variable of type class is a reference variable. In other words, any object (or instance of the class) is a reference variable. Using the operator new to create a class object is called an instantiation of that class. When working with classes, we actually declare a reference variable of a class type and then, using the operator new we instantiate an object of that class type and store the address of the object into the reference variable.

·  Once a class is defined, you can create objects of that class. Whenever  we declare an object of a class we declare a reference variables of that class type.

·  Object declaration implies:

  1. Declaration of a reference variable of type class.
  2. Instantiation of  the class object.
  3. Storage of  the address of the object into the reference variable declared.

·  In order to allocate memory space for the variable the operator new is used as follows:

new className()

OR

new className(param1, param2, …, paramN)

 

·  The first statement instantiates the object and initializes the instance variables of the object using the default constructor. The second statement instantiates the object and initializes the instance variables using a constructor with parameters (alternate constructor.)  The number of actual parameters and their type should match the formal parameters (in the order given) of one of the constructors.  If the types don't match the formal parameters of any constructor (in the order given), Java uses type coercion and looks for the best match.

·  Example: Create 2 new objects of the Time class.

Time startTime = new Time();// default constructor invoked, no parameters
//What happened: startTime declared as a reference variable of type Time and hrs, mins, secs initialized with 0
//Object startTime accessed via the reference variable startTime

Time endTime = new Time(19,30,10);// alternate constructor invoked, parameterized constructor
//What happened: endTime declared as a reference variable of type Time and hrs, mins, secs initialized with 19, 30, 10
//Object endTime accessed via the reference variable endTime

Memory Diagram:

Description: Description: Description: Description: Description: Description: Description: Description: Description: C:\Courses-NOW\Webpage\237\LectureNotes\Le1-RefObj.JPG

Accessing Class Members for an Object:
 

·  Once an object is created, you can access the public members of the class. The general syntax to access a member of a class object (data or method) is:

objectName.memberName

 

·  The dot . (period) is called the member access operator.

·  The member access operator operates on (or on behalf of) a class object, not a class. In the Time class, the increment member method does not adjust the time of "the Time class," nor does it adjust the time of "all objects of the class." It adjusts the time of one particular class object, the object for which the method was invoked:

startTime.increment();

·  The class members that the class object can access depend on where the object is created.

·  If the object is created in the definition of a method of the class, then the object can access both the public and private members.

·  If the object is created elsewhere (client program) then the object can access only the public members of the class.

·  Examples:

Time startTime = new Time();
Time endTime = new Time(19,30,10);
startTime.increment();
startTime.set(6,30,20);
endTime.set(inputHrs, inputMins, inputSecs);//input from user
endTime.write();
startTime.hrs = 5; //ILLEGAL!!!
if (startTime.lessThan(endTime))...

 

Built-in Operations on Classes

Most of Java’s built-in operations do not apply to classes. Invalid operations:

The built-in operation that is valid for classes is the dot operator (.) or the member access operator discussed earlier. Also, objects can be passed to methods (by reference!) and returned from value-returning methods.

Shallow Copy vs. Deep Copy

(Definition) Shallow copy: Two reference variables of the same type point to the same object.
The statement startTime = endTime will create a shallow copy. The statement copies the value of the reference variable endTime into the reference variable startTime. After execution, both variables refer to the same object. The result of a shallow copy operation for startTime and endTime looks like this:
 

Description: Description: Description: Description: Description: Description: Description: Description: Description: C:\Courses-NOW\Webpage\237\LectureNotes\Le1-Assign.JPG
 
 
 

(Definition) Deep copy: Each reference variable refers to its own object.
The result of a deep copy operation for startTime and endTime should look like this:

Description: Description: Description: Description: Description: Description: Description: Description: Description: C:\Courses-NOW\Webpage\237\LectureNotes\Le1-CopyMethod.JPG

To get a deep copy:

1. Create and use a method copy, so that you can make the call: startTime.copy(endTime); (the values of all instance variables of the object endTime are copied into the corresponding instance variables of the object startTime.
2. Create and use a method
getCopy, so that you can make the call: startTime = endTime.getCopy(); (the object being copied creates a copy of itself and return a reference to that copy)

Take a look at both methods in class Time.

Class Scope

Any member of a class (data or method) is local to the class. A public class member can be accessed outside the class through the object name and the member access operator (.).
If the same identifier is declared outside the class, the two are not related. Example (valid, no confusion):

Time checkInTime; // contains member write()
SomeClass someObject; // contains member write()
void write(); // some method
...
checkInTime.write();
someObject.write();
write();

 

Some Common Methods in Java: toString, equals

Classes and the Method toString

By default, Java doesn't know how to print the state of a class object, so if you make an attempt to simply display an object the result will look very strange, like in the example below:

Time startTime = new Time(8, 20, 30);
System.put.println("The start time is " + startTime);
//will display The start time is Time@8b9a54

 

This doesn't work. We'd like to be able to print the object itself and have something meaningful appear in the output. Options:

·  Create your own print method to show the state of a class object.

·  Use the method toString provided by Java. Whenever a class is created, Java provides the method toString to the class. This method is used to convert an object to a String object.

The method toString is a public value-returning method. It does not take any parameters and returns the address of a String object. The default definition of the method toString creates a string that is the name of the object’s class, followed by the hash code of the object (Time@8b9a54). The methods print and println can be used to output the string created by the method toString.

The heading of the method toString is:

public String toString()

 

NOTE #8: The method must have this exact name and signature.
NOTE #9: The method is used when more flexibility in output is needed (not necessarily doing output to the monitor, could also print to a file, etc.)

Every class contains a toString method, even if it isn't written in your class's code. The default toString behavior is to return the class's name followed by a hexadecimal (base-16) number: Time@8b9a54.
You can override the default definition of the method toString to convert an object to a desired string.
Example:

// Method toString for class Time
public String toString(){
    return hrs + ":" + mins + ":" + secs;
}

// Method toString for class Point
public String toString() {
    return "(" + x + ", " + y + ")";
}

The toStringmethod is called when an object is printed or concatenated with a String.

Time t1 = new Time(8, 30, 15);
System.out.println("Time t1 is " + t1);

 

You can also write the .toString() method explicitly:

 System.out.println("Time t1 is " + t1.toString());

 

Classes and the Method equals
Don't use the == operator to compare objects (it compares references to objects and only evaluates to true if two variables refer to the same object. The == operator doesn't tell whether two objects have the same state or not.
Example:

Time t1 = new Time(8, 30, 15);
Time t2 = new Time(8, 30, 15);
if (t1 == t2) //false!
    System.out.println("Same times!");
else
    System.out.println("NOT same times!");

 

The Java equals method compares the state of objects BUT when you write your own new class, Java doesn't know how to compare the state of 2 objects. The default equals behavior acts just like the == operator.
Example:

Time t1 = new Time(8, 30, 15);
Time t2 = new Time(8, 30, 15);
if (t1.equals(t2))  //STILL false!
    System.out.println("Same times!");
else
    System.out.println("NOT same times!");

 

You can replace this default behavior by writing your own  equals  method. The method will actually compare the state of the two objects and return true for cases like the example above.
The following is a valid implementation of the
equals method:

public boolean equals(Time otherTime) {
    return hrs == otherTime.hrs && mins == otherTime.mins && secs == otherTime.secs;
}

 

However, the parameter to a thorough and careful equals method must be of type Object (meaning that an object of any type can be passed to be compared with a class object).
NOTE #10: The object that is passed to equals can be cast from
Object into the type of your class. Type-casting with objects behaves differently than casting primitive values. We are really casting a reference of type Object into a reference of type Time.
NOTE #11: We can use a keyword called  instanceof  to ask whether a variable refers to an object of a given type. Syntax:

       variableName instanceof className

 

This version of the equals method allows us to thoroughly compare Time objects against any other type of object:

// Returns whether obj refers to a Time object with
// the same hrs, mins, secs as this Time object.
public boolean equals(Object obj) {
    if (obj instanceof Time) {
        Time otherTime = (Time) obj; //type-casting
        return hrs == otherTime.hrs && mins == otherTime.mins && secs == otherTime.secs;
    }
    else
        return false;
}

 

The Reference this

Every object has access to a reference to itself. The name of this reference is the reserved word this. Java implicitly uses the reference this to refer to both the instance variables and the methods of a class.
this is a reference to the implicit parameter (the object on which an instance method or constructor is being called.)
this is the reserved word for the generic name of the calling object.

Syntax:
To refer to an instance variable (field):

this.fieldName

 

To refer to a method:

this.methodName(parameters);

To call a constructor from another constructor:

this(parameters);

It is a logical error when a method contains a parameter or local variable that has the same name as a field of a class. In this case, use reference this if you want to access the field of the class. Avoid method parameter names or local variable names that conflict with field names.

Example:  

public void setTime(int h, int m, int s) {
    hrs  = (h >= 0 && h < 24)? h : 0;
    mins = (m >= 0 && m < 60)? m : 0;
    secs = (s >= 0 && s < 60)? s : 0;
}

public void setTime(int hrs, int mins, int secs) {
    this.hrs  = (hrs >= 0 && hrs < 24)? hrs : 0;
    this.mins = (mins >= 0 && mins < 60)? mins : 0;
    this.secs = (secs >= 0 && secs < 60)? secs : 0;
}

When it is used:

1. If you want to use the same name for method parameters and instance variables.
2. If you want to avoid confusion. When no chance for confusion, using
this is unnecessary and usually omitted.

NOTE #12:  Some Java programmer tools (including Eclipse) use this technique when writing code for you.

Creating Your Own Packages:

As you develop more and more classes, you can create your own packages and categorize your classes. You can import your classes in the same way that you import classes from the packages provided by Java.
You can create packages using a reserved word package. To create a package and add a class to the package so that the class can be used in a program, you do the following:

  1. Define the class to be public. (If the class is not public, it can only be used within the package.)
  2. Choose name for package
  3. Organize package (create subdirectories)

Programming Samples using User-Defined Classes:

Class Time (Time.java):

public class Time {

    private int hrs;  //store hours

    private int mins; //store minutes

    private int secs; //store seconds

 

 

    //Default constructor

    public Time() {

       hrs = 0;

       mins = 0;

       secs = 0;

    }

 

    //Alternate constructor with parameters, to set the time

    public Time(int h, int m, int s) {

       hrs = h;

       mins = m;

       secs = s;

    }

 

    //Method to set the time

    public void setTime(int h, int m, int s) {

        hrs =  (h >= 0 && h < 24)? h : 0;

        mins = (m >= 0 && m < 60)? m : 0;

        secs = (s >= 0 && s < 60)? s : 0;

    }

 

    //Method to return the hours

    public int getHours() {

        return hrs;

    }

 

    //Method to return the minutes

    public int getMinutes(){

        return mins;

    }

 

    //Method to return the seconds

    public int getSeconds() {

        return secs;

    }

 

    //Method to print time in military format

    //Time is printed in the form HH:MM:SS

    public void printTimeMilitary(){

        System.out.print((hrs < 10? "0": "") + hrs + ":");

        System.out.print((mins < 10? "0": "") + mins + ":");

        System.out.print((secs < 10? "0": "") + secs);

    }

 

    //Method to print time in standard format

    //Time is printed in the form HH:MM:SS AM/PM

    public void printTimeStandard(){

        System.out.print((hrs == 0 || hrs == 12? 12: hrs % 12) + ":");

        System.out.print((mins < 10? "0": "") + mins + ":");

        System.out.print((secs < 10? "0": "") + secs + " ");

        System.out.print((hrs < 12? "AM": "PM"));

    }

 

    //Method toString

    public String toString(){

         return hrs + ":" + mins + ":" + secs;

    }

   

    //Time advanced by one second. When 23:59:59 wrap around to 00:00:00
    public void increment(){
        secs++;
        if(secs > 59){
            secs = 0;
            mins++;
            if(mins > 59){
                mins = 0;
                hrs++;
                if(hrs > 23)
                    hrs = 0;
            }
        }
    }

 

    //Method to compare two times for equality

    public boolean equals(Time otherTime) {

        return (hrs == otherTime.hrs && mins == otherTime.mins && secs == otherTime.secs);

    }

 

    //Method to compare two times for less than

    public boolean lessThan(Time t) {

        return (hrs < t.hrs || hrs == t.hrs && mins < t.mins || hrs == t.hrs && mins == t.mins && secs < t.secs);

    }

 

    //Method to copy the time

    public void copy(Time otherTime) {

        hrs = otherTime.hrs;

        mins = otherTime.mins;

        secs = otherTime.secs;

    }

 

    //Method to return a copy of the time

    public Time getCopy() {

        Time temp = new Time();

        temp.hrs = hrs;

        temp.mins = mins;

        temp.secs = secs;

        return temp;

    }

}

//CLIENT #1: Program to test class Time (TimeClient1.java)
import java.util.Scanner;
public class TimeClient1 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        Time t1 = new Time(8, 15, 30);
        Time t2 = new Time();
        int hours, minutes, seconds;

        System.out.print("Initial time t1 (alternate constructor invoked) - military format: ");
        t1.printTimeMilitary();
        System.out.println();

        System.out.print("Initial time t1 (alternate constructor invoked) - standard format: ");
        t1.printTimeStandard();
        System.out.println();

        System.out.print("Initial time t2 (default constructor invoked) - military format: ");
        t2.printTimeMilitary();
        System.out.println();
        System.out.print("Initial time t2 (default constructor invoked) - standard format: ");
        t2.printTimeStandard();
        System.out.println();

        t2.setTime(9, 45, 35);
        System.out.print("t2 after call to setTime - military format: ");
        t2.printTimeMilitary();
        System.out.println();
        System.out.print("t2 after call to setTime - standard format: ");
        t2.printTimeStandard();
        System.out.println();

        if(t1.equals(t2))
            System.out.println("After call to equals: times are equal.");
        else
            System.out.println("After call to equals: times are NOT equal.");

        if(t1.lessThan(t2))
            System.out.println("After call to lessThan: t1 is less than t2.");
        else
            System.out.println("After call to lessThan: t1 is NOT less than t2.");

        System.out.print("Enter hours, minutes, and seconds: ");
        hours = input.nextInt();   //valid type??
        minutes = input.nextInt(); //valid type??
        seconds = input.nextInt(); //valid type??

        t1.setTime(hours, minutes, seconds);
        System.out.print("New time t1 after call to setTime - standard format: ");
        t1.printTimeStandard();
        System.out.println();

        t1.increment();
        System.out.print("New time t1 after call to increment - standard format: ");
        t1.printTimeStandard();
        System.out.println();

        t2.copy(t1);
        System.out.print("New t2 after call to copy - standard format: ");
        t2.printTimeStandard();
        System.out.println();
        System.out.println("Test toString for t2: " + t2);
     }
}

OUTPUT:
Initial time t1 (alternate constructor invoked) - military format: 08:15:30
Initial time t1 (alternate constructor invoked) - standard format: 8:15:30 AM
Initial time t2 (default constructor invoked) - military format: 00:00:00
Initial time t2 (default constructor invoked) - standard format: 12:00:00 AM
t2 after call to setTime - military format: 09:45:35
t2 after call to setTime - standard format: 9:45:35 AM
After call to equals: times are NOT equal.
After call to lessThan: t1 is less than t2.
Enter hours, minutes, and seconds: 10 11 12
New time t1 after call to setTime - standard format: 10:11:12 AM
New time t1 after call to increment - standard format: 10:11:13 AM
New t2 after call to copy - standard format: 10:11:13 AM
Test toString for t2: 10:11:13

//CLIENT #2: Program to test class Time (TimeClient2.java)
import java.util.*;

public class TimeClient2 {
    public static final int SIZE = 3;
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        Time[] t = new Time[SIZE];
        Time start = new Time();
        Time end = new Time(9, 15, 9);
        int hours, minutes, seconds;
        int i;

        System.out.print("Initial time end (alternate constructor invoked) - military format: ");
        end.printTimeMilitary();
        System.out.println();
        System.out.print("Initial time end (alternate constructor invoked) - standard format: ");
        end.printTimeStandard();
        System.out.println();

        System.out.print("Initial time start (default constructor invoked) - military format: ");
        start.printTimeMilitary();
        System.out.println();
        System.out.print("Initial time start (default constructor invoked) - standard format: ");
        start.printTimeStandard();
        System.out.println();

        start.setTime(9, 45, 35);
        System.out.print("start after call to setTime - military format: ");
        start.printTimeMilitary();
        System.out.println();
        System.out.print("start after call to setTime - standard format: ");
        start.printTimeStandard();
        System.out.println();

        if(start.equals(end))
            System.out.println("After call to equals: times are equal.");
        else
            System.out.println("After call to equals: times are NOT equal.");

        if(start.lessThan(end))
            System.out.println("After call to lessThan: start time is less than end time.");
        else
            System.out.println("After call to lessThan: start time is NOT less than end time.");

        //Array processing for times
        //Initialize with data entered by the user

        for(i = 0; i < t.length; i++) {
            System.out.print("Enter hours, minutes, and seconds: ");
            hours = input.nextInt();      //valid type??
            minutes = input.nextInt(); //valid type??
            seconds = input.nextInt(); //valid type??
            t[i] = new Time(hours, minutes, seconds);

        }
        // Print the times
        System.out.println("The times entered are (standard format):");
        for (i = 0; i < t.length; i++) {
           t[i].printTimeStandard();
           System.out.println();
        }
        System.out.println("Called setTime for start time. Time initialized with 23 59 55.");
        System.out.println("Increment 10 times. The times are (standard format):");
        start.setTime(23, 59, 55);
        for(i = 1; i <= 10; i++) {
           start.printTimeStandard();
           System.out.println();
           start.increment();
        }
    }
}

OUTPUT:
Initial time end (alternate constructor invoked) - military format: 09:15:09
Initial time end (alternate constructor invoked) - standard format: 9:15:09 AM
Initial time start (default constructor invoked) - military format: 00:00:00
Initial time start (default constructor invoked) - standard format: 12:00:00 AM
start after call to setTime - military format: 09:45:35
start after call to setTime - standard format: 9:45:35 AM
After call to equals: times are NOT equal.
After call to lessThan: start time is NOT less than end time.
Enter hours, minutes, and seconds: 1 2 3
Enter hours, minutes, and seconds: 11 22 33
Enter hours, minutes, and seconds: 12 23 34
The times entered are (standard format):
1:02:03 AM
11:22:33 AM
12:23:34 PM
Called setTime for start time. Time initialized with 23 59 55.
Increment 10 times. The times are (standard format):
11:59:55 PM
11:59:56 PM
11:59:57 PM
11:59:58 PM
11:59:59 PM
12:00:00 AM
12:00:01 AM
12:00:02 AM
12:00:03 AM
12:00:04 AM

Class Fraction:
//Class Fraction. File name: Fraction.java
//A fraction object is represented as num/denom
import java.util.Scanner;

public class Fraction {
    private int num;
    private int denom;

    //default constructor
    public Fraction() {
        num = 0;
        denom = 1;
    }

    //alternate constructor: 1 param
    public Fraction(int n) {
        num = n;
        denom = 1;
    }

    //alternate constructor: 2 param
    public Fraction(int n, int d) {
        num = n;
        denom = (d != 0)? d : 1;
    }

    public void setNum(int n) {
        num = n;
    }

    public void setDenom(int n) {
        denom = (n != 0)? n : 1;
    }

    public int getNum() {
        return num;
    }

    public int getDenom (){
        return denom;
    }

    public void read() {
        Scanner input =  new Scanner(System.in);
        System.out.print("Enter fraction(numerator denominator): ");
        num = input.nextInt();      //VALID TYPE??? Find solutions.
        denom = input.nextInt(); //VALID TYPE??? Find solutions.
    }

    public void write() {
        System.out.print(num + " / " + denom);
        System.out.println();
    }

   
    public String toString() {
        return num + " / " + denom;
    }

    private int greatestCommonDivisor(int n, int d) {//to be called by simplify()
        //Helper! Using the Euclidean algorithm
        int temp = n % d;
        while(temp > 0) {
            n = d;
            d = temp;
            temp = n % d;
        }
        return d;
    }

    public void simplify() {
        //fraction is reduced to lowest terms:
        //no positive integer evenly divides both num and denom
        int gcd;
        int absNum = Math.abs(num);
        if(num != 0 && absNum != 1 && denom != 1) {
            gcd = greatestCommonDivisor(absNum, denom);
            if(gcd > 1) {
                num = num / gcd;
                denom = denom / gcd;
            }
        }
    }

    public double transform(){
        return (double)num / denom;
    }

    public boolean equals(Fraction someFraction) {
        return(num == someFraction.num && denom == someFraction.denom);
    }

    /*Another version of equals:
    public boolean equals(Object obj) {
        if (obj instanceof Fraction) {
            Fraction someFraction = (Fraction) obj;
            return (this.num == someFraction.num && this.denom == someFraction.denom);
        }
        else   // not a Fraction object
            return false;
    } */

    public Fraction add(Fraction someFraction){
        Fraction result = new Fraction();
        result.num = num * someFraction.denom + denom * someFraction.num;
        result.denom = denom * someFraction.denom;
        result.simplify();
        return result;
    }

    public Fraction subtract(Fraction someFraction){
        Fraction result = new Fraction();
        result.num = num * someFraction.denom - denom * someFraction.num;
        result.denom = denom * someFraction.denom;
        result.simplify();
        return result;
    }

    public Fraction multiply(Fraction someFraction){
        Fraction result = new Fraction();
        result.num = num * someFraction.num;
        result.denom = denom * someFraction.denom;
        result.simplify();
        return result;
    }

    public Fraction divide(Fraction someFraction){
        Fraction result = new Fraction();
        result.num = num * someFraction.denom;
        result.denom = denom * someFraction.num;
        result.simplify();
        return result;
    }

    public Fraction getCopy () {
        return new Fraction(num, denom);
    }

    public void copy (Fraction someFraction) {
        num = someFraction.num;
        denom = someFraction.denom;
    }

       //add a helper method getInt! To be used by method read
}

//CLIENT: Program to test class Fraction, using a menu
import java.util.Scanner;
public class FractionClient1 {
    static Scanner input = new Scanner(System.in);
    public static void main(String[] args) {
        int x, y;
        Fraction f1 = new Fraction();
        Fraction f2 = new Fraction();
        Fraction result = new Fraction();
        int choice;
        choice = menu(); //priming read
        while(choice != 0) {
            f1.read();
            System.out.println("\tCall to toString --> Fraction 1 = " + f1);
            System.out.print("\tCall to write --> Fraction 1 = ");
            f1.write();
            f1.simplify();
            System.out.print("\tFraction 1 simplified = ");
            f1.write();
            f2.read();
            System.out.println("\tCall to toString --> Fraction 2 = " + f2);
            System.out.print("\tCall to write --> Fraction 2 = ");
            f2.write();
            f2.simplify();
            System.out.print("\tFraction 2 simplified = ");
            f2.write();
            switch(choice) {
                case 1:
                    result = f1.add(f2);
                    System.out.print("Their sum = ");
                    result.write();
                    System.out.printf("Decimal value for sum = %.2f\n" , result.transform());
                    break;
                case 2:
                    result = f1.subtract(f2);
                    System.out.print("Their difference = ");
                    result.write();
                    System.out.printf("Decimal value for difference = %.2f\n" , result.transform());
                    break;
                case 3:
                    result = f1.multiply(f2);
                    System.out.print("Their product = ");
                    result.write();
                    System.out.printf("Decimal value for product = %.2f\n" , result.transform());
                    break;
                case 4:
                    result = f1.divide(f2);
                    System.out.print("Their division = ");
                    result.write();
                    System.out.printf("Decimal value for division = %.2f\n" , result.transform());
                    break;
                case 5:
                    if(f1.equals(f2))
                        System.out.println("The fractions are equal.");
                    else
                        System.out.println("The fractions are NOT equal.");
            }
            choice = menu(); //next read
        }
    }

    public static int menu() {
        int choice;
        do {
            System.out.println("\nYour options for fraction operations are:");
            System.out.println("-----------------------------------------");
            System.out.println("\t1) Add 2 fractions");
            System.out.println("\t2) Subtract 2 fractions");
            System.out.println("\t3) Multiply 2 fractions");
            System.out.println("\t4) Divide 2 fractions");
            System.out.println("\t5) Compare fractions");
            System.out.println("\t0) EXIT");
            System.out.print("Please enter your option: ");
            choice = input.nextInt(); //VALID TYPE??? Find solutions. getInt()???
            System.out.println();
        } while(choice < 0 || choice > 5);
        return choice;
    }
}
OUTPUT:

Your options for fraction operations are:
-----------------------------------------
 1) Add 2 fractions
 2) Subtract 2 fractions
 3) Multiply 2 fractions
 4) Divide 2 fractions
 5) Compare fractions
 0) EXIT
Please enter your option: 1

Enter fraction(numerator denominator): 12 36
   Call to toString --> Fraction 1 = (12 / 36)
   Call to write --> Fraction 1 = 12 / 36
   Fraction 1 simplified = 1 / 3
Enter fraction(numerator denominator): 15 80
   Call to toString --> Fraction 2 = (15 / 80)
   Call to write --> Fraction 2 = 15 / 80
   Fraction 2 simplified = 3 / 16
Their sum = 25 / 48
Decimal value for sum = 0.52

Your options for fraction operations are:
-----------------------------------------
 1) Add 2 fractions
 2) Subtract 2 fractions
 3) Multiply 2 fractions
 4) Divide 2 fractions
 5) Compare fractions
 0) EXIT
Please enter your option: 2

Enter fraction(numerator denominator): 24 72
   Call to toString --> Fraction 1 = (24 / 72)
   Call to write --> Fraction 1 = 24 / 72
   Fraction 1 simplified = 1 / 3
Enter fraction(numerator denominator): 12 96
   Call to toString --> Fraction 2 = (12 / 96)
   Call to write --> Fraction 2 = 12 / 96
   Fraction 2 simplified = 1 / 8
Their difference = 5 / 24
Decimal value for difference = 0.21

Your options for fraction operations are:
-----------------------------------------
 1) Add 2 fractions
 2) Subtract 2 fractions
 3) Multiply 2 fractions
 4) Divide 2 fractions
 5) Compare fractions
 0) EXIT
Please enter your option: 3

Enter fraction(numerator denominator): 4 5
   Call to toString --> Fraction 1 = (4 / 5)
   Call to write --> Fraction 1 = 4 / 5
   Fraction 1 simplified = 4 / 5
Enter fraction(numerator denominator): 12 7
   Call to toString --> Fraction 2 = (12 / 7)
   Call to write --> Fraction 2 = 12 / 7
   Fraction 2 simplified = 12 / 7
Their product = 48 / 35
Decimal value for product = 1.37

Your options for fraction operations are:
-----------------------------------------
 1) Add 2 fractions
 2) Subtract 2 fractions
 3) Multiply 2 fractions
 4) Divide 2 fractions
 5) Compare fractions
 0) EXIT
Please enter your option: 4

Enter fraction(numerator denominator): 12 36
   Call to toString --> Fraction 1 = (12 / 36)
   Call to write --> Fraction 1 = 12 / 36
   Fraction 1 simplified = 1 / 3
Enter fraction(numerator denominator): 7 21
   Call to toString --> Fraction 2 = (7 / 21)
   Call to write --> Fraction 2 = 7 / 21
   Fraction 2 simplified = 1 / 3
Their division = 1 / 1
Decimal value for division = 1.00

Your options for fraction operations are:
-----------------------------------------
 1) Add 2 fractions
 2) Subtract 2 fractions
 3) Multiply 2 fractions
 4) Divide 2 fractions
 5) Compare fractions
 0) EXIT
Please enter your option: 5

Enter fraction(numerator denominator): 12 36
   Call to toString --> Fraction 1 = (12 / 36)
   Call to write --> Fraction 1 = 12 / 36
   Fraction 1 simplified = 1 / 3
Enter fraction(numerator denominator): 7 21
   Call to toString --> Fraction 2 = (7 / 21)
   Call to write --> Fraction 2 = 7 / 21
   Fraction 2 simplified = 1 / 3
The fractions are equal.

Your options for fraction operations are:
-----------------------------------------
 1) Add 2 fractions
 2) Subtract 2 fractions
 3) Multiply 2 fractions
 4) Divide 2 fractions
 5) Compare fractions
 0) EXIT
Please enter your option: 0


Additional Resources:
1. (Sun) API specification for version 6 of the Java Platform, Standard Edition: http://java.sun.com/javase/6/docs/api/
2. (Sun) The Java Tutorials, Classes and Objects: http://java.sun.com/docs/books/tutorial/java/javaOO/index.html


References:
[1] Building Java Programs: A Back to Basics Approach, by Stuart Reges, and Marty Stepp, Addison Wesley, 2008.
[2] Java Programming: From Problem Analysis to Program Design, by D.S. Malik, Thomson Course Technology, 2008