6.設計模式-原型模式-深淺/克隆

設計模式-原型模式-深/淺克隆

1. 原型模式在 Spring 框架中應用

1.1 實體類Student
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private String name;

    private String sex;

    private String address;

}
1.2 配置文件applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="student" class="cn.justweb.pojo.Student" p:name="濤哥" p:sex="nan" p:address="南京" scope="prototype"></bean>

</beans>

以及spring的依賴。

1.3 測試

public class TestStudent {
    @Test
    public void test(){
        ApplicationContext ctx = new ClassPathXmlApplicationContext( "applicationContext.xml" );

        //同時指定bean的id值和類型,來拿到對象
        Student student = ctx.getBean( "student", Student.class );

        System.out.println( "student = " + student );
    }
}
1.4 通過debug調試追蹤getBean()方法
	// 1. 首先進到方法裏面
	@Override
	public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
		assertBeanFactoryActive();
		return getBeanFactory().getBean(name, requiredType);
	}
	// 2. 通過getBeanFactory()方法拿到ConfigurableListableBeanFactory對象
	@Override
	public final ConfigurableListableBeanFactory getBeanFactory() {
		synchronized (this.beanFactoryMonitor) {
			if (this.beanFactory == null) {
				throw new IllegalStateException("BeanFactory not initialized or already closed - " +
						"call 'refresh' before accessing beans via the ApplicationContext");
			}
			return this.beanFactory;
		}
	}
	// 3. 調用getBean()
	@Override
	public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
		return doGetBean(name, requiredType, null, false);
	}

	// 4.進入到此方法中 Create bean instance.
	//	prototypeInstance = createBean(beanName, mbd, args);
	@SuppressWarnings("unchecked")
	protected <T> T doGetBean(
			final String name, 
            final Class<T> requiredType, 
             final Object[] args, 
            boolean typeCheckOnly) throws BeansException 
    {

        // 省略....
				// Create bean instance.
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
						@Override
						public Object getObject() throws BeansException {
							try {
								return createBean(beanName, mbd, args);
							}
							catch (BeansException ex) {
								// Explicitly remove instance from singleton cache: It might have been put there
								// eagerly by the creation process, to allow for circular reference resolution.
								// Also remove any beans that received a temporary reference to the bean.
								destroySingleton(beanName);
								throw ex;
							}
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

				else if (mbd.isPrototype()) {
					// It's a prototype -> create a new instance.
					Object prototypeInstance = null;
					try {
						beforePrototypeCreation(beanName);
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}

				else {
					String scopeName = mbd.getScope();
					final Scope scope = this.scopes.get(scopeName);
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
					}
				
                    
                    // 省略.....
                    
		return (T) bean;
	}

2. 原型模式

  1. 原型模式(Prototype 模式)是指:用 原型實例指定創建對象的種類,並且通過拷貝這些原型,創建新的對象。
  2. 原型模式是一種創建型設計模式,允許一個對象再創建另外一個可定製的對象,無需知道如何創建的細節。
  3. 工作原理是:通過將一個原型對象傳給那個要發動創建的對象,這個要發動創建的對象通過請求原型對象拷貝它們自己來實施創建,即 對象.clone()

Sheep實體類


/**
 * @Date 2020/6/11 11:17
 * @Version 10.21
 * @Author DuanChaojie
 */
public class Sheep implements Cloneable{
    private String name;
    private Integer age;
    private String color;

    public Sheep() {
    }

    public Sheep(String name, Integer age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                '}';
    }
}
2.1 傳統方式解決克隆羊問題
   /**
 * @Date 2020/6/11 11:19
 * @Version 10.21
 * @Author DuanChaojie
 */
public class Client {
    public static void main(String[] args) {
        Sheep sheep = new Sheep("MM", 3, "yellow");

        // 我們很容易想到的克隆對象
        Sheep sheep1 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        
        /**
         * sheep.hashCode() = 1163157884
         * sheep1.hashCode() = 1956725890
         * sheep2.hashCode() = 356573597
         * sheep3.hashCode() = 1735600054
         */
        System.out.println("sheep.hashCode() = " + sheep.hashCode());
        System.out.println("sheep1.hashCode() = " + sheep1.hashCode());
        System.out.println("sheep2.hashCode() = " + sheep2.hashCode());
        System.out.println("sheep3.hashCode() = " + sheep3.hashCode());
    }
}
2.2 原型模式解決克隆羊問題

Java 中 Object 類是所有類的根類,Object 類提供了一個 clone()方法,該方法可以將一個 Java 對象複製
一份,但是需要實現 clone的Java類必須要實現一個接口Cloneable,該接口表示該類能夠複製且具有複製的能力 — 原型模式

第一步:Sheep實體類實現Cloneable接口
第二步:Sheep中重寫clone()方法
    /**
    *克隆該實例,使用默認的 clone 方法來完成
     * */
    @Override
    protected Object clone() {
        Sheep sheep = null;
        try {
            sheep = (Sheep) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return sheep;
    }
第三步:使用clone()方法克隆Sheep對象
/**
 * @Date 2020/6/11 11:19
 * @Version 10.21
 * @Author DuanChaojie
 */
public class Client {
    public static void main(String[] args) {
        Sheep sheep = new Sheep("MM", 3, "yellow");

        // 使用原型模式
        Sheep sheep4 = (Sheep)sheep.clone();
        Sheep sheep5 = (Sheep)sheep.clone();
        
        /**
         * sheep4 = Sheep{name='MM', age=3, color='yellow'}
         * sheep5 = Sheep{name='MM', age=3, color='yellow'}
         */
        System.out.println("sheep4 = " + sheep4);
        System.out.println("sheep5 = " + sheep5);

        /**
         * sheep4.hashCode() = 1163157884
         * sheep5.hashCode() = 1956725890
         */
        System.out.println("sheep4.hashCode() = " + sheep4.hashCode());
        System.out.println("sheep5.hashCode() = " + sheep5.hashCode());
    }
}

3. 深拷貝和淺拷貝

3.1 淺拷貝
  1. 對於數據類型是基本數據類型的成員變量,淺拷貝會直接進行值傳遞,也就是將該屬性值複製一份給新的對象。
  2. 對於數據類型是引用數據類型的成員變量,比如說成員變量是某個數組、某個類的對象等,那麼淺拷貝會進行
    引用傳遞,也就是隻是將該成員變量的引用值(內存地址)複製一份給新的對象。因爲實際上兩個對象的該成
    員變量都指向同一個實例。在這種情況下,在一個對象中修改該成員變量會影響到另一個對象的該成員變量值。
  3. 前面我們克隆羊就是淺拷貝。
  4. 淺拷貝是使用默認的 clone()方法來實現 sheep = (Sheep) super.clone();
3.2 深拷貝
  1. 複製對象的所有基本數據類型的成員變量值
  2. 爲所有引用數據類型的成員變量申請存儲空間,並複製每個引用數據類型成員變量所引用的對象,直到該對象可達的所有對象。也就是說, 對象進行深拷貝要對整個對象( 包括對象的引用類型) 進行拷貝。
  3. 深拷貝實現方式:
    1. 重寫 clone 方法來實現深拷貝
    2. 通過對象序列化實現深拷貝(推薦)
準備工作

測試深拷貝,需要讓DeepCloneTarget類作爲DeepProtoType類的屬性。

DeepProtoType類

/**
 * @Date 2020/6/11 15:55
 * @Version 10.21
 * @Author DuanChaojie
 */
public class DeepProtoType implements Serializable, Cloneable{

    // String屬性
    public String name;
    // 引用類型
    public DeepCloneTarget deepCloneTarget;
    
    public DeepProtoType() {
        super();
    }
        
    @Override
    public String toString() {
        return "DeepProtoType{" +
                "name='" + name + '\'' +
                ", deepCloneTarget=" + deepCloneTarget +
                '}';
    }

}

DeepProtoType類

/**
 * @Date 2020/6/11 15:55
 * @Version 10.21
 * @Author DuanChaojie
 */
public class DeepCloneTarget implements Serializable, Cloneable {

    private String cloneName;

    private Integer cloneAge;

    /**
     * 構造器
     */
    public DeepCloneTarget(String cloneName, Integer cloneAge) {
        this.cloneName = cloneName;
        this.cloneAge = cloneAge;
    }

    /**
     * 因爲該類的屬性,是String和Integer
     * 因此我們這裏使用默認的clone完成即可,即通過淺拷貝拷貝對象
     * @return
     * @throws CloneNotSupportedException
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
        
    @Override
    public String toString() {
        return "DeepCloneTarget{" +
                "cloneName='" + cloneName + '\'' +
                ", cloneAge=" + cloneAge +
                '}';
    }
}
方案一:重寫 clone 方法實現深拷貝

在DeepProtoType類中重寫clone()方法

	//深拷貝 - 方式 1 使用clone 方法
	@Override
    protected Object clone() throws CloneNotSupportedException {

        Object deep = null;
        
		//這裏完成對基本數據類型(屬性)和String的克隆
        deep = super.clone();

        //對引用類型的屬性,進行單獨處理
        DeepProtoType deepProtoType = (DeepProtoType) deep;

        deepProtoType.deepCloneTarget = (DeepCloneTarget) deepCloneTarget.clone();

        return deepProtoType;
    }

測試結果

/**
 * @Date 2020/6/11 16:07
 * @Version 10.21
 * @Author DuanChaojie
 */
public class client {
    public static void main(String[] args) throws CloneNotSupportedException {
        DeepProtoType dpt = new DeepProtoType();
        dpt.name = "MM";
        dpt.deepCloneTarget = new DeepCloneTarget("DD",17);

        /**
         * dpt = DeepProtoType{name='MM', deepCloneTarget=DeepCloneTarget{cloneName='DD', cloneAge=17}}
         * dpt.deepCloneTarget.hashCode() = 1163157884
         * dpt2 = DeepProtoType{name='MM', deepCloneTarget=DeepCloneTarget{cloneName='DD', cloneAge=17}}
         * dpt2.deepCloneTarget.hashCode() = 1956725890
         */
        System.out.println("dpt = " + dpt);
        System.out.println("dpt.deepCloneTarget.hashCode() = " + dpt.deepCloneTarget.hashCode());

        DeepProtoType dpt2 = (DeepProtoType)dpt.clone();
        System.out.println("dpt2 = " + dpt2);
        System.out.println("dpt2.deepCloneTarget.hashCode() = " + dpt2.deepCloneTarget.hashCode());


    }

}
方案二:對象序列化實現深拷貝(推薦)
 /**
     * 深拷貝 - 方式2 通過對象的序列化實現 (推薦)
     * @return
     */
    public Object deepClone(){

        // 將流對象中的數組輸入到內存中
        ByteArrayInputStream bis = null;
        // 序列化流
        ObjectInputStream ois = null;
        //將內存中的數組輸出
        ByteArrayOutputStream bos = null;
        // 反序列化流
        ObjectOutputStream oos = null;

        try {
            // 序列化
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);

            // 當前這個對象以對象流的方式輸出
            oos.writeObject(this);

            // 反序列化
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);

            DeepProtoType copydeepProtoType = (DeepProtoType)ois.readObject();
            return copydeepProtoType;
        } catch (Exception e1) {
            e1.printStackTrace();
            return null;
        }finally {
            //關閉流
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
    }
測試
/**
 * @Date 2020/6/11 16:07
 * @Version 10.21
 * @Author DuanChaojie
 */
public class client {
    public static void main(String[] args) throws CloneNotSupportedException {
        DeepProtoType dpt = new DeepProtoType();
        dpt.name = "MM";
        dpt.deepCloneTarget = new DeepCloneTarget("DD",17);

        /**
         * dpt = DeepProtoType{name='MM', deepCloneTarget=DeepCloneTarget{cloneName='DD', cloneAge=17}}
         * dpt.deepCloneTarget.hashCode() = 1163157884
         * dpt2 = DeepProtoType{name='MM', deepCloneTarget=DeepCloneTarget{cloneName='DD', cloneAge=17}}
         * dpt2.deepCloneTarget.hashCode() = 1956725890
         */
        System.out.println("dpt = " + dpt);
        System.out.println("dpt.deepCloneTarget.hashCode() = " + dpt.deepCloneTarget.hashCode());

        DeepProtoType dpt2 = (DeepProtoType)dpt.clone();
        System.out.println("dpt2 = " + dpt2);
        System.out.println("dpt2.deepCloneTarget.hashCode() = " + dpt2.deepCloneTarget.hashCode());

        //方式 2 完成深拷貝
        DeepProtoType dpt3 = (DeepProtoType) dpt.deepClone();
        /**
         * dpt.name=MM   dpt.deepCloneableTarget=1163157884
         * dpt3.name=MM   dpt3.deepCloneableTarget=189568618
         */
        System.out.println("dpt.name=" + dpt.name + "   dpt.deepCloneableTarget=" + dpt.deepCloneTarget.hashCode());
        System.out.println("dpt3.name=" + dpt3.name + "   dpt3.deepCloneableTarget=" + dpt3.deepCloneTarget.hashCode());

    }

}

4. 小結

  1. 創建新的對象比較複雜時,可以利用原型模式簡化對象的創建過程,同時也能夠提高效率。
  2. 不用重新初始化對象,而是動態地獲得對象運行時的狀態
  3. 如果原始對象發生變化(增加或者減少屬性--這個指的是對被克隆的對象,如果修改被克隆對象裏面的對象屬性則需要通過深克隆才能克隆其對象屬性),其它克隆對象的也會發生相應的變化,無需修改代碼。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章