//Class Person:
public class Person implements Cloneable, Comparable{
    private String firstName;
    private String lastName;

    //Default constructor
    public Person() {
        firstName = " ";
        lastName = " ";
    }

    //Alternate Constructor
    public Person(String first, String last) {
        setName(first, last);
    }

    //Copy constructor
    public Person(Person original) {
        if(original == null)  {
            System.out.println("Fatal error for this Person.");
            System.exit(0);
        }
        firstName = original.firstName;
        lastName = original.lastName;
    }

    //clone; without this, compile error in client (.clone protected)
    public Object clone() {
        try  {
            return super.clone();
        }
        catch (CloneNotSupportedException e) {
            return null;
        }
    }

    public String toString(){
        return (lastName + ", " + firstName);
    }

    public void printLastFirst() {
        System.out.print(lastName + ", " + firstName);
    }

    public void printFirstLast() {
        System.out.print(firstName + " " + lastName);
    }

    public void setName(String first, String last) {
        firstName = first;
        lastName = last;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public boolean equals(Object obj) {
      if (obj instanceof Person) {
          Person otherPerson = (Person) obj;
          return lastName.equals(otherPerson.lastName) && firstName.equals(otherPerson.firstName);
      }
      else   // not a Person object
          return false;
    }

    public int compareTo(Object otherPerson) {
        Person temp = (Person) otherPerson;
        int compare = lastName.compareTo(temp.lastName);
        if(compare == 0)
            compare = firstName.compareTo(temp.firstName);
        return compare;
    }

}

//Class: Date
public class Date implements Cloneable, Comparable{
    private int month;
    private int day;
    private int year;

    //Default constructor
    public Date() {
        month = 0;
        day = 0;
        year = 1900;
    }

    //Alternate constructor
    public Date(int m, int d, int y) {
        month = (m >=1 && m <= 12)? m: 0;
        day = (d >=1 && d <= 31)? d: 0;
        year = (y >=1900 && y <= 2020)? y: 0;
        //setDate(m, d, y);
    }

    //Copy constructor
    public Date(Date original) {
        if(original == null)  {
            System.out.println("Fatal error for this Date.");
            System.exit(0);
        }
        month = original.month;
        day = original.day;
        year = original.year;
    }

    //clone; without this, compile error in client (.clone protected)
    public Object clone() {
        try  {
            return super.clone();
        }
        catch (CloneNotSupportedException e) {
            return null;
        }
    }

    public void setDate(int m, int d, int y) {
        month = (m >=1 && m <= 12)? m: 0;
        day = (d >=1 && d <= 31)? d: 0;
        year = (y >=1900 && y <= 2020)? y: 0;
    }

    public int getMonth() {
        return month;
    }

    public int getDay() {
        return day;
    }

    public int getYear() {
        return year;
    }

    public String toString() {
        return (month + "-" + day + "-" + year);
    }

    public boolean equals(Object obj) {
      if (obj instanceof Date) {
          Date otherDate = (Date) obj;
          return month == otherDate.month && day == otherDate.day && year == otherDate.year;
      }
      else   // not a Date object
          return false;
    }

    public int compareTo(Object otherDate) {
        Date temp = (Date) otherDate;
        int yearDiff = year - temp.year;
        if (yearDiff != 0)
            return yearDiff;
        int monthDiff = month - temp.month;
        if (monthDiff != 0)
            return monthDiff;
        return day - temp.day;
    }
}

//Class: PersonalInfo
public class PersonalInfo implements Cloneable, Comparable {
    private Person fullName; //(class) Person type
    private Date birthDate;  //(class) Date type
    private int personID;

    //Default constructor
    public PersonalInfo() {
        fullName = new Person();
        birthDate = new Date();
        personID = 0;
    }

    //Alternate constructor
    public PersonalInfo(String first, String last, int m, int d, int y, int id) {
        fullName = new Person(first, last);
        birthDate = new Date(m, d, y);
        personID = id;
    }

    //Copy constructor
    public PersonalInfo(PersonalInfo original) {
        if(original == null)  {
            System.out.println("Fatal error.");
            System.exit(0);
        }
        fullName = new Person(original.fullName);//Person has to have a copy constructor!
        birthDate = new Date(original.birthDate);//Date has to have a copy constructor!
        personID = original.personID;
    }

