溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Spring中的原型模式是什么

發(fā)布時間:2021-06-25 11:37:34 來源:億速云 閱讀:363 作者:chen 欄目:大數(shù)據(jù)

本篇內(nèi)容主要講解“Spring中的原型模式是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“Spring中的原型模式是什么”吧!

簡介

其定義為: >使用原型實例指定將要創(chuàng)建的對象類型,通過復(fù)制這個實例創(chuàng)建新的對象。

具體來說就是,通過給出一個原型對象來指明所創(chuàng)建的對象的類型,然后使用自身實現(xiàn)的克隆接口來復(fù)制這個原型對象,該模式就是用這種方式來創(chuàng)建出更多同類型的對象。

這樣的好處是: >Object 類的 clone() 方法是一個本地方法,它可以直接操作內(nèi)存中的二進(jìn)制流,所以性能相對 new 實例化來說,更加優(yōu)秀。

一個對象通過 new 實例化創(chuàng)建過程為:

  1. 在內(nèi)存中開辟一塊空間。

  2. 在開辟的內(nèi)存空間中創(chuàng)建對象。

  3. 調(diào)用對象的構(gòu)造函數(shù)進(jìn)行初始化對象。

而一個對象通過 clone() 創(chuàng)建過程為:

  1. 根據(jù)原對象內(nèi)存大小開辟一塊內(nèi)存空間。

  2. 復(fù)制已有對象,克隆對象中所有屬性值。

相對 new 來說,clone() 少了調(diào)用構(gòu)造函數(shù)。如果構(gòu)造函數(shù)中存在大量屬性初始化或大對象,則使用 clone() 的復(fù)制對象的方式性能會好一些。

簡單例子

讓我們通過一個例子來具體了解一下:

/**
 * 實現(xiàn)Cloneable 接口的原型抽象類Prototype
 */
public class Prototype implements Cloneable {
    /**
     * 重寫 clone() 方法
     */
    @Override
    public Prototype clone() {
        Prototype prototype = null;
        try {
            prototype = (Prototype) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return prototype;
    }
}

/**
 * 實現(xiàn)原型類
 */
public class ConcretePrototype extends Prototype {
    public void show() {
        System.out.println("原型模式實現(xiàn)類");
    }
}

/**
 * 測試類
 */
public class Client {
    public static void main(String[] args) {
        ConcretePrototype cp = new ConcretePrototype();
        for (int i = 0; i < 10; i++) {
            ConcretePrototype cloneCp = (ConcretePrototype) cp.clone();
            cloneCp.show();
        }
    }
}

當(dāng)我們實現(xiàn)原型抽象類時,需要注意三點:

  1. 實現(xiàn) Cloneable 接口:Cloneable 接口與序列化接口的作用類似,它只是告訴虛擬機(jī)可以安全地在實現(xiàn)了這個接口的類上使用 clone() 方法。在 JVM 中,只有實現(xiàn)了 Cloneable 接口的類才可以被拷貝,否則會拋出 CloneNotSupportedException 異常。

  2. 重寫 Object 類中的 clone() 方法:在 Java 中,所有類的父類都是 Object 類,而 Object 類中有一個 clone() 方法,作用是返回對象的一個拷貝。

  3. 在重寫的 clone() 方法中調(diào)用 super.clone():默認(rèn)情況下,類不具備復(fù)制對象的能力,需要調(diào)用 super.clone() 來實現(xiàn)。

深拷貝與淺拷貝

談到了拷貝,就不得不說到一個經(jīng)典的問題:深拷貝與淺拷貝,有的地方也叫深克隆與淺克隆

在上面的原型模式中,在調(diào)用 super.clone() 方法之后,首先會檢查當(dāng)前對象所屬的類是否支持 clone,也就是看該類是否實現(xiàn)了 Cloneable 接口。

如果支持,則創(chuàng)建當(dāng)前對象所屬類的一個新對象,并對該對象進(jìn)行初始化,使得新對象的成員變量的值與當(dāng)前對象的成員變量的值一模一樣,但對于其它對象的引用以及 List 等類型的成員屬性,則只能復(fù)制這些對象的引用了。所以簡單調(diào)用 super.clone() 這種克隆對象方式,就是一種淺拷貝。

為了讓大家更加清楚淺拷貝的弊端,舉個具體的例子:

Student 類中有一個 Teacher 對象,我們讓這兩個類都實現(xiàn) Cloneable 接口:

@Getter
@Setter
public class Student implements Cloneable{

