java设计模式——原型模式
一. 定义与类型
定义:指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。不需要知道任何创建的细节,不调用构造函数
类型:创建型
二.使用场景
类初始化消耗较多资源
new 产生的一个对象需要非常繁琐的过程(数据准备,访问权限等)
构造函数比较复杂
循环体中生产大量对象时
三.优缺点
优点:
原型模式性能比直接new一个对象性能高,简化创建过程
缺点:
必须配备克隆方法,
对克隆复杂对象或对克隆出的对象进行复杂改造时,容易引入风险
深拷贝,浅拷贝要运用得当
四. 扩展
深克隆:对于引用类型,如果需要指向不同的对象,而对于某个对象的引用类型的时候,必须要显式的去写对那个属性进行深克隆
浅克隆:
五. Coding
/**
* @program: designModel
* @description:
* @author: YuKai Fan
* @create: 2018-12-13 16:11
**/
public class Mail implements Cloneable {
private String name;
private String emailAddress;
private String content;
public Mail() {
System.out.println("Mail Class Constructor");
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getEmailAddress() {
return emailAddress;
} public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
} public String getContent() {
return content;
} public void setContent(String content) {
this.content = content;
} @Override
public String toString() {
return "Mail{" +
"name='" + name + '\'' +
", emailAddress='" + emailAddress + '\'' +
", content='" + content + '\'' + super.toString() +
'}';
} @Override
protected Object clone() throws CloneNotSupportedException {
System.out.println("clone mail object");
return super.clone();
}
}
/**
* @program: designModel
* @description:
* @author: YuKai Fan
* @create: 2018-12-13 16:17
**/
public class MailUtil {
public static void sendMail(Mail mail) {
String outputContent = "向{0}同学,邮件地址:{1},邮件内容:{2}发送成功";
System.out.println(MessageFormat.format(outputContent,mail.getName(),mail.getEmailAddress(),mail.getContent()));
} public static void saveOriginMailRecord(Mail mail) {
System.out.println("存储originMail记录,originMail:" + mail.getContent());
}
}
/**
* @program: designModel
* @description:
* @author: YuKai Fan
* @create: 2018-12-13 16:20
**/
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Mail mail = new Mail();
mail.setContent("初始化模板");
System.out.println("初始化mail:" + mail);
for (int i = 0; i < 10; i++) {
//克隆的时候,并不会使用原对象的构造器
Mail mailTemp = (Mail) mail.clone();
mailTemp.setName("姓名" + i);
mailTemp.setEmailAddress("姓名" + i + "@qq.com");
mailTemp.setContent("恭喜您,中奖了");
MailUtil.sendMail(mailTemp);
}
MailUtil.saveOriginMailRecord(mail);
}
}
从上面的代码不难看出,其实原型模式就是实现了cloneable接口,在创建一个不同的对象,来完成邮件的发送,而保留了原来的邮件模板。由于clone不会,调用原对象的构造器,所以在效率上比直接new 对象要高。但是,因为在Mail类中的属性都是简单类型,所以在clone的时候,基本上不会出现上面问题。但是看下面一个实体:
/**
* @program: designModel
* @description:
* @author: YuKai Fan
* @create: 2018-12-13 16:33
**/
public class Pig implements Cloneable {
private String name;
private Date birthDay; public Pig(String name, Date birthDay) {
this.name = name;
this.birthDay = birthDay;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Date getBirthDay() {
return birthDay;
} public void setBirthDay(Date birthDay) {
this.birthDay = birthDay;
} @Override
public String toString() {
return "Pig{" +
"name='" + name + '\'' +
", birthDay=" + birthDay +
'}';
} @Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
/**
* @program: designModel
* @description:
* @author: YuKai Fan
* @create: 2018-12-13 16:34
**/
public class Test {
public static void main(String[] args) throws CloneNotSupportedException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Date birthDay = new Date(0L);
Pig pig = new Pig("佩奇", birthDay);
Pig pig1 = (Pig) pig.clone();
System.out.println(pig);
System.out.println(pig1); pig.getBirthDay().setTime(6666666666666L);
/**
* 通过上面对birthDay进行操作,如果是浅拷贝,那么pig与pig1的date对象都会改变:
* 因为在浅拷贝的时候,两个对象中的引用对象date,都是引用同一个对象,所以改变了一个,那么两个都会改变
*
* 如果是深拷贝,那么pig与pig1引用的date对象就是不一样的,改变其中一个,对另一个并没有影响。
*
* 由于深克隆,浅克隆的关系,也算是原型模式的一个坑。(原则是,都会使用深克隆,不然就算是给项目埋坑)
*
*/
System.out.println(pig);
System.out.println(pig1);
}
}
输出结果为:
上面的注释和结果,其实也清楚的看到了。我只改变了pig对象中的birthDay,但是pig1中也改变了。如果clone的对象中存在引用类型的对象,那么如果是浅拷贝,拷贝与被拷贝出的对象的引用对象都是指向同一地址的,所以改变其中一个,另一个也会改变。这时候,如果根据需求就必须使用深拷贝。
也就是对于对象中的引用对象也要进行clone。看下面代码:
/**
* @program: designModel
* @description:
* @author: YuKai Fan
* @create: 2018-12-13 16:33
**/
public class Pig implements Cloneable {
private String name;
private Date birthDay; public Pig(String name, Date birthDay) {
this.name = name;
this.birthDay = birthDay;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Date getBirthDay() {
return birthDay;
} public void setBirthDay(Date birthDay) {
this.birthDay = birthDay;
} @Override
public String toString() {
return "Pig{" +
"name='" + name + '\'' +
", birthDay=" + birthDay +
'}';
} @Override
protected Object clone() throws CloneNotSupportedException {
Pig pig = (Pig) super.clone(); //深克隆
pig.birthDay = (Date) pig.birthDay.clone();
return pig;
}
}
输出结果:
可以看到,Pig中重写了clone方法,对Date对象也进行了clone,从而使得,引用对象指向不同的地址。所以改变pig中的birthDay,对于pig1并没有影响。
由于clone方法,可以用原型模式拷贝来破坏单例模式:
/**
* @program: designModel
* @description:
* @author: YuKai Fan
* @create: 2018-12-13 16:34
**/
public class Test {
public static void main(String[] args) throws CloneNotSupportedException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { /**
* 使用原型模式,克隆破坏单例模式
*
* 这种情况的解决方式:
* 要么单例模式类不去实现Cloneable接口,要么就重写clone方法,直接返回getInstance()方法,这个对象的实例
*/
HungrySingleton hungrySingleton = HungrySingleton.getInstance();
Method method = hungrySingleton.getClass().getDeclaredMethod("clone");
method.setAccessible(true);
HungrySingleton cloneHungrySingleton = (HungrySingleton) method.invoke(hungrySingleton);
System.out.println(hungrySingleton);
System.out.println(cloneHungrySingleton);
}
}
六. 源码分析
基本上只要知道了哪些类使用了Cloneable就知道,原型模式如何使用。
比如ArrayList,HashMap类都重写了clone方法
Mybatis中的CacheKey类也重写了clone()方法
java设计模式——原型模式的更多相关文章
- 【设计模式】Java设计模式 - 原型模式
[设计模式]Java设计模式 - 原型模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一起 ...
- 我的Java设计模式-原型模式
"不好意思,我是卧底!哇哈哈哈~"额......自从写了上一篇的观察者模式,就一直沉浸在这个角色当中,无法自拨.昨晚在看<使徒行者2>,有一集说到啊炮仗哥印钞票,我去, ...
- Java设计模式—原型模式
原型设计模式是一种比较简单的设计模式,在项目中使用的场景非常多. 个人理解: 原型模式实现了对Java中某个对象的克隆功能,即该对象的类必须implements实现Cloneable接口来标识为可被克 ...
- Java设计模式-原型模式(Prototype)
原型模式属于对象的创建模式.通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象.这就是选型模式的用意. 原型模式的结构 原型模式要求对象实现一个可以“克 ...
- java设计模式---原型模式
原型模式(Prototype):用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 原型模式结构图 通俗来说:原型模式就是深拷贝和浅拷贝的实现. 浅拷贝 只实现了值拷贝,对于引用对象还是 ...
- 4.java设计模式-原型模式(prototype)
在<JAVA与模式>一书中开头是这样描述原型(Prototype)模式的: 原型模式属于对象的创建模式.通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更 ...
- Java设计模式原型模式
原型模式: – 通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式. – 就是java中的克隆技术,以某个对象为原型,复制出新的对象.显然,新的对象具备原型对象的特点 – 优势 ...
- PHP 设计模式 原型模式(Prototype)之深/浅拷贝
看PHP 设计模式 原型模式(Prototype)时,衍生出一个扩展问题之 原型拷贝的浅拷贝和深拷贝问题(不管写Java还是写PHP还是写JS时都多多少少遇到过对象拷贝问题) 比如写前端页面时 ...
- 10. 星际争霸之php设计模式--原型模式
题记==============================================================================本php设计模式专辑来源于博客(jymo ...
随机推荐
- 今天实现一个T-sql的小编程,分享给大家,看看就好~(列值赋值)
介绍:将一个表的某列值插入到另一个表的列里,暂定为表aa和表nn DECLARE @a int set @a=1 while @a<nn.列1.length --(注:语法不对你可以用查询 ...
- GUI的最终选择 Tkinter(四):Entry、Listbox、Scrollbar和Scale组件
Entry组件 Entry组件就是平时所说的输入框.输入框是程序员用到的最多的一个程序,例如在输入账号和密码的时候需要提供两个输入框,用于接收密码的输入框还会有星号将实际输入的内容隐藏起来. Tkin ...
- 【ACM】大数阶乘 - Java BigInteger实现
大数阶乘 时间限制:3000 ms | 内存限制:65535 KB 难度:3 描述 我们都知道如何计算一个数的阶乘,可是,如果这个数很大呢,我们该如何去计算它并输出它? 输入 输入一个整数 ...
- 配置HEXO
软件介绍 Hexo 是一个快速.简洁且高效的博客框架.Hexo 使用 Markdown(或其他渲染引擎)解析文章,在几秒内,即可利用靓丽的主题生成静态网页. 使用版本和操作系统 官网最新版 Windo ...
- (转)Linux 文件和目录的属性
linux 文件属性与权限 原文:https://www.cnblogs.com/kzloser/articles/2673790.html https://www.cnblogs.com/danh/ ...
- jquery的.get方法说解
·Customer类 public class Customer { public int Unid { get; set; } public string CustomerName { get; s ...
- JS展示预览PDF。
刚好遇到需求,需要在手机端--展示一个电子收据,电子收据返回是PDF格式的,所以需要在前端上面去做PDF预览. 在学习过程中,了解到一种很简单,不需要任何插件的方法做PDF预览,但是这方法有局限性. ...
- Oracle单行函数。。。
单行函数 --字符函数--1.ASCII 返回与指定的字符对应的十进制数;select ascii('A') A,ascii('a') a,ascii('0') zero,ascii(' ') spa ...
- Ubuntu 14.04上安装Riak
一.安装riak 1. 安装包:riak_2.0.2-1_amd64.deb 路 径:~/software/riak_2.0.2-1_amd64.deb 2. 安装riak $ ...
- vue3.0学习笔记(二)
一.选择合适的ide 推荐使用vs code编辑器,界面清晰.使用方便,控制台功能很好用.webstorm也可以,看个人喜好. 二.ui框架选择 目前,pc端一般是选择element ui(饿了么), ...