二十三种设计模式之:(十) 原型模式

Prototype 原型模式:(实际也叫克隆模式)用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节

原型模式的优点:

  • Java 自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良
  • 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便 - 在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
    原型模式的缺点:
  • 需要为每一个类都配置一个 clone 方法
  • clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
  • 当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类 - 都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。

结构图:
在这里插入图片描述
实例:创建一个Person对象, 通过原型模式克隆一份(实际就是调用Object的clone方法)
实现需要两个步骤:
1、实现clonable接口,这个接口虽然没有任何方法,只是一个标记,但是必须实现它,否则运行出错(编译不会出错)
2、调用Object父类的clone方法(看源码可以看出,clone方法是native方法,实际底层用的C++实现的。就是直接从内存copy一份)

public class Prototype{

    public static void main(String[] args) throws CloneNotSupportedException {
        Person p = new Person("张三", 12, new Bird("小鸟", 2));
        Person p2 = (Person)p.clone();
        System.out.println(p.name + "--------" + p2.name);
        System.out.println(p.age + "--------" + p2.age);
        System.out.println(p.bird.name +"-------"+p2.bird.name);
        System.out.println(p.bird.age +"-------"+p2.bird.age);
        System.out.println("分割线--------------------------------");
        //改变p对象属性值--看看p2的值会不会变
        p.name = "李四";
        p.age = 10;
        p.bird.name="大鸟";
        p.bird.age = 3;
        System.out.println(p.name + "--------" + p2.name);
        System.out.println(p.age + "--------" + p2.age);
        System.out.println(p.bird.name +"-------"+p2.bird.name);
        System.out.println(p.bird.age +"-------"+p2.bird.age);
    }
}
class Person implements Cloneable{ //必须实现Cloneable这个接口,只是一个标记,没有任何用处,但是必须有
    String name;
    int age;
    Bird bird;

    public Person(String name, int age, Bird bird) {
        this.name = name;
        this.age = age;
        this.bird = bird;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {  //需要被克隆的对象实现一下这个方法,调用父类clone()方法即可
        return super.clone();
    }
}
class Bird{
    String name;
    int age;

    public Bird(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

打印值:

// 可以看出克隆出来后,两个对象一模一样
张三--------张三
12--------12
小鸟-------小鸟
2-------2
分割线--------------------------------
// 改变p属性的值后,看看两个对象的值得变化
李四--------张三
10--------12
大鸟-------大鸟  // 这里我只是改变了 p 对象的值,为什么 p2 的值也跟着变了?
3-------3

通过上面可以看出一个问题,基本变量通过clone方法可以完全克隆一份,互相没有牵扯但是引用对象的值还是一模一样的(这是因为Bird只是个引用,克隆一份,Bird对象还是同一个)
看图:
在这里插入图片描述
这里就牵扯出一个概念:浅克隆,深克隆
浅克隆
只会克隆基本类型,对引用类型是克隆不了的。
(有些人可能会问String不是基本类型,String也是个对象类型,他也是一个引用。String比较特殊,虽然是个引用类型,但是他指向一个常量池。当你改变String的引用的时候,另一个引用不会变。看下图)
在这里插入图片描述

深克隆
无论基本类型还是引用类型都需要克隆一份

注: 如果要完成深克隆就需要把需要克隆的引用对象也按照Person类的形式做一遍,实现Clonable接口,重写clone方法。

深刻了实例:

public class Prototype{

    public static void main(String[] args) throws CloneNotSupportedException {
        Person p = new Person("张三", 12, new Bird("小鸟", 2));
        Person p2 = (Person)p.clone();
        System.out.println(p.name + "--------" + p2.name);
        System.out.println(p.age + "--------" + p2.age);
        System.out.println(p.bird.name +"-------"+p2.bird.name);
        System.out.println(p.bird.age +"-------"+p2.bird.age);
        System.out.println("分割线--------------------------------");
        //改变属性值
        p.name = new String("李四");
        p.age = 10;
        p.bird.name="大鸟";
        p.bird.age = 3;
        System.out.println(p.name + "--------" + p2.name);
        System.out.println(p.age + "--------" + p2.age);
        System.out.println(p.bird.name +"-------"+p2.bird.name);
        System.out.println(p.bird.age +"-------"+p2.bird.age);
    }
}
class Person implements Cloneable{
    String name;
    int age;
    Bird bird;

    public Person(String name, int age, Bird bird) {
        this.name = name;
        this.age = age;
        this.bird = bird;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {  //这里也需要做一些更改
        Person p = (Person) super.clone();
        p.bird = (Bird)bird.clone();
        return p;
    }
}
class Bird implements Cloneable{   //这里也实现Cloneable接口
    String name;
    int age;

    public Bird(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

运行结果:

张三--------张三
12--------12
小鸟-------小鸟
2-------2
分割线-------------------------------- // 看下面,p对象的更改,完全影响不到p2对象了。实现了深克隆
李四--------张三
10--------12
大鸟-------小鸟
3-------2

总结: 从上面可以看出,如果克隆带有引用的对象,写起来是比较麻烦的。特别是属性引用多的,每个引用对象都要实现一遍Cloneable接口,重写一下clone方法,主类的clone方法也要手动赋值,挺麻烦的。浅克隆还是比较好用的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值