原型模式超详解!

深拷贝与浅拷贝

当系统中需要大量创建相同或者相似的对象时,就可以通过“原型设计模式”来实现。原型模式是属于创建型模式。
核心思想:通过拷贝指定的"原型实例"来创建跟对象一样的新对象,一句话就是:克隆指定对象。

浅拷贝与深拷贝:

浅拷贝:拷贝一个对象时,只复制对象内部的基本数据类型,对象内部指向引用类型或者数组不复制。(其实也复制了,但是复制的是一个指针,新拷贝的对象和原型对象指向的是同一个实例)
深拷贝:拷贝一个对象时,对象内部的无论基本类型还是引用类型还是数组都复制

//TODO 浅拷贝和深拷贝的原理图
浅拷贝

这里是引用
深拷贝
这里是引用

1.通过Cloneable

把要克隆的类实现Cloneable接口,并且实现Cloneable中的clone方法。

浅拷贝

Dog类

package com.jc.prototype;

import java.io.Serializable;

/**
* @program: 设计模式
*
* @description: 
*
* @author: Mr.Wang
*
* @create: 2021-06-13 19:34
**/
public class Dog {
    private int age;

    public Dog(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "age=" + age +
                '}';
    }

    public int getAge() {
        return age;
    }


    public void setAge(int age) {
        this.age = age;
    }
}



Sheep类

package com.jc.prototype;

/**
 * @program: 设计模式
 * @description:
 * @author: Mr.Wang
 * @create: 2021-06-13 18:12
 **/
public class Sheep implements Cloneable {
   private int age;
   private char sex;
   private Dog friend;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public Dog getFriend() {
        return friend;
    }

    public void setFriend(Dog friend) {
        this.friend = friend;
    }

    public Sheep(int age, char sex, Dog friend) {
        this.age = age;
        this.sex = sex;
        this.friend = friend;
    }
    public Sheep(){

    }

    @Override
    protected Sheep clone()  {

        Sheep clone = null;
        try {
            //这里完成基本类型克隆
            clone = (Sheep) super.clone();

        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return clone;
    }
    @Override
    public String toString() {
        return "Sheep{" +
                "age=" + age +
                ", sex=" + sex +
                ", friend=" + friend +
                '}';
    }
}


Client类

package com.jc.prototype;

import java.util.function.DoubleFunction;

/**
 * @program: 设计模式
 * @description:
 * @author: Mr.Wang
 * @create: 2021-06-13 18:21
 **/
public class Client {

    public static void main(String[] args) {
        Dog friend = new Dog(3);

        Sheep sheep = new Sheep(22,'男',friend);

        Sheep clone = sheep.clone();

        System.out.println("sheep' hashcode:"+sheep.hashCode()+", "+sheep+", friend' hashcode:"+sheep.getFriend().hashCode());
        System.out.println("clone' hashcode:"+clone.hashCode()+", "+clone+", friend' hashcode:"+clone.getFriend().hashCode());

        /**
         * 打印结果
         *sheep' hashcode:460141958, Sheep{age=22, sex=男, friend=Dog{age=3}}, friend' hashcode:1163157884
         *clone' hashcode:1956725890, Sheep{age=22, sex=男, friend=Dog{age=3}}, friend' hashcode:1163157884
         */
    }
}


通过打印结果可以验证这是浅拷贝,浅拷贝的不好就是,如果对引用类型数据进行修改的话,clone后的数据会跟着修改(看前边的原理图)。
例如我们的这个代码,克隆后重新设置下年龄。

 Dog friend = new Dog(3);

