生也有涯,知也无涯。这篇文章主要讲述设计模式-- 原型模式相关的知识,希望能为你提供帮助。
设计模式文章集合:http://aphysia.cn/categories/designpattern
1前言接触过 ??Spring?
? 或者 ??Springboot?
? 的同学或许都了解, ??Bean?
? 默认是单例的,也就是全局共用同一个对象,不会因为请求不同,使用不同的对象,这里我们不会讨论单例,前面已经讨论过单例模式的好处以及各种实现,有兴趣可以了解一下:http://aphysia.cn/archives/designpattern1。除了单例以外,??Spring?
?还可以设置其他的作用域,也就是??scope="prototype"?
?,这就是原型模式,每次来一个请求,都会新创建一个对象,这个对象就是按照原型实例创建的。
2原型模式的定义原型模式,也是创建型模式的一种,是指用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,简单来说,就是拷贝。一般适用于:
优点:
3原型模式的实现方式一般来说,原型模式就是用来复制对象的,那么复制对象必须有原型类,也就是??Prototype?
?,??Prototype?
?需要实现??Cloneable?
?接口,实现这个接口才能被拷贝,再重写??clone()?
?方法,还可以根据不同的类型来快速获取原型对象。
我们先定义一个原型类??Fruit?
?:
public abstract class Fruit implements Cloneable
String name;
float price;
public String getName()
return name;
public void setName(String name)
this.name = name;
public float getPrice()
return price;
public void setPrice(float price)
this.price = price;
public Object clone()
Object clone = null;
try
clone = super.clone();
catch (CloneNotSupportedException e)
e.printStackTrace();
return clone;
@Override
public String toString()
return "Fruit" +
"name=" + name + \\ +
", price=" + price +
;
以及拓展了??Fruit?
?类的实体类??Apple?
?,??Pear?
?,??Watermelon?
?:
public class Apple extends Fruit
public Apple(float price)
name = "苹果";
this.price = price;
public class Pear extends Fruit
public Pear(float price)
name = "雪梨";
this.price = price;
public class Watermelon extends Fruit
public Watermelon(float price)
name = "西瓜";
this.price = price;
创建一个获取不同水果类的缓存类,每次取的时候,根据不同的类型,取出来,拷贝一次返回即可:
public class FruitCache
private static ConcurrentHashMap<
String,Fruit>
fruitMap =
new ConcurrentHashMap<
String,Fruit>
();
static
Apple apple = new Apple(10);
fruitMap.put(apple.getName(),apple);
Pear pear = new Pear(8);
fruitMap.put(pear.getName(),pear);
Watermelon watermelon = new Watermelon(5);
fruitMap.put(watermelon.getName(),watermelon);
public static Fruit getFruit(String name)
Fruit fruit = fruitMap.get(name);
return (Fruit)fruit.clone();
测试一下,分别获取不同的水果,以及对比两次获取同一种类型,可以发现,两次获取的同一种类型,不是同一个对象:
public class Test
public static void main(String[] args)
Fruit apple = FruitCache.getFruit("苹果");
System.out.println(apple);
Fruit pear = FruitCache.getFruit("雪梨");
System.out.println(pear);
Fruit watermelon = FruitCache.getFruit("西瓜");
System.out.println(watermelon);
Fruit apple1 = FruitCache.getFruit("苹果");
System.out.println("是否为同一个对象" + apple.equals(apple1));
结果如下:
Fruitname=苹果, price=10.0
Fruitname=雪梨, price=8.0
Fruitname=西瓜, price=5.0
false
再测试一下,我们看看里面的??name?
?属性是不是同一个对象:
public class Test
public static void main(String[] args)
Fruit apple = FruitCache.getFruit("苹果");
System.out.println(apple);
Fruit apple1 = FruitCache.getFruit("苹果");
System.out.println(apple1);
System.out.println("是否为同一个对象:" + apple.equals(apple1));
System.out.println("是否为同一个字符串对象:" + apple.name.equals(apple1.name));
结果如下,里面的字符串确实还是用的是同一个对象:
Fruitname=苹果, price=10.0
Fruitname=苹果, price=10.0
是否为同一个对象:false
是否为同一个字符串对象:true
这是为什么呢?因为上面使用的clone()是浅拷贝!!!不过有一点,字符串在Java里面是不可变的,如果发生修改,也不会修改原来的字符串,由于这个属性的存在,类似于深拷贝。如果属性是其他自定义对象,那就得注意了,浅拷贝不会真的拷贝该对象,只会拷贝一份引用。
这里不得不介绍一下浅拷贝与深拷贝的区别:
如果我们使用??Fruit apple = apple1?
?,这样只是拷贝了对象的引用,其实本质上还是同一个对象,上面的情况虽然对象是不同的,但是??Apple?
?属性的拷贝还属于同一个引用,地址还是一样的,它们共享了原来的属性对象??name?
?。
那如何进行深拷贝呢?一般有以下方案:
序列化实现深拷贝Serializable
接口。clone()
方法
序列化实现代码如下:
创建一个??Student?
?类和??School?
?类:
import java.io.Serializable;
public class Student implements Serializable
String name;
School school;
public Student(String name, School school)
this.name = name;
this.school = school;
import java.io.Serializable;
public class School implements Serializable
String name;
public School(String name)
this.name = name;
序列化拷贝的类:
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class CloneUtil
public static <
T extends Serializable>
T clone(T obj)
T result = null;
try
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(obj);
objectOutputStream.close();
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
// 返回生成的新对象
result = (T) objectInputStream.readObject();
objectInputStream.close();
catch (Exception e)
e.printStackTrace();
return result;
【设计模式-- 原型模式】测试类:
public class Test
public static void main(String[] args)
School school = new School("东方小学");
Student student =new Student("小明",school);
Student student1= CloneUtil.clone(student);
System.out.println(student.equals(student1));
System.out.println(student.school.equals(student1.school));
上面的结果均是??false?
?,说明确实不是同一个对象,发生了深拷贝。
clone实现深拷贝
前面的??Student?
?和??School?
?都实现??Cloneable?
?接口,然后重写??clone()?
?方法:
public class Student implements Cloneable
String name;
School school;
public Student(String name, School school)
this.name = name;
this.school = school;
@Override
protected Object clone() throws CloneNotSupportedException
Student student = (Student) super.clone();
student.school = (School) school.clone();
return student;
public class School implements Cloneable
String name;
public School(String name)
this.name = name;
@Override
protected Object clone() throws CloneNotSupportedException
return super.clone();
测试类:
public class Test
public static void main(String[] args) throws Exception
School school = new School("东方小学");
Student student =new Student("小明",school);
Student student1= (Student) student.clone();
System.out.println(student.equals(student1));
System.out.println(student.school.equals(student1.school));
测试结果一样,同样都是??false?
?,也是发生了深拷贝。
4总结原型模式适用于创建对象需要很多步骤或者资源的场景,而不同的对象之间,只有一部分属性是需要定制化的,其他都是相同的,一般来说,原型模式不会单独存在,会和其他的模式一起使用。值得注意的是,拷贝分为浅拷贝和深拷贝,浅拷贝如果发生数据修改,不同对象的数据都会被修改,因为他们共享了元数据。
【作者简介】:
秦怀,公众号【秦怀杂货店】作者,技术之路不在一时,山高水长,纵使缓慢,驰而不息。个人写作方向:Java源码解析
,JDBC
,Mybatis
,Spring
,redis
,分布式
,剑指Offer
,LeetCode
等,认真写好每一篇文章,不喜欢标题党,不喜欢花里胡哨,大多写系列文章,不能保证我写的都完全正确,但是我保证所写的均经过实践或者查找资料。遗漏或者错误之处,还望指正。
??剑指Offer全部题解PDF??
??2020年我写了什么???
??开源编程笔记??
关注公众号 ”秦怀杂货店“ 可以领取??剑指 Offer V1?
?版本的 ??PDF?
?解法,V2版本增加了题目,还在哼哧哼哧的更新中,并且为每道题目增加了??C++?
?解法,敬请期待。
推荐阅读
- 在windows vm上部署Azure Pipelines Agent
- Windows主机上运行Azure IoT Edge的推荐方法
- #私藏项目实操分享#分布式技术专题「分布式ID系列」百度开源的分布式高性能的唯一ID生成器UidGenerator
- #yyds干货盘点#函数高级用法递归和高级工具expect等
- #yyds干货盘点#Vuex实战讲解(全),玩转Vue必备知识
- spring-boot-devtools 快速重启的秘密!#yyds干货盘点#
- 算法 | 第2章 链表相关《程序员面试金典》#yyds干货盘点#
- #yyds干货盘点#Redis缓存三大问题,一次解决
- #私藏项目实操分享#Java技术开发专题系列之Guava RateLimiter针对于限流器的入门到实战(含源码分析介绍)