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

在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. linux 设备驱动程序中的一些关联性思考

    首先,个人感觉设备驱动程序与应用程序中的文件操作隔得有点远,用户空间不论是直接使用系统调用还是库函数都是通过系统调用的接口进入内核空间代码的.但是看过一个博客的分析整个过程,感觉中间层太过麻烦,必须经 ...

  2. SpringBoot配置文件详解

    自定义属性与加载 com.dongk.selfproperty.title=wangdkcom.dongk.selfproperty.name=10000 然后通过@Value("${属性名 ...

  3. NavigationView更改菜单icon和title颜色变化效果

    NavigationView menu默认icon和title会随着菜单状态改变而改变,选择某个菜单后再次打开侧边菜单后会发现该菜单的icon和title会变成应用的主颜色,其他菜单项仍然为黑色. 如 ...

  4. VMware Ubuntu 共享文件夹

    /**************************************************************************** * VMware Ubuntu 共享文件夹 ...

  5. Logistic/Softmax Regression

    辅助函数 牛顿法介绍 %% Logistic Regression close all clear %%load data x = load('ex4x.dat'); y = load('ex4y.d ...

  6. JSP有哪些内置对象

    JSP有哪些内置对象? 1.page:JSP网页本身; 2.request:用户端请求,此请求会包含来自GET/POST请求的参数; 3.session:请求有关的会话; 4.application: ...

  7. Python 函数的参数传递

    C/C++中,传递参数的类型是可以指定的.一般来说,传递参数可以分为两种:值传递和引用传递.对于值传递,参数传递的过程中进行了复制操作,也就是说,在函数中对参数的任何改动都不会影响到传入的变量:对于引 ...

  8. iOS View

    创建: 2018/04/19 完成: 2018/04/20 View的创建  创建  storyboard上操作  与代码连接 ● 目的: 通过代码控制view ● 按住option拖动 View的坐 ...

  9. python __builtins__ reversed类 (58)

    58.'reversed',  返回一个反转的迭代器. class reversed(object) | reversed(sequence) -> reverse iterator over ...

  10. springboot整合H2内存数据库,实现单元测试与数据库无关性

    一.新建spring boot工程 新建工程的时候,需要加入JPA,H2依赖 二.工程结构   pom文件依赖如下: <?xml version="1.0" encoding ...