    public Object clone() {
        try {
            PersonalInfo copy = (PersonalInfo) super.clone();
            copy.birthDate = (Date) birthDate.clone();
            copy.fullName = (Person) fullName.clone();
            return copy;
        }
        catch (CloneNotSupportedException e) {
            return null;
        }
    }

    public void setPersonalInfo(String first, String last, int m, int d, int y, int id) {
        fullName.setName(first, last); //method setName in class Person
        birthDate.setDate(m, d, y);    //method setDate in class Date
        personID = id;
    }

    public Person getfullName( ){
        return new Person(fullName); //Person has to have a copy constructor!
    }

    public Date getBirthDate( ){
        return new Date(birthDate); //Date has to have a copy constructor!
    }

    public int getPersonID( ){
        return personID;
    }

    public String toString() {
       //return ("Name: " + fullName.toString() + "\nDate of birth: " + birthDate.toString() + "\nPersonal ID: " + personID);
       //Same as:
       return ("Name: " + fullName + "\nDate of birth: " + birthDate + "\nPersonal ID: " + personID);
    }

    public boolean equals(Object obj) {
        if (obj instanceof PersonalInfo) {
            PersonalInfo otherPerson = (PersonalInfo) obj;
            return  fullName.equals(otherPerson.fullName) && birthDate.equals(otherPerson.birthDate) && personID == otherPerson.personID;
        }
        else   // not a PersonalInfo object
            return false;
    }
     /*Another version for equals:
    public boolean equals(PersonalInfo otherPerson) {
        if (otherPerson == null)
            return false;
        else
            return  fullName.equals(otherPerson.fullName) && birthDate.equals(otherPerson.birthDate) && personID == otherPerson.personID;
    }*/

    public boolean sameName(Object obj) {
        if (obj instanceof PersonalInfo) {
            PersonalInfo otherPerson = (PersonalInfo) obj;
            return  fullName.equals(otherPerson.fullName);//equals from Person
        }
        else   // not a PersonalInfo object
          return false;
    }

    public boolean sameBirthDate(Object obj) {
        if (obj instanceof PersonalInfo) {
            PersonalInfo otherPerson = (PersonalInfo) obj;
            return  birthDate.equals(otherPerson.birthDate);//equals from Date
        }
        else   // not a PersonalInfo object
          return false;
    }

    public boolean sameID(Object obj) {
        if (obj instanceof PersonalInfo) {
            PersonalInfo otherPerson = (PersonalInfo) obj;
            return  personID == otherPerson.personID;
        }
        else   // not a PersonalInfo object
          return false;
    }

    public int compareTo(Object other) {
        PersonalInfo temp = (PersonalInfo) other;
        int compare = personID - temp.personID;
        if(compare == 0)
            compare = fullName.compareTo(temp.fullName);
        if(compare == 0)
            compare = birthDate.compareTo(temp.birthDate);
        return compare;
    }
}

//Class: ClientPersonalInfoClone
public class ClientPersonalInfoClone {
    public static void main(String[] args)  {
        PersonalInfo student = new PersonalInfo("Joe", "Doe", 1, 2, 1979, 12345);
        System.out.println("student info: \n" + student + "\n");
        PersonalInfo temp = (PersonalInfo) student.clone();
        System.out.println("Copy student using .clone(). After copy, temp info: \n" + temp + "\n");
        if (student == temp)
            System.out.println("Both student and temp refer to the same object.");
        else
            System.out.println("student and temp DON'T refer to the same object.");
        student.setPersonalInfo("Jane", "Smith", 11, 22, 1989, 11111);
        System.out.println("Changed student. After change, student info: \n" + student + "\n");
        System.out.println();
        System.out.println("temp info: \n" + temp + "\n");
    }
}

OUTPUT:

student info:
Name: Doe, Joe
Date of birth: 1-2-1979
Personal ID: 12345

Copy student using .clone(). After copy, temp info:
Name: Doe, Joe
Date of birth: 1-2-1979
Personal ID: 12345

student and temp DON'T refer to the same object.

Changed student. After change, student info:
Name: Smith, Jane
Date of birth: 11-22-1989
Personal ID: 11111

temp info:
Name: Doe, Joe
Date of birth: 1-2-1979
Personal ID: 12345