设计模式之原型模式(Prototype)

原型模式

如果已经有一个对象了,你想创建一个对象,而且对象里面的属性和已经存在的对象的属性差不多,就可以使用clone方法

克隆一个出来

实现原型模式需要实现标记型接口Cloneable -->标记型接口 : 里面没有需要实现的方法(空接口)

一般会重写clone方法 --> 如果只是重新clone方法,而没有实现Cloneable接口,调用时会报异常

一般用于一个对象的属性以及确定,需要产生很多相同或大部分属性相同的对象的时候

需要区分深克隆和浅克隆

java自带,Object类里面有个Object.clone()。也称为克隆模式

clone方法是protected native方法 C++实现 只能子类调用

浅克隆:

Object的clone,是在内存里面重新创建一个对象,copy过来原来对象的属性,如果是基本数据类型,拷贝的是值过去,

如果是引用数据类型,拷贝的是对象的内存地址,指向的是同一个对象,互相有影响

有没有方法把引用的对象也拷贝一份呢? --> 深克隆

java 复制代码
/**
 * 浅克隆
 */

public class Test {
    public static void main(String[] args) throws Exception {
        Person p1 = new Person();
        // 克隆
        Person p2 = (Person)p1.clone();
        // 基本数据类型,值直接copy过来
        System.out.println(p2.age + " " + p2.score);
        System.out.println(p2.loc);
        // 引用数据类型,copy的是内存地址,指向的是同一个对象,所以相等
        System.out.println(p1.loc == p2.loc); // true
        // 如果p1对象的loc改变了,p2的loc也会改变
        p1.loc.street = "sh";
        System.out.println(p2.loc); // sh

    }
}

// 实现Cloneable接口
class Person implements Cloneable {
    int age = 8;
    int score = 100;

    Location loc = new Location("bj", 22);
    // 实现clone方法
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class Location {
    String street;
    int roomNo;

    @Override
    public String toString() {
        return "Location{" +
                "street='" + street + '\'' +
                ", roomNo=" + roomNo +
                '}';
    }

    public Location(String street, int roomNo) {
        this.street = street;
        this.roomNo = roomNo;
    }
}

深克隆:

引用的对象Location也实现Cloneable接口以及clone方法,在clone Person对象的时候将原来对象的loc也clone一份出来,

然后新clone出来 的对象的loc引用指向clone处理的loc

java 复制代码
/**
 * 深克隆的处理
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Person p1 = new Person();
        Person p2 = (Person)p1.clone();
        System.out.println(p2.age + " " + p2.score); // 8 100
        System.out.println(p2.loc);
        // 不是同一个对象了
        System.out.println(p1.loc == p2.loc); // false
        p1.loc.street = "sh";
        // 改变p1的loc不好影响p2
        System.out.println(p2.loc); // bj
    }
}

class Person implements Cloneable {
    int age = 8;
    int score = 100;

    Location loc = new Location("bj", 22);
    @Override
    public Object clone() throws CloneNotSupportedException {
        // clone一份新的对象出来
        Person p = (Person)super.clone();
        // 将原来的loc,也clone一份出来,新对象的loc指针指向clone处理的loc对象
        p.loc = (Location)loc.clone();
        return p;
    }
}
// 被引用的对象实现Cloneable接口以及clone方法
class Location implements Cloneable {
    String street;
    int roomNo;

    @Override
    public String toString() {
        return "Location{" +
                "street='" + street + '\'' +
                ", roomNo=" + roomNo +
                '}';
    }

    public Location(String street, int roomNo) {
        this.street = street;
        this.roomNo = roomNo;
    }

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

引用数据类型,在深克隆的时候,是需要被克隆一份的,上面的String也是引用数据类型,clone的时候也需要将它克隆一份吗?

不需要。

String类型放在了常量池里面,比如说第一个Person对象里面的String指向的是常量池里面的"bj",clone出来的对象也是指向的常量池里面的"bj",

如果第一个Person对象里面的String变成了"sh",是不会影响第二个对象的,因为只是第一个对象里面的String指向了常量池里面的"sh",被克隆出来

的对象还是指向的"bj"。

java 复制代码
/**
 * String需要进一步深克隆吗?
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Person p1 = new Person();
        Person p2 = (Person)p1.clone();
        System.out.println(p2.age + " " + p2.score);
        System.out.println(p2.loc);

        System.out.println(p1.loc == p2.loc); // false
        p1.loc.street = "sh";
        System.out.println(p2.loc);

        p1.loc.street.replace("sh", "sz");
        // 改变p1里面的string,不好影响p2
        System.out.println(p2.loc.street); // bj
    }
}

class Person implements Cloneable {
    int age = 8;
    int score = 100;

    Location loc = new Location("bj", 22);
    @Override
    public Object clone() throws CloneNotSupportedException {
        Person p = (Person)super.clone();
        p.loc = (Location)loc.clone();
        return p;
    }
}

class Location implements Cloneable {
    String street;
    int roomNo;

    @Override
    public String toString() {
        return "Location{" +
                "street='" + street + '\'' +
                ", roomNo=" + roomNo +
                '}';
    }

    public Location(String street, int roomNo) {
        this.street = street;
        this.roomNo = roomNo;
    }

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

下面的例子中,String用的是StringBuilder,Person 1和Person 2里面的String都是指向的同一个StringBuilder,所以

当Person 1的String改变之后,Person 2的String也会改变。

怎么解决? --> 深克隆的时候,也需要把StringBuilder克隆一份

java 复制代码
/**
 * String需要进一步深克隆吗?
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Person p1 = new Person();
        Person p2 = (Person)p1.clone();
        // loc 经过clone之后指向的不是同一个对象
        System.out.println("p1.loc == p2.loc? " + (p1.loc == p2.loc)); // false
        // p1和p2里面的String指向的是同一个StringBuilder,所以当p1里面的String改变的时候,
        // p2的也会改变
        p1.loc.street.reverse();
        System.out.println(p2.loc.street); // jb
    }
}

class Person implements Cloneable {
    int age = 8;
    int score = 100;

    Location loc = new Location(new StringBuilder("bj"), 22);
    @Override
    public Object clone() throws CloneNotSupportedException {
        Person p = (Person)super.clone();
        p.loc = (Location)loc.clone();
        return p;
    }
}

class Location implements Cloneable {
    StringBuilder street;
    int roomNo;

    @Override
    public String toString() {
        return "Location{" +
                "street='" + street + '\'' +
                ", roomNo=" + roomNo +
                '}';
    }

    public Location(StringBuilder street, int roomNo) {
        this.street = street;
        this.roomNo = roomNo;
    }

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

深克隆用的少,面试也问的少,了解就行

相关推荐
杨充19 小时前
10.可测试性实战设计
设计模式·开源·代码规范
杨充19 小时前
9.重构十二式的实战
设计模式·开源·代码规范
杨充19 小时前
6.设计原则的全景图
设计模式·开源·全栈
杨充19 小时前
2.面向对象的特性
设计模式
杨充19 小时前
7.SOLID原则案例汇
设计模式·开源·全栈
杨充19 小时前
8.反模式与坏味道
设计模式·开源·代码规范
杨充19 小时前
3.接口vs抽象类比较
设计模式
咖啡八杯1 天前
文法、BNF与AST
java·设计模式·解释器模式·ast·文法
咖啡八杯1 天前
GoF设计模式——解释器模式
java·后端·spring·设计模式