开发者社区> 汤高> 正文

Java设计模式(五)----原型模式

简介:
+关注继续查看

原型模式(Prototype)
一、概述
二、结构
三、浅度克隆和深度克隆
浅度克隆
深度克隆

一、概述
定义:原型模式属于对象的创建模式。通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象。简言之:就是复制粘贴。这就是选型模式的用意。


二、结构

原型模式主要用于对象的复制,它的核心是就是类图中的原型类Prototype。Prototype类需要具备以下两个条件:


1、实现Cloneable接口。在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接 口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出 CloneNotSupportedException异常。

2、重写Object类中的clone方法。Java中,所有类的父类都是 Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因 此,Prototype类需要将clone方法的作用域修改为public类型。


三、浅度克隆和深度克隆
浅度克隆
只负责克隆按值传递的数据(比如基本数据类型、String类型),而不复制它所引用的对象,换言之,所有的对其他对象的引用都仍然指向原来的对象。

public class Person1  implements Cloneable{

    //基本数据类型
    private int age;
    //String引用类型
    private String name;

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public Person1 clone() {
        Person1 person =null;
        try {
             person = (Person1) super.clone();
            return person;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

}

public class Client {
    public static void main(String[] args) {
        Person1 p1=new Person1();
        p1.setName("汤高");
        p1.setAge(20);

//      Person p2=p1;//地址相同  只是把引用给了p2 指向同一个地址
//      System.out.println(p1==p2);//true
        Person1 p2=p1.clone();
        //拷贝  地址不同了 指向不同的地址
        System.out.println("前后地址相同吗:  "+(p2==p1));
        System.out.println("输出p1:" +p1.getName()+"\t"+p1.getAge());
        System.out.println("输出p2:" +p2.getName()+"\t"+p2.getAge());

        //修改拷贝后的对象的属性值
        p2.setName("周思远");
        p2.setAge(19);
        System.out.println("输出p1:" +p1.getName()+"\t"+p1.getAge());
        System.out.println("输出p2:" +p2.getName()+"\t"+p2.getAge());

    }
}

结果:
前后地址相同吗: false
输出p1:汤高 20
输出p2:汤高 20
输出p1:汤高 20
输出p2:周思远 19


通过上诉测试可知对于基本类型和String类型的数据前后都是指向不同的地址空间,改变一个不会影响其他的对象


浅度克隆图如下
这里写图片描述


这里写图片描述


但是如果包含引用类型比如对象、数组、集合等,就只会克隆引用,结果指向同一个引用地址


public class Person2  implements Cloneable{

    //基本数据类型
    private int age;
    //String引用类型
    private String name;
    //引用类型
    private List<String> friends=new ArrayList<String>();
    //对象
    private School school;

    public School getSchool() {
        return school;
    }
    public void setSchool(School school) {
        this.school = school;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public List<String> getFriends() {
        return friends;
    }
    public void setFriends(List<String> friends) {
        this.friends = friends;
    }


    public Person2 clone() {
        Person2 person =null;
        try {
             person = (Person2) super.clone();
            return person;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

}



public class Client2 {
    public static void main(String[] args) {
        Person2 p1=new Person2();

        List<String> friends=new ArrayList<String>();
        friends.add("汤小高");
        friends.add("周小思");
        p1.setFriends(friends);


        Person2 p2=p1.clone();
        System.out.println(p1.getFriends());
        System.out.println(p2.getFriends());


        friends.add("TSY");
        p1.setFriends(friends);
        System.out.println(p1.getFriends());
        System.out.println(p2.getFriends());



        School school=new School();
        school.setName("清华");

    }
}

结果:
[汤小高, 周小思]
[汤小高, 周小思]
[汤小高, 周小思, TSY]
[汤小高, 周小思, TSY]


public class Client3 {
    public static void main(String[] args) {
        Person2 p1=new Person2();

        School school=new School();
        school.setName("清华");
        p1.setSchool(school);

        Person2 p2=p1.clone();

        System.out.println(p1.getSchool()==p2.getSchool());
        System.out.println(p1.getSchool());
        System.out.println(p2.getSchool());
        school.setName("北大");
        p1.setSchool(school);

        System.out.println(p1.getSchool());//北大
        System.out.println(p2.getSchool());//北大
    }
}

结果:
true
学校名: 清华
学校名: 清华
学校名: 北大
学校名: 北大

我只改变了p1 结果p2也变了 原因如下图,共享一个引用

浅度克隆图如下
这里写图片描述

这时候就需要使用深度克隆了!
深度克隆
除了浅度克隆要克隆的值外,还负责克隆引用类型的数据。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深度克隆把要复制的对象所引用的对象都复制了一遍,而这种对被引用到的对象的复制叫做间接复制。
深度克隆要深入到多少层,是一个不易确定的问题。在决定以深度克隆的方式复制一个对象的时候,必须决定对间接复制的对象时采取浅度克隆还是继续 采用深度克隆。因此,在采取深度克隆时,需要决定多深才算深。此外,在深度克隆的过程中,很可能会出现循环引用的问题,必须小心处理。

要实现深度克隆 必须修改clone()方法


public class Person2  implements Cloneable{

    //基本数据类型
    private int age;
    //String引用类型
    private String name;
    //引用类型
    private List<String> friends=new ArrayList<String>();
    //对象
    private School school;

    public School getSchool() {
        return school;
    }
    public void setSchool(School school) {
        this.school = school;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public List<String> getFriends() {
        return friends;
    }
    public void setFriends(List<String> friends) {
        this.friends = friends;
    }
   //修改后的clone方法
    public Person2 clone() {
        try {
            Person2 person = (Person2) super.clone();
            if(this.getFriends()!=null){
                List<String> friends=new ArrayList<String>();
                for(String friend:this.getFriends()){
                    friends.add(friend);
                }
                person.setFriends(friends);
            }

            if(this.getSchool()!=null){
                 School school=new School();
                 school.setName(this.getSchool().getName());
                 person.setSchool(school);

            }
            return person;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

}

测试类同上


结果:
[汤小高, 周小思]
[汤小高, 周小思]
[汤小高, 周小思, TSY]


结果:
false
学校名: 清华
学校名: 清华
学校名: 北大
学校名: 清华
我修改了p1,p2的值没有改变 原因如下

深度克隆图如下
这里写图片描述

以上内容来自平时所看书籍和网络资源整理测试所得,如有不完善之处,欢迎指正!

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,大概有三种登录方式:
9406 0
阿粉带你学习设计模式之原型(Prototype)模式
大家好,我是鸭血粉丝,今天阿粉带大家学习一下设计模式之原型模式,话不多说,我们往下看。 今天要跟大家分享的是创建型模式之原型模式,作为设计模式中创建型模式中的一员,很明显原型模式是用来创建对象的,那么到底什么是原型模式,以及原型模式到底是怎么使用的,有哪些使用场景呢?且听阿粉我慢慢道来。
15 0
JavaScript创建对象(三)——原型模式
在JavaScript创建对象(二)——构造函数模式中提到,构造函数模式存在相同功能的函数定义多次的问题。本篇文章就来讨论一下该问题的解决方案——原型模式。
1117 0
【设计模式】原型模式 ( 概念简介 | 使用场景 | 优缺点 | 基本用法 )
【设计模式】原型模式 ( 概念简介 | 使用场景 | 优缺点 | 基本用法 )
32 0
设计模式之八(原型模式)
原文:设计模式之八(原型模式) 前言 原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 结构图 Prototype,原型类,声明一个克隆自身的接口 ConcretePrototype1,ConcretePrototype2具体原型类,实现一个克隆自身的操作 Client,调用,让一个原型克隆自身从而创建一个新的对象 其实原型模式就是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建的细节。
826 0
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
20062 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
18349 0
+关注
汤高
学习,进步
49
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载