设计模式04----原型设计模式(prototype)

参考文献:https://www.cnblogs.com/lwbqqyumidi/p/3746821.html

首先对原型模式进行一个简单概念说明:通过一个已经存在的对象,复制出更多的具有与此对象具有相同类型的新的对象。

在理解Java原型模式之前,首先需要理解Java中的一个概念:复制/克隆。

一. java.lang.Object类中的clone方法

源码如下:

protected native Object clone() throws CloneNotSupportedException;

看,clone()方法又是一个被声明为native的方法,因此,我们知道了clone()方法并不是Java的原生方法,具体的实现是有C/C++完成的。clone英文翻译为"克隆",其目的是创建并返回此对象的一个副本。形象点理解,这有一辆科鲁兹,你看着不错,想要个一模一样的。你调用此方法即可像变魔术一样变出一辆一模一样的科鲁兹出来。配置一样,长相一样。但从此刻起,原来的那辆科鲁兹如果进行了新的装饰,与你克隆出来的这辆科鲁兹没有任何关系了。你克隆出来的对象变不变完全在于你对克隆出来的科鲁兹有没有进行过什么操作了。Java术语表述为:clone函数返回的是一个引用,指向的是新的clone出来的对象,此对象与原对象分别占用不同的堆空间。

明白了clone的含义后,接下来看看如果调用clone()函数对象进行此克隆操作。

首先看一下下面的这个例子:

 1 package com.corn.objectsummary;
 2
 3 import com.corn.Person;
 4
 5 public class ObjectTest {
 6
 7     public static void main(String[] args) {
 8
 9         Object o1 = new Object();
10         // The method clone() from the type Object is not visible
11         Object clone = o1.clone();
12     }
13
14 }

例子很简单,在main()方法中,new一个Oject对象后,想直接调用此对象的clone方法克隆一个对象,但是出现错误提示:"The method clone() from the type Object is not visible"

why? 根据提示,第一反应是ObjectTest类中定义的Oject对象无法访问其clone()方法。回到Object类中clone()方法的定义,可以看到其被声明为protected,估计问题就在这上面了,protected修饰的属性或方法表示:在同一个包内或者不同包的子类可以访问。显然,Object类与ObjectTest类在不同的包中,但是ObjectTest继承自Object,是Object类的子类,于是,现在却出现子类中通过Object引用不能访问protected方法,原因在于对"不同包中的子类可以访问"没有正确理解。

"不同包中的子类可以访问",是指当两个类不在同一个包中的时候,继承自父类的子类内部且主调(调用者)为子类的引用时才能访问父类用protected修饰的成员(属性/方法)。 在子类内部,主调为父类的引用时并不能访问此protected修饰的成员。!(super关键字除外)

于是,上例改成如下形式,我们发现,可以正常编译:

 1 package com.corn.objectsummary;
 2
 3
 4 public class ObjectTest {
 5
 6     public static void main(String[] args) {
 7         ObjectTest ot1 = new ObjectTest();
 8
 9         try {
10             ObjectTest ot2 = (ObjectTest) ot1.clone();
11         } catch (CloneNotSupportedException e) {
12             // TODO Auto-generated catch block
13             e.printStackTrace();
14         }
15     }
16
17 }

是的,因为此时的主调已经是子类的引用了。

上述代码在运行过程中会抛出"java.lang.CloneNotSupportedException",表明clone()方法并未正确执行完毕,问题的原因在与Java中的语法规定:

clone()的正确调用是需要实现Cloneable接口,如果没有实现Cloneable接口,并且子类直接调用Object类的clone()方法,则会抛出CloneNotSupportedException异常。

Cloneable接口仅是一个表示接口,接口本身不包含任何方法,用来指示Object.clone()可以合法的被子类引用所调用。

