1. 程式人生 > >設計模式(三)---原型模式

設計模式(三)---原型模式

吐槽

今天帶貓貓去打針,然後她各種皮,差點從袋子裡面跑出去了emmmmm,早上上課時候,編譯原理上上課居然就聽不懂了,很尷尬,趕緊回去補。

什麼叫原型模式

就是類似鳴人的影分身之術,可以克隆物件 定義:用原型例項指向建立物件的種類,並通過複製這些原型建立新的物件

原型模式使用的場景

  • 當初始化類物件需要消耗非常多資源,或者說要進繁瑣的資料準備或者許可權,如果想簡化建立,可以使用原型模式
  • 一個物件要提供給其他物件訪問的時候,而且個個呼叫者都可能改變其值的時候,可以用原型模式複製個個物件提供呼叫者使用,即保護性拷貝

原型模式下的各種角色

  • Client —— 客戶端使用者,呼叫類
  • ConcretePrototype —— 實現Prototype介面的類,這些類真正實現克隆自身的相關程式碼
  • Prototype —— 宣告一個克隆自身的介面,用於約束想要克隆自己的類,要求實現定義的克隆方法。 這裡寫圖片描述

物件的拷貝

在Java中,如果我們將原始物件的值賦給另一個物件,就是值的傳遞,如

int a = 1;
int b = a;

如果將引用型別的值賦給另一個物件,則是引用的傳遞,如

String[] a = new String[5];
String[] b = a;//這裡b只是指向了a的引用

在java中

==,如果是對比的基本資料型別(int,long等),比較儲存的值是否相等,

如果對比的是引用型的變數,比較的是所指向的物件地址是否相等

equals,不能用於比較基本資料型別,如果沒對equals()方法進行 重寫,比較的是指向的物件地址,如果想要比較物件內容,需要自行重寫 方法,做相應的判斷

java中的克隆方法及其使用過程

  • 對任何的物件x,都有:x.clone()!=x ,即不是同一物件
  • 對任何的物件x,都有:x.clone().getClass==x.getClass(),即物件型別一致
  • 如果物件obj的equals()方法定義恰當的話,那麼obj.clone().equals(obj) 應當是成立的

使用clone()方法的步驟:

1.實現clone的類首先需要實現Cloneable介面。Cloneable介面實質上是一個標識介面 2.在類中重寫Object類中的clone方法 3.在clone方法中呼叫super.clone()。無論clone類的繼承結構是什麼,super.clone會直接或間接呼叫java.lang.Object類的clone()方法。 4.把淺複製的引用指向原型物件新的克隆體。

java在處理基本資料型別(例如int,double,char等),都採用按值傳遞(傳遞的是輸入引數的複製),除此之外的其他型別都採用按引用傳遞(傳遞的是物件的一個引用)。物件除了在函式呼叫時是引用傳遞,在使用“=”賦值時也採用引用傳遞

實用流程

1 先寫個引用類:Student.java

public class Student {
    private String name; //名字
    Student(String name){
        this.name = name;
    }

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

    @Override
    public String toString() {
        return "name:"+name;
    }
}

2實現Cloneable介面的類:CloneStudent.java,核心就是重寫clone方法而已

public class CloneStudent implements Cloneable {
    private int age;  //年齡
    private Student student;//名字
    private String king;//學生的型別

    CloneStudent(int age,Student student,String king){
        System.out.println("執行了構造方法");
        this.age = age;
        this.student = student;
        this.king = king;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        CloneStudent cloneStudent = null;
        try {
            cloneStudent = (CloneStudent)super.clone();
        }catch (CloneNotSupportedException e){
            e.printStackTrace();
        }
        return cloneStudent;
    }

    public int getAge() {
        return age;
    }

    public String getKing() {
        return king;
    }

    public Student getStudent() {
        return student;
    }

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

    public void setKing(String king) {
        this.king = king;
    }

    public void setStudent(Student student) {
        this.student = student;
    }

    @Override
    public String toString() {
        return  "CloneStudent["+"age = "+age+"Student"+student.toString()+"kind"+king;

    }
}

3 呼叫類

 CloneStudent cloneStudent1 = new CloneStudent(10, new Student("笑笑"),"哈哈");
        try {
            CloneStudent cloneStudent2 = (CloneStudent)cloneStudent1.clone();

            System.out.println("Student1.equals(Student) "+cloneStudent1.equals(cloneStudent2));
            System.out.println("student1==student2"+(cloneStudent1==cloneStudent2));
            System.out.println(cloneStudent1.getClass()==cloneStudent2.getClass());

            System.out.println(cloneStudent1.toString());
            System.out.println(cloneStudent2.toString());

            cloneStudent1.setAge(200);
            System.out.println(cloneStudent1.toString());
            System.out.println(cloneStudent2.toString());

            cloneStudent2.getStudent().setName("啦啦");
            System.out.println(cloneStudent1.toString());
            System.out.println(cloneStudent2.toString());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

    }

執行結果: 這裡寫圖片描述

以上結果發現:

  • cloneStudent2是cloneStudent1克隆出來的
  • 通過克隆的時候不會執行建構函式
  • 克隆會生成的新的物件變數,指向的卻是同一個記憶體地址
  • 克隆前後資料型別一致
  • 克隆的時候,類中基本資料型別的屬性會新建,但是引用型別的 只會生成個新的引用變數,引用變數的地址依舊指向同一個記憶體地址

深拷貝和淺拷貝

淺拷貝:只新建基本型別資料,不新建引用型別資料 深拷貝:引用型別資料也新建

如何將淺拷貝轉換成深拷貝? 有兩種方法

1引用型別也實現Cloneable介面,然後實現clone方法

先把Student.java這個類實現介面,然後寫Clone方法

public class Student implements Cloneable{
    private String name; //名字
    Student(String name){
        this.name = name;
    }

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

    @Override
    public String toString() {
        return "name:"+name;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Student student = null;
        student = (Student)super.clone();

        return student;
    }
}

然後再在介面實現類CloneStudent修改clone方法

 @Override
    protected Object clone() throws CloneNotSupportedException {
        CloneStudent cloneStudent = null;
        try {
            cloneStudent = (CloneStudent)super.clone();
            cloneStudent.setStudent((Student)this.getStudent().clone());
        }catch (CloneNotSupportedException e){
            e.printStackTrace();
        }
        return cloneStudent;
    }

然後重新執行發行結果: 這裡寫圖片描述 然後結果發現我們cloneStudent2物件修改了name字串,cloneStudent1物件的name字串並沒有變化

總結

原型模式就是clone方法的使用,推薦用深拷貝的方式 淺拷貝適用於 物件只包含原始資料域或者不可變物件域的時候,提高效率 該模式的優點是: 1.簡化物件建立過程,當物件建立比較煩瑣時,可提高建立效率 2.深拷貝可儲存物件狀態,可將物件拷貝後儲存起來,需要的時候恢復 缺點是: 克隆時候不會執行構造方法,所以很尷尬