Fork me on GitHub

java基础之----23种设计模式(克隆模式)

概述

  克隆模式是一种比较简单的设计模式,基本从字面意思就可以明白这种设计模式是干什么的,简单来说就是造一个和原来一模一样的对象,就叫克隆模式。克隆模式分为两种,一种是浅度克隆,一种是深度克隆,至于这两者之前的区别,看下面的代码。

浅度克隆

实体类,没有特别的作用,作为原型对象(其实就是克隆对象,原型是一个别名)中的一个引用类型的属性

package com.gxl.demo.DesignPattern.clonepattern;

/**
 * Description: 实体类,作为ConcretePrototype类中的一个引用类型的属性。
 */
public class Address {
    public String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}
View Code

 

原型对象(克隆对象)

package com.gxl.demo.DesignPattern.clonepattern;


/**
 * Description: 克隆模式,浅度克隆
 */
public class ConcretePrototype implements Cloneable{

    public String name ;

    public Address address;

    public String getName() {
        return name;
    }

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

    public Address getAddress() {
        return address;
    }

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

    public ConcretePrototype clone(){
        Object obj = null;

        try {
            obj = super.clone();
            return (ConcretePrototype)obj;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }

    }

    public static void main(String[] args) {

        ConcretePrototype concretePrototype = new ConcretePrototype();
        Address address = new Address();
        address.setAddress("ganloucun");
        concretePrototype.setAddress(address);
        ConcretePrototype concretePrototype1 = concretePrototype.clone();
        //结果为true,证明Cloneable接口中的clone对于引用类型的属性clone的是地址,不是值
        System.out.println(concretePrototype1.getAddress() == concretePrototype.getAddress());
        //重新修改address,新克隆出来的对象的值也跟着变了
        address.setAddress("gunduzi");
        concretePrototype.setAddress(address);
        System.out.println(concretePrototype1.getAddress().getAddress());

    }
}
View Code

 

解析:原型对象实现Cloneable接口,可以调用Cloneable中的clone方法,这个方法是一个native方法,就是说这个方法不是用java语言实现的,是使用C语言或者C++语言实现的,所以我们看不到源码,但是要明白这个方法的返回,这个方法的返回就是实现这个接口的类的对象的克隆。但是原生的clone方法是一个浅度克隆,什么是浅度克隆呢?浅度克隆就是如果类中的属性如果是非引用类型的就直接把值克隆到新对象的对应属性中,如果类中的属性是引用类型的,就像例子中的address,是一个对象,是一个引用类型的属性,克隆的时候只是把原对象的这个字段对应的的引用克隆到新对象中,也就是说,新对象的address字段和旧对象address所引用的是同一个地址。

深度克隆

深度克隆有两种实现方式,一种实现方式是原型对象中的引用类型属性也实现了Cloneable接口。另一种方式是,原型对象和原型对象中引用类型的属性都实现了Serializable接口,然后通过序列化的方法实现。

通过Cloneable实现

实体类

package com.gxl.demo.DesignPattern.clonepattern;

/**
 * Description: 实体类
 */
public class AddressDeep implements Cloneable{

    public String address;

    public String getAddress() {
        return address;
    }

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

    public AddressDeep clone(){
        Object obj = null;

        try {
            obj = super.clone();
            return (AddressDeep)obj;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }
}
View Code

原型对象

package com.gxl.demo.DesignPattern.clonepattern;

/**
 * Description: 克隆模式,深度克隆,通过Cloneable实现
 */
public class ConcretePrototypeDeep implements Cloneable{

    public String name ;
    public AddressDeep addressDeep;

    public String getName() {
        return name;
    }

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

    public AddressDeep getAddressDeep() {
        return addressDeep;
    }

    public void setAddressDeep(AddressDeep addressDeep) {
        this.addressDeep = addressDeep;
    }

    public ConcretePrototypeDeep clone(){
        Object obj = null;

        try {
            obj = super.clone();
            ConcretePrototypeDeep concretePrototypeDeep = (ConcretePrototypeDeep)obj;
            AddressDeep addressDeep = this.addressDeep.clone();
            concretePrototypeDeep.addressDeep = addressDeep;
            return concretePrototypeDeep;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void main(String[] args) {
        ConcretePrototypeDeep concretePrototypeDeep = new ConcretePrototypeDeep();
        AddressDeep addressDeep = new AddressDeep();
        addressDeep.setAddress("ganloucun");
        concretePrototypeDeep.setAddressDeep(addressDeep);
        ConcretePrototypeDeep concretePrototypeDeep1 = concretePrototypeDeep.clone();
        //输出false,证明克隆出来的新对象的引用类型属性重新赋值,而不是原来属性的地址
        System.out.println(concretePrototypeDeep.getAddressDeep() == concretePrototypeDeep1.getAddressDeep());
        System.out.println(concretePrototypeDeep1.getAddressDeep().getAddress());
    }
}
View Code

解析:原型对象中的引用类型属性addressDeep也实现了Cloneable接口,这样在克隆原型对象的时候,把addressDeep对象也克隆一下,然后再把克隆的值赋值给新对象。也就是说无论原型对象中有多少引用类型变量,如果要实现深度克隆,原型对象中的引用类型的属性都要实现Cloneable接口。

通过Serializable实现

实体类

package com.gxl.demo.DesignPattern.clonepattern;

import java.io.Serializable;

/**
 *  Description: 实体类
 */
public class AddressStream implements Serializable {
    public String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}
View Code

原型对象

package com.gxl.demo.DesignPattern.clonepattern;

import java.io.*;

/**
 * Description: 深度克隆模式,通过继承Serializable接口,通过流的方式实现
 */
public class ConcretePrototypeStream implements Serializable {

    public String name;

    public AddressStream addressStream;

    public String getName() {
        return name;
    }

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

    public AddressStream getAddressStream() {
        return addressStream;
    }

    public void setAddressStream(AddressStream addressStream) {
        this.addressStream = addressStream;
    }

    public ConcretePrototypeStream clone(){
        try {
            //将对象写入流
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(this);

            //将对象从流中取出
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
            ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
            return (ConcretePrototypeStream)objectInputStream.readObject();

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void main(String[] args) {
        ConcretePrototypeStream concretePrototypeStream = new ConcretePrototypeStream();
        AddressStream addressStream = new AddressStream();
        addressStream.setAddress("ganloucun");
        concretePrototypeStream.setAddressStream(addressStream);
        ConcretePrototypeStream concretePrototypeStream1 = concretePrototypeStream.clone();
        System.out.println(concretePrototypeStream.getAddressStream() == concretePrototypeStream1.getAddressStream());
    }
}
View Code

解析:原型对象实现了Serializable接口,表明这个类可以序列化,然后用流的方式把原来的对象写入到流中,之后重新读出来,就可以实现深度克隆。

深度克隆和浅度克隆的区别

  通过上面的叙述,大家应该基本可以搞清楚这两者之间的区别了,不过这里还是在重新叙述一下,深度和浅度的主要区别就体现在原型对象的引用类型变量的克隆方式上,浅度克隆是把旧对象的引用类型变量的地址传给新对象的引用类型的变量。而深度克隆是把旧对象的引用类型变量的值传给新对象的引用类型变量。

posted @ 2020-01-19 10:06  猿起缘灭  阅读(1265)  评论(0编辑  收藏  举报