于是,上述代码改成如下形式,即可正确指定clone()方法以实现克隆。

 1 package com.corn.objectsummary;
 2
 3 public class ObjectTest implements Cloneable {
 4
 5     public static void main(String[] args) {
 6
 7         ObjectTest ot1 = new ObjectTest();
 8
 9         try {
10             ObjectTest ot2 = (ObjectTest) ot1.clone();
11             System.out.println("ot2:" + ot2);
12              System.out.println(ot1.equals(ot2));
13             System.out.println(ot1==ot2);
14         } catch (CloneNotSupportedException e) {
15             // TODO Auto-generated catch block
16             e.printStackTrace();
17         }
18     }
19
20 }
21
22
23
24 ot2:[email protected]
25 false
26 false

Java中的对象复制/克隆分为浅复制和深复制。

1. 浅复制

我们知道,一个类的定义中包括属性和方法。属性用于表示对象的状态,方法用于表示对象所具有的行为。其中,属性既可以是Java中基本数据类型,也可以是引用类型。Java中的浅复制通常使用clone()方式完成。

当进浅复制时,clone函数返回的是一个引用,指向的是新的clone出来的对象,此对象与原对象分别占用不同的堆空间。同时,复制出来的对象具有与原对象一致的状态。

此处对象一致的状态是指:复制出的对象与原对象中的属性值完全相等==。

下面以复制一本书为例:

(1)定义Book类和Author类:

 1 class Author {
 2
 3     private String name;
 4     private int age;
 5
 6     public String getName() {
 7         return name;
 8     }
 9
10     public void setName(String name) {
11         this.name = name;
12     }
13
14     public int getAge() {
15         return age;
16     }
17
18     public void setAge(int age) {
19         this.age = age;
20     }
21
22 }

 1 class Book implements Cloneable {
 2
 3     private String title;
 4     private int pageNum;
 5     private Author author;
 6
 7     public Book clone() {
 8         Book book = null;
 9         try {
10             book = (Book) super.clone();
11         } catch (CloneNotSupportedException e) {
12             // TODO Auto-generated catch block
13             e.printStackTrace();
14         }
15         return book;
16     }
17
18     public String getTitle() {
19         return title;
20     }
21
22     public void setTitle(String title) {
23         this.title = title;
24     }
25
26     public int getPageNum() {
27         return pageNum;
28     }
29
30     public void setPageNum(int pageNum) {
31         this.pageNum = pageNum;
32     }
33
34     public Author getAuthor() {
35         return author;
36     }
37
38     public void setAuthor(Author author) {
39         this.author = author;
40     }
41
42 }

(2)测试

 1 package com.qqyumidi;
 2
 3 public class PrototypeTest {
 4
 5     public static void main(String[] args) {
 6         Book book1 = new Book();
 7         Author author = new Author();
 8         author.setName("corn");
 9         author.setAge(100);
10         book1.setAuthor(author);
11         book1.setTitle("好记性不如烂博客");
12         book1.setPageNum(230);
13
14         Book book2 = book1.clone();
15
16         System.out.println(book1 == book2);  // false
17         System.out.println(book1.getPageNum() == book2.getPageNum());   // true
18         System.out.println(book1.getTitle() == book2.getTitle());        // true
19         System.out.println(book1.getAuthor() == book2.getAuthor());        // true
20
21     }
22 }

由输出的结果可以验证说到的结论。由此我们发现:虽然复制出来的对象重新在堆上开辟了内存空间,但是,对象中各属性确保持相等。对于基本数据类型很好理解,但对于引用数据类型来说,则意味着此引用类型的属性所指向的对象本身是相同的, 并没有重新开辟内存空间存储。换句话说,引用类型的属性所指向的对象并没有复制。

由此,我们将其称之为浅复制。当复制后的对象的引用类型的属性所指向的对象也重新得以复制,此时,称之为深复制。

2. 深复制

Java中的深复制一般是通过对象的序列化和反序列化得以实现。序列化时,需要实现Serializable接口。

下面还是以Book为例,看下深复制的一般实现过程:

