import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class DeepCopy {

    public static void main(String[] args) {
        Person p1 = new Person("Clark", "Kent", new Address("1 World Center", "Metropolis", "NY", "010101"));
        Person p2 = p1;

        System.out.printf("Demonstrate shallow copy.  Both are the same object.%n");
        System.out.printf("Person p1 =%s%n", p1);
        System.out.printf("Person p2 =%s%n", p2);
        System.out.printf("Set city on person 2.  City on both objects is changed.%n");
        p2.getAddress().setCity("New York");
        System.out.printf("Person p1 =%s%n", p1);
        System.out.printf("Person p2 =%s%n", p2);

        p1 = new Person("Clark", "Kent", new Address("1 World Center", "Metropolis", "NY", "010101"));
        p2 = new Person(p1);
        System.out.printf("%nDemonstrate copy constructor.  Object p2 is a deep copy of p1.%n");
        System.out.printf("Person p1 =%s%n", p1);
        System.out.printf("Person p2 =%s%n", p2);
        System.out.printf("Set city on person 2.  City on objects is different.%n");
        p2.getAddress().setCity("New York");
        System.out.printf("Person p1 =%s%n", p1);
        System.out.printf("Person p2 =%s%n", p2);

        p2 = (Person) deepCopy(p1);
        System.out.printf("%nDemonstrate serialization.  Object p2 is a deep copy of p1.%n");
        System.out.printf("Person p1 =%s%n", p1);
        System.out.printf("Person p2 =%s%n", p2);
        System.out.printf("Set city on person 2.  City on objects is different.%n");
        p2.getAddress().setCity("New York");
        System.out.printf("Person p1 =%s%n", p1);
        System.out.printf("Person p2 =%s%n", p2);

        p2 = (Person) p1.clone();
        System.out.printf("%nDemonstrate cloning.  Object p2 is a deep copy of p1.%n");
        System.out.printf("Person p1 =%s%n", p1);
        System.out.printf("Person p2 =%s%n", p2);
        System.out.printf("Set city on person 2.  City on objects is different.%n");
        p2.getAddress().setCity("New York");
        System.out.printf("Person p1 =%s%n", p1);
        System.out.printf("Person p2 =%s%n", p2);
    }

    
    private static Object deepCopy(Object object) {
        try {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ObjectOutputStream outputStrm = new ObjectOutputStream(outputStream);
            outputStrm.writeObject(object);
            ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
            ObjectInputStream objInputStream = new ObjectInputStream(inputStream);
            return objInputStream.readObject();
        }
        catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static class Address implements Serializable, Cloneable {

        private static final long serialVersionUID = -7073778041809445593L;

        private String street;
        private String city;
        private String state;
        private String postalCode;
        public String getStreet() {
            return street;
        }
        public String getCity() {
            return city;
        }
        public void setCity(String city) {
            this.city = city;
        }
        public String getState() {
            return state;
        }
        public String getPostalCode() {
            return postalCode;
        }

        @Override
        public String toString() {
            return "[street=" + street + ", city=" + city + ", state=" + state + ", code=" + postalCode + "]";
        }

        public Address(String s, String c, String st, String p) {
            street = s;
            city = c;
            state = st;
            postalCode = p;
        }