设计模式(三)---原型模式

吐槽

今天带猫猫去打针,然后她各种皮,差点从袋子里面跑出去了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.深拷贝可保存对象状态,可将对象拷贝后保存起来,需要的时候恢复
缺点是:
克隆时候不会执行构造方法,所以很尴尬

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值