原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型来创建对象。

在java中有语言级别的支持:clone

在java中使用原型模式是非常简单的事情,实现Cloneable接口,调用Object的clone方法,便可以实现对象的拷贝。

浅复制:被复制的对象的值与原对象的值是相同的,对其他对象的复制仅仅是该对象的引用

深复制:被复制的对象的值与原对象的值是相同的,对其他对象的复制不是引用原有对象的引用,而是重新复制了该对象。

浅复制:

/**
* 浅复制
* 1.在派生类的clone()方法中,调用super.clone()
* 2.派生类中实现Cloneable接口,否则会报CloneNotSupportedException错误
* 3.克隆对象与原对象不是同一个对象 克隆对象与原对象的类型一样
* @author junjin4838
*
*/
public class ShallowStudent implements Cloneable { private String name; private int age; Professor p;     public ShallowStudent2(String name, int age, Professor p) {
        this.name = name;
        this.age = age;
        this.p = p;
    } public Object clone(){
ShallowStudent o = null;
try {
o = (ShallowStudent)super.clone();
} catch (CloneNotSupportedException e) {
System.out.println(e.toString());
}
return o;
} public static void main(String[] args) {
People people1 = new People("test1",23);
        ShallowStudent s1 = new ShallowStudent("jibingkun",23,people1);
ShallowStudent s2 = (ShallowStudent)s1.clone();
s2.name = "zhangtianyu";
s2.age = 22;
s2.people.peopleName = "test2";
        s2.people.peopleAge = 123;
System.out.println("s1 name: "+s1.name + " s1 age: "+s1.age + "s1 people.name "+ s1.people.peopleName + "s1 people.age "+ s1.people.peopleAge);
        System.out.println("s2 name: "+s2.name + " s2 age: "+s2.age + "s2 people.name "+ s2.people.peopleName + "s2 people.age "+ s2.people.peopleAge);
boolean b1 = s1.equals(s2);
boolean b2 = (s1.getClass())== (s2.getClass());
System.out.println(b1);
System.out.println(b2);
} class Professor {
    String name;
    int age;
    public Professor(String name, int age) {
        this.name = name;
        this.age = age;
    }
} ------》》》-------
s1 name: jibingkun s1 age: 23 s1 people.name test2 s1 people.age 123
s2 name: zhangtianyu s2 age: 22 s2 people.name test2 s2 people.age 123
false
true -----《《《--------- 从中可以看出,调用Object类中clone()方法产生的效果是:先在内存中开辟一块和原始对象一样的空间,然后原样拷贝原始对象中的内容。对基本数据类型,这样的操作是没有问题的.
但对非基本类型变量,我们知道它们保存的仅仅是对象的引用,这也导致clone后的非基本类型变量和原始对象中相应的变量指向的是同一个对象。
大多时候,这种clone的结果往往不是我们所希望的结果,这种clone也被称为”影子clone”。

Object.clone()的源码实现:

    /**
* Creates and returns a copy of this object.
* x.clone() != x will be true
* x.clone().getClass() == x.getClass() will be true}
* if the class of this object does not implement the interface {@code Cloneable}, then a{@code CloneNotSupportedException} is thrown.
* 是native方法,native一般要高于非native的方法,因此使用clone往往会比new一个对象的效率要高
* 是protected属性的方法,这也意味着如果要应用 clone()方法,必须继承Object类,在Java中所有的类是缺省继承Object类的,也就不用关心这点了
* 重载clone()方法。还有一点要考虑的是为了让其它类能调用这个clone类的clone()方法,重载之后要把clone()方法的属性设置为public。
* Cloneable接口是不包含任何方法的!其实这个接口仅仅是一个标志,而且这个标志也仅仅是针对Object类中clone()方法的。
* 如果clone类没有实现Cloneable接口,并调用了Object的 clone()方法(也就是调用了super.Clone()方法),那么Object的clone()方法就会抛出 CloneNotSupportedException异常。
*/
protected native Object clone() throws CloneNotSupportedException;

深复制(实现深度拷贝,则需要将实现了Cloneable接口并重写了clone方法的类中,所有的引用类型也全部实现Cloneable接口并重写clone方法,而且需要将引用类型的属性全部拷贝一遍。

/**
* 深复制
*
*/
public class ShallowStudent implements Cloneable {
    
    private String name;
    
    private int age;
    
    public People people;
    
    public ShallowStudent(){
        this.name  = "test1";
        this.age = 23;
        this.people = new People("test1",23);
    }
    
    public People getPeople(){
        return people;
    }
    
    public Object clone(){
        ShallowStudent o = null;
        try {
            o = (ShallowStudent)super.clone();
//子对象也要进行复制
            o.people = (People) this.people.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println(e.toString());
        }
        return o;
    }
    
    public static void main(String[] args) {
        ShallowStudent s1 = new ShallowStudent();
        ShallowStudent s2 = (ShallowStudent)s1.clone();
        s2.name = "test2";
        s2.age = 22;
        System.out.println("s1 name: "+s1.name + " s1 age: "+s1.age + " People: " + s1.getPeople());
        System.out.println("s2 name: "+s2.name + " s2 age: "+s2.age + " People: " + s2.getPeople());
    } } class People implements Cloneable{
    
    public String peopleName;
    
    public int  peopleAge;
    
    public People(String peopleName,int peopleAge){
        this.peopleName = peopleName;
        this.peopleAge = peopleAge;
    }
        
    public Object clone(){
        People o = null;
        try {
            o = (People)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return o;
    }
} ---------》》》---------
s1 name: test1 s1 age: 23 People: com.pingan.haofang.fjs.web.action.People@15db9742
s2 name: test2 s2 age: 22 People: com.pingan.haofang.fjs.web.action.People@6d06d69c
--------《《《----------

从原型模式的使用方式不难推断出,原型模式常使用于以下场景:

 1、对象的创建非常复杂,可以使用原型模式快捷的创建对象。

               2、在运行过程中不知道对象的具体类型,可使用原型模式创建一个相同类型的对象,或者在运行过程中动态的获取到一个对象的状态。

下面我们来看下原型模式的主要优点:

1、由于clone方法是由虚拟机直接复制内存块执行,所以在速度上比使用new的方式创建对象要快。

               2、可以基于原型,快速的创建一个对象,而无需知道创建的细节。可以在运行时动态的获取对象的类型以及状态,从而创建一个对象。

然而原型模式的缺点也是相当明显的,主要的缺点就是实现深度拷贝比较困难,需要很多额外的代码量

不过实际当中我们使用原型模式时,也可以写一个基类实现Cloneable接口重写clone方法,然后让需要具有拷贝功能的子类继承自该类,这是一种节省代码量的常用方式

JDK的StringBuffer类没有重写clone()方法,还是final类型的,说明不能用继承的方法来实现clone()方法。

如果一个类中包含有StringBuffer类型对象或和 StringBuffer相似类的对象,我们有两种选择:要么只能实现影子clone,要么就在类的clone()方法中加一句(假设是 SringBuffer对象,而且变量名仍是p): o.p = new StringBuffer(p.toString());

利用串行化来做深复制

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamField;
import java.io.Serializable; /**
* 通过串行化实现深复制
* @author junjin4838
*
*/
public class ShallowStudent implements Serializable { private String name; private int age; public People people; public ShallowStudent(String name,int age,People people){
this.name = name;
this.age = age;
this.people = people;
} public People getPeople(){
return people;
} public Object deepClone() throws IOException, ClassNotFoundException{
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
//从流里面读出数据
oo.writeObject(this);
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
return (oi.readObject());
} public static void main(String[] args) throws ClassNotFoundException, IOException {
People people = new People("test1",20);
ShallowStudent s1 = new ShallowStudent("test2",21,people);
ShallowStudent s2 = (ShallowStudent) s1.deepClone();
s2.name = "test3";
s2.age = 22;
System.out.println("s1 name: "+s1.name + " s1 age: "+s1.age + " People: " + s1.getPeople());
System.out.println("s2 name: "+s2.name + " s2 age: "+s2.age + " People: " + s2.getPeople());
} } class People implements Serializable{ public String peopleName; public int peopleAge; public People(String peopleName,int peopleAge){
this.peopleName = peopleName;
this.peopleAge = peopleAge;
}
} -----------》》》---------
s1 name: test2 s1 age: 21 People: com.pingan.haofang.fjs.web.action.People@232204a1
s2 name: test3 s2 age: 22 People: com.pingan.haofang.fjs.web.action.People@4554617c
两个对象是不一样的
----------《《《----------

设计模式(3)-- 原型模式 (clone分析)的更多相关文章

  1. 设计模式之 原型模式详解(clone方法源码的简单剖析)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 原型模式算是JAVA中最简单 ...

  2. c# 24种设计模式5原型模式(Prototype)

    前言 原型模式其实C# Object中已经提供了一个Clone( )方法,平时很少用到,最近读Retrofit源码时候看到有这种使用方式. 定义 原型模式就是在系统clone()标记的基础上,对Clo ...

  3. C#设计模式(6)——原型模式(Prototype Pattern) C# 深浅复制 MemberwiseClone

    C#设计模式(6)——原型模式(Prototype Pattern)   一.引言 在软件系统中,当创建一个类的实例的过程很昂贵或很复杂,并且我们需要创建多个这样类的实例时,如果我们用new操作符去创 ...

  4. C# Json反序列化 C# 实现表单的自动化测试<通过程序控制一个网页> 验证码处理类:UnCodebase.cs + BauDuAi 读取验证码的值(并非好的解决方案) 大话设计模式:原型模式 C# 深浅复制 MemberwiseClone

    C# Json反序列化   Json反序列化有两种方式[本人],一种是生成实体的,方便处理大量数据,复杂度稍高,一种是用匿名类写,方便读取数据,较为简单. 使用了Newtonsoft.Json,可以自 ...

  5. java设计模式4——原型模式

    java设计模式4--原型模式 1.写在前面 本节内容与C++语言的复制构造函数.浅拷贝.深拷贝极为相似,因此建议学习者可以先了解C++的该部分的相关知识,或者学习完本节内容后,也去了解C++的相应内 ...

  6. 设计模式_11_原型模式(prototype)深拷贝、浅拷贝

    设计模式_11_原型模式(prototype) 浅拷贝: package designPatternOf23; /** * 定义:用原型实例,指定创建对象的种类,并通过拷贝这些原型创建新的对象 * P ...

  7. C#设计模式(6)——原型模式(Prototype Pattern)

    一.引言 在软件系统中,当创建一个类的实例的过程很昂贵或很复杂,并且我们需要创建多个这样类的实例时,如果我们用new操作符去创建这样的类实例,这未免会增加创建类的复杂度和耗费更多的内存空间,因为这样在 ...

  8. 乐在其中设计模式(C#) - 原型模式(Prototype Pattern)

    原文:乐在其中设计模式(C#) - 原型模式(Prototype Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 原型模式(Prototype Pattern) 作者:weba ...

  9. C#设计模式之六原型模式(Prototype)【创建型】

    一.引言 在开始今天的文章之前先说明一点,欢迎大家来指正.很多人说原型设计模式会节省机器内存,他们说是拷贝出来的对象,这些对象其实都是原型的复制,不会使用内存.我认为这是不对的,因为拷贝出来的每一个对 ...

随机推荐

  1. codeforces 433C. Ryouko's Memory Note 解题报告

    题目链接:http://codeforces.com/problemset/problem/433/C 题目意思:一本书有 n 页,每页的编号依次从 1 到 n 编排.如果从页 x 翻到页 y,那么| ...

  2. 【Selenium】显示、隐式等待

    显示等待 WebDriverWait 超时抛出TimeOutException,默认500毫秒 public class WaitToReturnElement { /* * 设置超时时间为5秒,返回 ...

  3. 调节音量的各个方法——AudioManager的使用

    AudioManager类位于android.Media包中,该类提供访问控制音量和铃声模式的操作. //获取AudioManager实例对象 AudioManager audioManage = ( ...

  4. codevs1258关路灯

    传送门 1258 关路灯  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 大师 Master   题目描述 Description 多瑞卡得到了一份有趣而高薪的工作.每天早晨他 ...

  5. 实现列表两端完全对其text-aligh:justify 和display:inline-block

    dispaly:inline-block 对于块级元素,在IE8-下是不兼容的,所以一般不要用在这些元素上,可以用在inline元素上:span text-align:justify实现子元素完全对齐 ...

  6. python之路,day6-面向对象

    一.面向过程编程 简单的说就是:如果你只是写一些简单的脚本,去做一些一次性任务,用面向过程的方式是极好的,但是如果你要处理的任务比较复杂,且需要不断迭代和维护的,那还是用面向对象最方便了. 二.面向对 ...

  7. Windows下findstr命令的使用

    命令:findstr 参数解释 /b          如果位于行的开头则匹配模式. /e         如果位于行的末尾则匹配模式. /l         使用文字搜索字符串. /r        ...

  8. J20170426-hm

    ジェネリクス Generics 泛型 バルーン balloon 气球 アングルブラケット Angle bracket 尖括号 プレースホルダ Placeholder 占位符

  9. Android笔记---常用控件以及用法

    这篇文章主要记录下Android的常用控件以及使用的方法,Android 给我们提供了大量的UI控件,合理地使用这些控件就可以非常轻松地编写出相当不错的界面,这些是Android学习的基础,没有什么业 ...

  10. 黑客攻防技术宝典web实战篇:攻击本地编译型应用程序习题

    猫宁!!! 参考链接:http://www.ituring.com.cn/book/885 随书答案. 1. 如果不采用特殊的防御措施,为什么栈缓冲区溢出比堆溢出更容易被攻击者利用? 利用基于栈的溢出 ...