您好,登錄后才能下訂單哦!
本篇內容主要講解“Spring中的原型模式是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Spring中的原型模式是什么”吧!
其定義為: >使用原型實例指定將要創建的對象類型,通過復制這個實例創建新的對象。
具體來說就是,通過給出一個原型對象來指明所創建的對象的類型,然后使用自身實現的克隆接口來復制這個原型對象,該模式就是用這種方式來創建出更多同類型的對象。
這樣的好處是: >Object 類的 clone() 方法是一個本地方法,它可以直接操作內存中的二進制流,所以性能相對 new 實例化來說,更加優秀。
一個對象通過 new 實例化創建過程為:
在內存中開辟一塊空間。
在開辟的內存空間中創建對象。
調用對象的構造函數進行初始化對象。
而一個對象通過 clone() 創建過程為:
根據原對象內存大小開辟一塊內存空間。
復制已有對象,克隆對象中所有屬性值。
相對 new 來說,clone() 少了調用構造函數。如果構造函數中存在大量屬性初始化或大對象,則使用 clone() 的復制對象的方式性能會好一些。
讓我們通過一個例子來具體了解一下:
/** * 實現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; } } /** * 實現原型類 */ public class ConcretePrototype extends Prototype { public void show() { System.out.println("原型模式實現類"); } } /** * 測試類 */ 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(); } } }
當我們實現原型抽象類時,需要注意三點:
實現 Cloneable 接口:Cloneable 接口與序列化接口的作用類似,它只是告訴虛擬機可以安全地在實現了這個接口的類上使用 clone() 方法。在 JVM 中,只有實現了 Cloneable 接口的類才可以被拷貝,否則會拋出 CloneNotSupportedException 異常。
重寫 Object 類中的 clone() 方法:在 Java 中,所有類的父類都是 Object 類,而 Object 類中有一個 clone() 方法,作用是返回對象的一個拷貝。
在重寫的 clone() 方法中調用 super.clone():默認情況下,類不具備復制對象的能力,需要調用 super.clone() 來實現。
談到了拷貝,就不得不說到一個經典的問題:深拷貝與淺拷貝
,有的地方也叫深克隆與淺克隆
。
在上面的原型模式中,在調用 super.clone() 方法之后,首先會檢查當前對象所屬的類是否支持 clone,也就是看該類是否實現了 Cloneable 接口。
如果支持,則創建當前對象所屬類的一個新對象,并對該對象進行初始化,使得新對象的成員變量的值與當前對象的成員變量的值一模一樣,但對于其它對象的引用以及 List 等類型的成員屬性,則只能復制這些對象的引用了
。所以簡單調用 super.clone() 這種克隆對象方式,就是一種淺拷貝
。
為了讓大家更加清楚淺拷貝
的弊端,舉個具體的例子:
Student 類中有一個 Teacher 對象,我們讓這兩個類都實現 Cloneable 接口:
@Getter @Setter public class Student implements Cloneable{ /** * 學生姓名 */ private String name; /** * 學生所屬的老師 */ private Teacher teacher; /** * 重寫克隆方法,對學生進行克隆 */ 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; /** * 重寫克隆方法,對老師類進行克隆 */ public Teacher clone() { Teacher teacher= null; try { teacher= (Teacher) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return teacher; } }
測試的時候,我們先定義一個學生和一個老師,并讓其關聯在一起。然后復制之前的學生,生成一個新的學生,修改新學生的老師。
public class Test { public static void main(String args[]) { // 定義老師1 Teacher teacher = new Teacher(); teacher.setName("劉老師"); // 定義學生1 Student stu1 = new Student(); stu1.setName("test1"); // 老師1和學生1進行關聯 stu1.setTeacher(teacher); // 復制學生1,生成學生2 Student stu2 = stu1.clone(); stu2.setName("test2"); // 修改學生2的老師 stu2.getTeacher().setName("王老師"); // 查看修改結果 System.out.println("學生" + stu1.getName() + "的老師是:" + stu1.getTeacher().getName()); System.out.println("學生" + stu1.getName() + "的老師是:" + stu2.getTeacher().getName()); } }
我們想要的結果是:
學生test1的老師是:劉老師 學生test2的老師是:王老師
但實際結果是:
學生test1的老師是:王老師 學生test2的老師是:王老師
觀察以上運行結果,我們可以發現:在我們給學生2修改老師的時候,學生1的老師也跟著被修改了。這就是淺拷貝帶來的問題。
我們可以通過深拷貝
的方式解決這類問題,修改 Student 類的 clone() 方法:
/** * 重寫克隆方法,對學生和老師都進行克隆 */ 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() 方法,就可以得到我們預想的結果了。
在一些重復創建對象的場景下,我們就可以使用原型模式來提高對象的創建性能。例如:循環體內創建對象時,我們就可以考慮用 clone() 的方式來實現。
除此之外,原型模式在開源框架中的應用也非常廣泛。例如 Spring 中,@Service 默認都是單例的。用了私有全局變量,若不想影響下次注入或每次上下文獲取 bean,就需要用到原型模式,我們可以通過以下注解來實現,@Scope("prototype")。有興趣的朋友深入了解一下其中的原理。
原型模式,就是針對需要大量復制同一對象的場景,比如用戶獲取商品、循環體內創建對象等,都是不錯的選擇,且效率好。
到此,相信大家對“Spring中的原型模式是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。