    /**
     * 學(xué)生姓名
     */
    private String name;

    /**
     * 學(xué)生所屬的老師
     */
    private Teacher teacher;

    /**
     * 重寫克隆方法,對學(xué)生進(jìn)行克隆
     */
    public Student clone() {
        Student student = null;
        try {
            student = (Student) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return student;
    }
}

@Getter
@Setter
public class Teacher implements Cloneable{

    /**
     * 老師姓名
     */
    private String name;

    /**
     * 重寫克隆方法,對老師類進(jìn)行克隆
     */
    public Teacher clone() {
        Teacher teacher= null;
        try {
            teacher= (Teacher) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return teacher;
    }
}

測試的時候,我們先定義一個學(xué)生和一個老師,并讓其關(guān)聯(lián)在一起。然后復(fù)制之前的學(xué)生,生成一個新的學(xué)生,修改新學(xué)生的老師。

public class Test {
    public static void main(String args[]) {
        // 定義老師1
        Teacher teacher = new Teacher();
        teacher.setName("劉老師");
        // 定義學(xué)生1
        Student stu1 = new Student();
        stu1.setName("test1");
        // 老師1和學(xué)生1進(jìn)行關(guān)聯(lián)
        stu1.setTeacher(teacher);

        // 復(fù)制學(xué)生1,生成學(xué)生2
        Student stu2 = stu1.clone();
        stu2.setName("test2");
        // 修改學(xué)生2的老師
        stu2.getTeacher().setName("王老師");

        // 查看修改結(jié)果
        System.out.println("學(xué)生" + stu1.getName() + "的老師是:" + stu1.getTeacher().getName());
        System.out.println("學(xué)生" + stu1.getName() + "的老師是:" + stu2.getTeacher().getName());
    }
}

我們想要的結(jié)果是:

學(xué)生test1的老師是:劉老師
學(xué)生test2的老師是:王老師

但實際結(jié)果是:

學(xué)生test1的老師是:王老師
學(xué)生test2的老師是:王老師

觀察以上運行結(jié)果,我們可以發(fā)現(xiàn):在我們給學(xué)生2修改老師的時候,學(xué)生1的老師也跟著被修改了。這就是淺拷貝帶來的問題。

我們可以通過深拷貝的方式解決這類問題,修改 Student 類的 clone() 方法:

    /**
     * 重寫克隆方法,對學(xué)生和老師都進(jìn)行克隆
     */
    public Student clone() {
        Student student = null;
        try {
            student = (Student) super.clone();
            // 克隆 teacher 對象
            Teacher teacher = this.teacher.clone();
            student.setTeacher(teacher);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return student;
    }

此時,我們再次運行 Test 中的 main() 方法,就可以得到我們預(yù)想的結(jié)果了。

適用場景

在一些重復(fù)創(chuàng)建對象的場景下,我們就可以使用原型模式來提高對象的創(chuàng)建性能。例如:循環(huán)體內(nèi)創(chuàng)建對象時,我們就可以考慮用 clone() 的方式來實現(xiàn)。

除此之外,原型模式在開源框架中的應(yīng)用也非常廣泛。例如 Spring 中,@Service 默認(rèn)都是單例的。用了私有全局變量,若不想影響下次注入或每次上下文獲取 bean,就需要用到原型模式,我們可以通過以下注解來實現(xiàn),@Scope("prototype")。有興趣的朋友深入了解一下其中的原理。

總結(jié)

原型模式,就是針對需要大量復(fù)制同一對象的場景,比如用戶獲取商品、循環(huán)體內(nèi)創(chuàng)建對象等,都是不錯的選擇,且效率好。

到此,相信大家對“Spring中的原型模式是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI