[设计模式] ------ 原型模式(浅拷贝和深拷贝)

原型模式

原型模式就是使用复制对象,创建出新的对象,并且不需要知道创建的细节(比如类的属性的赋值等等)。

基本使用(浅拷贝)

常见的就是使用Object的clone方法。需要让类实现Cloneable并重写clone方法,才能使用。

public class A implements Cloneable{
    private String code;
    private String name;

    public A(String code, String name) {
        this.code = code;
        this.name = name;
    }

    public void setCode(String code) {
        this.code = code;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        A a = new A("00","name1");
        A a1 = (A)a.clone();
        a1.setCode("01");
        A a2 = (A)a.clone();
        a2.setCode("02");
        System.out.println(a.hashCode());
        System.out.println(a1.hashCode());
        System.out.println(a2.hashCode());
    }
}

结果,很明显,三个对象是不一样的。

596512129
824318946
930990596

深拷贝

上面那种方式,是浅拷贝,即只能拷贝A类中的基本数据类型和String。对于A类中如果还有其他引用类型,浅拷贝是实现不了引用类型的拷贝的。
所以,需要进行对A深拷贝B,通常有两种方法(重写Clone方法和序列化)。

方法1、重写A的clone方法,并在方法中对B进行拷贝(当然B类中也要实现浅拷贝)。

public class B implements Serializable,Cloneable{
    private String id;

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class A implements Serializable,Cloneable{
    private String code;
    private String name;
    private B b;

    public A(String code, String name, B b) {
        this.code = code;
        this.name = name;
        this.b = b;
    }

    public void setCode(String code) {
        this.code = code;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        A a = (A)super.clone();
        // 在A的clone方法中,对A的B引用进行clone
        a.b = (B)a.b.clone();
        return a;
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        A a = new A("00","name1",new B());
        A a1 = (A)a.clone();
        a1.setCode("01");
        A a2 = (A)a.clone();
        a2.setCode("02");
        System.out.println(a.b.hashCode());
        System.out.println(a1.b.hashCode());
        System.out.println(a2.b.hashCode());
    }
}
执行结果:
596512129
824318946
930990596

看执行结果,每个A实例的B引用,都是不同的hashCode值,说明A中的B也被拷贝了。
如果将A中clone方法中的a.b = (B)a.b.clone();注释掉,可以得到最终打印出来的三个结果就是一样的。

方法2、序列化

序列化前提,A和B都要实现Serializable接口。
A和B的定义还是和方法1一样,我们只重写A中的clone方法。

@Override
    public Object clone() throws CloneNotSupportedException {

        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;
        try {
            //序列化,以对象的方式输出去
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            //反序列化,再以对象的方式写回来,所有的引用类型自然都会带上了
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);

            A copyResult = (A)ois.readObject();

            return copyResult;

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return null;
    }

最终效果也是随着A的clone,A中的B引用也被clone出新的一份来了。
上面的这个序列化只是一种实现思路,也可以用fastjson将A对象转成json字符串,再反转成A返回去,其实原理都是一样的。

实际应用场景

其实很好找,只要找到哪些类实现了Cloneable接口,大概率就是用到了所谓了原型模式。

比如Spring的Bean的创建,我都都知道默认是单例模式,那其实还可以知道Bean是多例的,这个就用到了原型模式。

发布了203 篇原创文章 · 获赞 186 · 访问量 21万+

猜你喜欢

转载自blog.csdn.net/java_zhangshuai/article/details/105236908