(1)定义Book类和Author类(注意:不仅Book类需要实现Serializable接口,Author同样也需要实现Serializable接口!!):

 1 class Author implements Serializable{
 2
 3     private String name;
 4     private int age;
 5
 6     public String getName() {
 7         return name;
 8     }
 9
10     public void setName(String name) {
11         this.name = name;
12     }
13
14     public int getAge() {
15         return age;
16     }
17
18     public void setAge(int age) {
19         this.age = age;
20     }
21
22 }

 1 class Book implements Serializable {
 2
 3     private String title;
 4     private int pageNum;
 5     private Author author;
 6
 7     public Book deepClone() throws IOException, ClassNotFoundException{
 8         // 写入当前对象的二进制流
 9         ByteArrayOutputStream bos = new ByteArrayOutputStream();
10         ObjectOutputStream oos = new ObjectOutputStream(bos);
11         oos.writeObject(this);
12
13         // 读出二进制流产生的新对象
14         ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
15         ObjectInputStream ois = new ObjectInputStream(bis);
16         return (Book) ois.readObject();
17     }
18
19     public String getTitle() {
20         return title;
21     }
22
23     public void setTitle(String title) {
24         this.title = title;
25     }
26
27     public int getPageNum() {
28         return pageNum;
29     }
30
31     public void setPageNum(int pageNum) {
32         this.pageNum = pageNum;
33     }
34
35     public Author getAuthor() {
36         return author;
37     }
38
39     public void setAuthor(Author author) {
40         this.author = author;
41     }
42
43 }

(2)测试

 1 public class PrototypeTest {
 2
 3     public static void main(String[] args) throws ClassNotFoundException, IOException {
 4         Book book1 = new Book();
 5         Author author = new Author();
 6         author.setName("corn");
 7         author.setAge(100);
 8         book1.setAuthor(author);
 9         book1.setTitle("好记性不如烂博客");
10         book1.setPageNum(230);
11
12         Book book2 = book1.deepClone();
13
14         System.out.println(book1 == book2);  // false
15         System.out.println(book1.getPageNum() == book2.getPageNum());   // true
16         System.out.println(book1.getTitle() == book2.getTitle());        // false
17         System.out.println(book1.getAuthor() == book2.getAuthor());        // false
18
19     }
20 }

从输出结果中可以看出,深复制不仅在堆内存上开辟了空间以存储复制出的对象,甚至连对象中的引用类型的属性所指向的对象也得以复制,重新开辟了堆空间存储。

二. 原型设计模式

其实上面的两个例子都是使用了原型设计模式。它有两个要点,一个是原型,另外一个是创建原型的复制对象的地方

使用场景(其中的一种):

如果创建新对象成本较大,我们可以利用已有的对象进行复制来获得。

原文地址:https://www.cnblogs.com/Hermioner/p/10014719.html

时间: 2025-01-25 03:52:44

设计模式04----原型设计模式(prototype)的相关文章

二十四种设计模式:原型模式(Prototype Pattern)

