实验五:原型模式 一、实验目的

  1. 掌握原型模式的概念以及浅克隆与深克隆的工作原理,能够灵活使用原型模式解决相关问题。 二、实验内容 某销售管理系统设计并实现一个客户类Customer,在该类中包含姓名、电话以及存储客户地址的成员变量,客户地址的类型为Address,用浅克隆和深克隆分别实现Customer对象的复制,并比较这两种克隆方式的异同。 要求: 1、画出UML图(浅克隆和深克隆); 2、给出程序代码,程序代码有恰当的注释。(浅克隆和深克隆) 3、文档格式正确,排版清晰。 1、UML类图如下: image.png image.png 2、程序代码如下:
浅克隆
public class Address {
    private String province;
    private String city;
    private String street;

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }
}
public class Customer implements Cloneable {
    private String name;
    private Address address;

    public Customer(String name) {
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public Address getAddress() {
        return address;
    }

    @Override
    public Customer clone() {
        Object object = null;
        try {
            object = super.clone();
        } catch (CloneNotSupportedException e) {
            System.err.println("Not support cloneable");
        }
        return (Customer) object;
    }

}
public class Client {
    public static void main(String[] args) {
        Customer customer;
        Address address;

        customer = new Customer("张三");
        address = new Address();
        address.setProvince("江西省");
        address.setCity("南昌市");
        address.setStreet("北京西路");
        customer.setAddress(address);
        System.out.println(customer.getName() + "的地址:" + customer.getAddress().getProvince()
                + customer.getAddress().getCity() + customer.getAddress().getStreet());

        Customer customer2;
        customer2 = customer.clone();
        System.out.println(customer2.getName() + "的地址:" + customer2.getAddress().getProvince()
                + customer2.getAddress().getCity() + customer2.getAddress().getStreet());
    }
}

运行截图: image.png

深克隆
public class CustomerAddress implements Serializable {
    private String province;
    private String city;
    private String street;

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }
}
public abstract class CustomerPrototype implements Serializable {
    public abstract CustomerPrototype deepClone();
}
public class ConcreteCustomer extends CustomerPrototype {
    private String name;
    private CustomerAddress customerAddress;

    public ConcreteCustomer(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public CustomerAddress getCustomerAddress() {
        return customerAddress;
    }

    public void setCustomerAddress(CustomerAddress customerAddress) {
        this.customerAddress = customerAddress;
    }

    @Override
    public CustomerPrototype deepClone() {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this);

            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            try {
                return (CustomerPrototype) ois.readObject();
            } catch (ClassNotFoundException e) {
                return null;
            }
        } catch (IOException e) {
            return null;
        }
    }
}
public class Client {
    public static void main(String[] args) {
        ConcreteCustomer concreteCustomer;
        CustomerAddress customerAddress;

        customerAddress = new CustomerAddress();
        customerAddress.setProvince("江西省");
        customerAddress.setCity("南昌市");
        customerAddress.setStreet("北京西路");
        concreteCustomer = new ConcreteCustomer("张三");
        concreteCustomer.setCustomerAddress(customerAddress);
        System.out.println(concreteCustomer.getName() + "的地址:"
                + concreteCustomer.getCustomerAddress().getProvince()
                + concreteCustomer.getCustomerAddress().getCity()
                + concreteCustomer.getCustomerAddress().getStreet());

        ConcreteCustomer concreteCustomer2 = (ConcreteCustomer)concreteCustomer.deepClone();
        System.out.println(concreteCustomer2.getName() + "的地址:"
                + concreteCustomer2.getCustomerAddress().getProvince()
                + concreteCustomer2.getCustomerAddress().getCity()
                + concreteCustomer2.getCustomerAddress().getStreet());
    }
}

运行截图: image.png

3、请叙述Java中实现浅克隆和深克隆的工作原理。 Java中实现对象的克隆分两种一种是浅克隆一种是深克隆。首先java中Clone方法对于对象克隆的机制是:对象的基本数据类型的成员变量会被全部复制,引用类型的成员变量不会复制,只会复制该变量的引用,这样被克隆对象的引用类型的成员变量还是指向了原对象的同名引用类型的成员变量的堆内存空间,对其中一个对象的引用类型成员变量的修改会影响到另外一个被克隆对象或者源对象的引用类型的成员变量。 浅克隆:最普遍的克隆,即对象实现cloneable接口和重写clone方法,然后调用一次内部不做改写的clone方法克隆出一个对象,如果源对象内部存在引用类型的成员变量,那么就说该克隆是浅克隆,即对于引用类型属性,只克隆引用,两个对象的引用指向同一块内存地址,即同一个对象。 深克隆:基本数据类型变量和引用类型变量指向的对象都会被复制,即针对引用类型的成员变量真正的复制一份,重新开辟空间保存,这样两个引用类型属性互不影响。 实现深克隆的方法: (1)重写clone方法,clone中嵌套clone 1.这种方法的原理其实就是在需要克隆的对象以及该对象的引用类型的变量的类中全部实现cloneable接口,否则抛出CloneNotSupportedException将引用类型的变量也克隆一份。实际的操作上就是改写源对象的clone方法,在其内部嵌套克隆方法。 2.首先让源对象调用克隆方法获得克隆的对象,然后获得被克隆对象的引用类型的成员变量对象,对该成员变量对象调用克隆方法,此时成员变量对象也被克隆了一份,最后将该克隆的成员变量对象,设置为克隆对象的新的成员变量,再返回该被克隆的对象,即可实现深克隆。 (2)使用序列化流 1.其原理是:首先使要序列化的对象和该对象的引用类型成员变量对象的类都实现Serializable接口,将对象序列化到输出流中,然后再反序列化为对象就完成了完全的复制操作了,反序列化对象类似于运行新对象的构造方法。一般序列化到外部文件,此时只需要克隆对象,并不需要外部文件,因此我们的序列化和反序列化也应该在内存中进行最好,因此还使用到在内存操作数据的流ByteArrayOutputStream和ByteArrayInputStream,他们的输出和读取都默认是在内存的数组中操作。 2.首先创建一个ByteArrayOutputStream内存数组输出流,创建一个ObjectOutputStream序列化流,并传入内存数组输出流,使用序列化流的writeobject方法将要序列化的对象写入内部数组中,然后创建一个ByteArrayInputStream内存数组读取流,传入一个读取数据的数组,这个数组通过内存数组输出流的toByteArray方法获得,这个数组里面的数据其实就是已经被序列化成二进制数据的对象。最后创建一个ObjectInputStream反序列化流,并传入内存数组读取流,使用反序列化流的readobject方法将数组中的对象的信息,反序列化出来。反序列化出的对象就是一个新的对象,完成了深克隆。 1.当然还可以固定要被序列化对象的版本号,定义一个private static final long serialVersionUID,但需要注意静态的成员和transient关键字修饰的成员不能被序列化。