        Sheep sheep = new Sheep(22,'男',friend);
        Sheep clone = sheep.clone();
        //拷贝完了 如果我们在对Dog进行修改的话  clone后的对象还是会跟着更改,因为他们指向的都是friend实例
        friend.setAge(5);

打印结果为

sheep’ hashcode:460141958, Sheep{age=22, sex=男, friend=Dog{age=5}}, friend’ hashcode:1163157884
clone’ hashcode:1956725890, Sheep{age=22, sex=男, friend=Dog{age=5}}, friend’ hashcode:1163157884

那么我们如何通过Cloneable深拷贝呢?
解决办法:让引用类型也实现clone接口,在Sheep的clone方法中分两步:
①对基本类型拷贝②对引用类型的真正复制.(新new一个)

深拷贝

1.让Dog类也实现Clone接口

package com.jc.prototype;

import java.io.Serializable;

/**
* @program: 设计模式
*
* @description: 
*
* @author: Mr.Wang
*
* @create: 2021-06-13 19:34
**/
public class Dog  implements Cloneable {
    private int age;

    public Dog(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "age=" + age +
                '}';
    }

    public int getAge() {
        return age;
    }


    public void setAge(int age) {
        this.age = age;
    }

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

2.Sheep中的clone方法改为这样

 @Override
    protected Sheep clone()  {

        Sheep clone = null;
        try {
            //1.这里完成基本类型克隆
            clone = (Sheep) super.clone();

            //2.完成引用类型的真正克隆
            clone.friend = (Dog) friend.clone();
            
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return clone;
    }

Client类

package com.jc.prototype;

import java.util.function.DoubleFunction;

/**
 * @program: 设计模式
 * @description:
 * @author: Mr.Wang
 * @create: 2021-06-13 18:21
 **/
public class Client {

    public static void main(String[] args) {
        Dog friend = new Dog(3);

        Sheep sheep = new Sheep(22,'男',friend);
        Sheep clone = sheep.clone();
        //拷贝完了 如果我们在对Dog进行修改的话  clone后的对象还是会跟着更改,因为他们指向的都是friend实例
        friend.setAge(5);

        System.out.println("sheep' hashcode:"+sheep.hashCode()+", "+sheep+", friend' hashcode:"+sheep.getFriend().hashCode());
        System.out.println("clone' hashcode:"+clone.hashCode()+", "+clone+", friend' hashcode:"+clone.getFriend().hashCode());

        /**
         * 打印结果
         *sheep' hashcode:460141958, Sheep{age=22, sex=男, friend=Dog{age=3}}, friend' hashcode:1163157884
         *clone' hashcode:1956725890, Sheep{age=22, sex=男, friend=Dog{age=3}}, friend' hashcode:356573597
         */
    }
}

通过打印结果可以看到Sheep实例和clone实例hashcode不同,friend的hashcode也不同,就算克隆后对friend进行修改,也不会影响clone的实例,这才是真正意义上的克隆,也就是深拷贝

2.通过对象序列化实现深拷贝

Dog类

package com.jc.prototype;

import java.io.*;

/**
* @program: 设计模式
*
* @description: 
*
* @author: Mr.Wang
*
* @create: 2021-06-13 19:34
**/
public class Dog  implements Serializable{
    private int age;

    public Dog(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "age=" + age +
                '}';
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

Sheep类

package com.jc.prototype;

import java.io.*;

/**
 * @program: 设计模式
 * @description:
 * @author: Mr.Wang
 * @create: 2021-06-13 18:12
 **/
public class Sheep implements Serializable {
   private int age;
   private char sex;
   private Dog friend;

    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public Dog getFriend() {
        return friend;
    }

    public void setFriend(Dog friend) {
        this.friend = friend;
    }

    public Sheep(int age, char sex, Dog friend) {
        this.age = age;
        this.sex = sex;
        this.friend = friend;
    }
    public Sheep(){

    }


    public Sheep deepclone(){
        ByteArrayOutputStream bos = null;
        ByteArrayInputStream bis = null;
        ObjectOutputStream oos = null;
        ObjectInputStream ois = null;
        try {
            //序列化
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this);//把当前这个对象以对象流方式输出
            //反序列化
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);//反序列化

            Sheep clone = (Sheep) ois.readObject();

            bos.flush();
            oos.flush();

            return clone;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }finally {
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (oos != null) {
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (ois != null) {
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
    @Override
    public String toString() {
        return "Sheep{" +
                "age=" + age +
                ", sex=" + sex +
                ", friend=" + friend +
                '}';
    }
}

Client类

package com.jc.prototype;

import java.util.function.DoubleFunction;

/**
 * @program: 设计模式
 * @description:
 * @author: Mr.Wang
 * @create: 2021-06-13 18:21
 **/
public class Client {

    public static void main(String[] args) {
        Dog friend = new Dog(3);

        Sheep sheep = new Sheep(22,'男',friend);
        Sheep clone = sheep.deepclone();
        //拷贝完了 如果我们在对Dog进行修改的话  clone后的对象还是会跟着更改,因为他们指向的都是friend实例
        friend.setAge(5);

        System.out.println("sheep' hashcode:"+sheep.hashCode()+", "+sheep+", friend' hashcode:"+sheep.getFriend().hashCode());
        System.out.println("clone' hashcode:"+clone.hashCode()+", "+clone+", friend' hashcode:"+clone.getFriend().hashCode());

        /**
         * 打印结果
         *sheep' hashcode:2133927002, Sheep{age=22, sex=男, friend=Dog{age=5}}, friend' hashcode:1836019240
         *clone' hashcode:381259350, Sheep{age=22, sex=男, friend=Dog{age=3}}, friend' hashcode:2129789493
         */
    }
}

通过打印结果可以看出来完成了深拷贝。

3.在spring源码中引用

test方法

 @Test
    public void test2(){

        AnnotationConfigApplicationContext ApplicationContext =
                new AnnotationConfigApplicationContext(txconfig.class);
        System.out.println("容器初始化完成");

        Dog dog =(Dog) ApplicationContext.getBean("dog");
        System.out.println("dog' hashcode:"+dog.hashCode());
        Dog dog1 =(Dog) ApplicationContext.getBean("dog");
        System.out.println("dog1' hashcode:"+dog1.hashCode());
        ApplicationContext.close();

        /**Scope=prototype
         *
         * @Bean
         * @Scope("prototype")
         * public Dog dog(){
         *    return new Dog();
         *  }
         * 打印结果
         * dog' hashcode:2134607032
         * dog1' hashcode:2134607032
         */

        /**Scope=singleton
         *
         * @Bean
         * @Scope("singleton")
         * public Dog dog(){
         *    return new Dog();
         *  }
         * 打印结果
         *dog' hashcode:2134607032
         *dog1' hashcode:1470344997
         */
    }

通过hashcode可以看出来,当Scope=prototype,即原型模式创建对象;当Scope=singleton时,即单例模式创建对象。

debug追踪一下,发现在AbstractFactory中的doGetBean方法中有一段这样代码

				if (mbd.isSingleton()) {//如果是单例模式的话,创建单例。
	                    sharedInstance = this.getSingleton(beanName, () -> {
                        try {
                            return this.createBean(beanName, mbd, args);
                        } catch (BeansException var5) {
                            this.destroySingleton(beanName);
                            throw var5;
                        }
                    });
                    beanInstance = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                } else if (mbd.isPrototype()) {//如果是原型模式的话,创建一个原型实例。
                    var12 = null;

                    Object prototypeInstance;
                    try {
                        this.beforePrototypeCreation(beanName);
                        prototypeInstance = this.createBean(beanName, mbd, args);
                    } finally {
                        this.afterPrototypeCreation(beanName);
                    }

                    beanInstance = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                } 

通过if (mbd.isSingleton())和else if (mbd.isPrototype())来判断是通过单例模式创建对象还是原型模式创建对象。

总结

1.相比于之前我们复制对象,原型设计模式效率更高。
2.如果原型对象发生变化,克隆的对象也会发生变化,无需修改代码。(浅克隆)

不好

1.实现深克隆的时候可能需要比较复杂的代码
2需要为每一个类配置一个克隆方法,对后期拓展的类不难,,但对已有的类进行改造时,需要修改其源代码,违背了ocp原则。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值