原型模式(Prototype Pattern) 介绍用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象. 示例有一个Message实体类,现在要克隆它. MessageModel using System; using System.Collections.Generic; using System.Text; namespace Pattern.Prototype { /// <summary> /// Message实体类 /// </summary> publi

设计模式之:原型设计模式

原型设计模式(Prototype Design Pattern)很有意思, 因为它使用了一种克隆技术来复制实例化的对象. 新对象是通过复制原型实例来创建的. 在这里, 实例是批实例化的具体类.原型设计模式的目的是通过使用克隆来减少实例化对象的开销.与其从一个类实例化新对象, 完全可以使用一个已有实例的克隆. 克隆函数 PHP中使用原型设计模式的关键是要了解如何使用内置函数__clone(). <?php abstract class CloneObject { public $name; pub

设计模式之原型设计模式

原型设计模式 原型设计模式的定义是原型实例指定对象的类型,通过拷贝这些原型来创建新的对象 原型设计的例子 以<>例子为例子:我们求职投简历,经历这一栏在投不同的公司需要修改,我们不要再去创建个人信息.工作经历, 通过拷贝来减少简历中对象的创建. 简历父类 package com.learn.java.javabase.designpattern.prototype; import lombok.Data; @Data public class Resume implements Cloneab

设计模式 笔记 原型模式 prototype

//---------------------------15/04/07---------------------------- //prototype 原型模式--对象创建型模式 /* 1:意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 2:动机: 3:适用性: 1>当一个系统应该独立于它的产品创建.构成和表示时 2>当要实例化的类是在运行时刻制定时,例如通过动态装载 3>为了避免创建一个与产品类层次平行的工厂类层次时 4>当一个类的实例只能有几个不同状

设计模式之原型(prototype)模式

相信大多数的人都看过<西游记>,对孙悟空拔毛变出小猴子的故事情节应该都很熟悉.孙悟空可以用猴毛根据自己的形象复制出很多跟自己一模一样的小猴兵出来,其实在设计模式中也有一个类似的模式,我们可以通过一个原型对象来克隆出多个一模一样的对象,这个模式就是原型模式. 一 大同小异的工作周报 M公司一直在使用自行开发的一个OA系统进行日常工作办理,但在使用过程中,越来越多的人对工作周报的创建和编写模块产生了抱怨.追其原因,M公司的OA管理员发现,由于某些岗位每周工作存在重复性,工作周报内容都大同小异,如下

Java设计模式四: 原型模式(Prototype Pattern)

网上找了好多这个模型的资料说的都不透彻,看了半天都是云里雾里.只好自己操刀研究一把. 原型模式是一种创建型设计模式,它通过复制一个已经存在的实例来返回新的实例,而不是新建实例.被复制的实例就是我们所称的原型,这个原型是可定制的.原型模式多用于创建复杂的或者耗时的实例, 因为这种情况下,复制一个已经存在的实例可以使程序运行更高效,或者创建值相等,只是命名不一样的同类数据. 原型模式中的拷贝分为"浅拷贝"和"深拷贝":浅拷贝: 对值类型的成员变量进行值的复制,对引用类型

【设计模式】—— 原型模式Prototype

前言:[模式总览]——————————by xingoo 模式意图 由于有些时候,需要在运行时指定对象时哪个类的实例,此时用工厂模式就有些力不从心了.通过原型模式就可以通过拷贝函数clone一个原有的对象,给现在的对象使用,从而创建更多的同类型的对象. 模式结构 [简单原型模式]用于原型的版本不多的时候 [登记模式的原型模式]如果原型的实现很多种版本,那么通过一个登记管理类,可以方便的实现原型的管理. Prototype 原型接口,定义原型的结构. ConcretePrototype 原型的具体

创建型设计模式之原型模式(Prototype)

结构   意图 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 适用性 当要实例化的类是在运行时刻指定时,例如,通过动态装载:或者 为了避免创建一个与产品类层次平行的工厂类层次时:或者 当一个类的实例只能有几个不同状态组合中的一种时.建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些. 1 using System; 2 3 // Objects which are to work as prototypes must be based on class

设计模式之原型模式prototype

1.原型模式的使用和本质.以及优势: a.通过 new 产生一个对象需要非常繁琐的数据准备或者访问权限,则可以使用原型模式. b.原型模式的使用就是 java 中的克隆技术,以某个对象为原型,复制出新的对象.显然,新的对象具备原型对象的特点. c.其优势有:效率高(直接克隆,避免了重新执行构造过程步骤). d.克隆类似于 new, 但是不同于 new .new 创建新的对象属性采用的是默认值.克隆出的对象的属性值完全和原型对象相同.并且克隆出来的新对象改变不会影响原型对象.(可以对克隆对象修改属

设计模式之原型模式 Prototype

public class Sheep implements Cloneable,Serializable{ private String sname; private Date birthday; @Override protected Object clone() throws CloneNotSupportedException { Object obj = super.clone(); return obj; } public Sheep(String sname, Date birthd