本文转载自微信公众号「JavaKeeper」,作者不假 。转载本文请联系JavaKeeper公众号。
创新互联公司专注于米东企业网站建设,成都响应式网站建设公司,商城网站定制开发。米东网站建设公司,为米东等地区提供建站服务。全流程按需定制设计,专业设计,全程项目跟踪,创新互联公司专业和态度为您提供的服务
问题
“如果你有一个对象, 并希望生成与其完全相同的一个复制品, 你该如何实现呢?首先, 你必须新建一个属于相同类的对象。然后, 你必须遍历原始对象的所有成员变量, 并将成员变量值复制到新对象中。
- for (int i = 0; i < 10; i++) {
- Sheep sheep = new Sheep("肖恩"+i+"号",2+i,"白色");
- System.out.println(sheep.toString());
- }
这种方式是比较容易想到的,但是有几个不足
“万物兼对象的 Java 中的所有类的根类 Object,提供了一个 clone() 方法,该方法可以将一个 Java 对象复制一份,但是需要实现 clone() 的类必须要实现一个接口 Cloneable,该接口表示该类能够复制且具有复制的能力。这就引出了原型模式。
基本介绍
类图
Java 中 Prototype 类需要具备以下两个条件
实例
“我们用王二小放羊的例子写这个实例
1、原型类(实现 Clonable)
- @Setter
- @Getter
- @NoArgsConstructor
- @AllArgsConstructor
- class Sheep implements Cloneable {
- private String name;
- private Integer age;
- private String color;
- @Override
- protected Sheep clone() {
- Sheep sheep = null;
- try {
- sheep = (Sheep) super.clone();
- } catch (Exception e) {
- System.out.println(e.getMessage());
- }
- return sheep;
- }
- }
2、具体原型
按业务的不同实现不同的原型对象,假设现在主角是王二小,羊群里有山羊、绵羊一大群
- public class Goat extends Sheep{
- public void graze() {
- System.out.println("山羊去吃草");
- }
- }
- public class Lamb extends Sheep{
- public void graze() {
- System.out.println("羔羊去吃草");
- }
- }
3、客户端
- public class Client {
- static List
sheepList = new ArrayList<>(); - public static void main(String[] args) {
- Goat goat = new Goat();
- goat.setName("山羊");
- goat.setAge(3);
- goat.setColor("灰色");
- for (int i = 0; i < 5; i++) {
- sheepList.add(goat.clone());
- }
- Lamb lamb = new Lamb();
- lamb.setName("羔羊");
- lamb.setAge(2);
- lamb.setColor("白色");
- for (int i = 0; i < 5; i++) {
- sheepList.add(lamb.clone());
- System.out.println(lamb.hashCode()+","+lamb.clone().hashCode());
- }
- for (Sheep sheep : sheepList) {
- System.out.println(sheep.toString());
- }
- }
原型模式将克隆过程委派给被克隆的实际对象。模式为所有支持克隆的对象声明了一个通用接口, 该接口让你能够克隆对象,同时又无需将代码和对象所属类耦合。通常情况下,这样的接口中仅包含一个 克隆方法。
所有的类对 克隆方法的实现都非常相似。该方法会创建一个当前类的对象, 然后将原始对象所有的成员变量值复制到新建的类中。你甚至可以复制私有成员变量, 因为绝大部分编程语言都允许对象访问其同类对象的私有成员变量。
支持克隆的对象即为原型。当你的对象有几十个成员变量和几百种类型时, 对其进行克隆甚至可以代替子类的构造。
优势
使用原型模式创建对象比直接 new 一个对象在性能上要好的多,因为 Object 类的 clone 方法是一个本地方法,它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。
使用原型模式的另一个好处是简化对象的创建,使得创建对象就像我们在编辑文档时的复制粘贴一样简单。
因为以上优点,所以在需要重复地创建相似对象时可以考虑使用原型模式。比如需要在一个循环体内创建对象,假如对象创建过程比较复杂或者循环次数很多的话,使用原型模式不但可以简化创建过程,而且可以使系统的整体性能提高很多。
适用场景
《Head First 设计模式》是这么形容原型模式的:当创建给定类的实例的过程很昂贵或很复杂时,就是用原型模式。
如果你需要复制一些对象,同时又希望代码独立于这些对象所属的具体类,可以使用原型模式。
如果子类的区别仅在于其对象的初始化方式, 那么你可以使用该模式来减少子类的数量。别人创建这些子类的目的可能是为了创建特定类型的对象
原型模式在 Spring 中的应用
我们都知道 Spring bean 默认是单例的,但是有些场景可能需要原型范围,如下
同样,王二小还是有 10 只羊,感兴趣的也可以看下他们创建的对象是不是同一个
- public class Client {
- public static void main(String[] args) {
- ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- for (int i = 0; i < 10; i++) {
- Object bean = context.getBean("sheep");
- System.out.println(bean);
- }
- }
- }
感兴趣的同学可以深入源码看下具体的实现,在 AbstractBeanFactory 的 doGetBean() 方法中
原型模式的注意事项
浅拷贝和深拷贝
首先需要明白,浅拷贝和深拷贝都是针对一个已有对象的操作。
在 Java 中,除了基本数据类型(元类型)之外,还存在 类的实例对象 这个引用数据类型。而一般使用 『 = 』号做赋值操作的时候。对于基本数据类型,实际上是拷贝的它的值,但是对于对象而言,其实赋值的只是这个对象的引用,将原对象的引用传递过去,他们实际上还是指向的同一个对象。
而浅拷贝和深拷贝就是在这个基础之上做的区分,如果在拷贝这个对象的时候,只对基本数据类型进行了拷贝,而对引用数据类型只是进行了引用的传递,而没有真实的创建一个新的对象,则认为是浅拷贝。反之,在对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量,则认为是深拷贝。
“所谓的浅拷贝和深拷贝,只是在拷贝对象的时候,对 类的实例对象 这种引用数据类型的不同操作而已
浅拷贝
- Sheep s = new Sheep();
- s.setName("sss");
- s.friend = new Sheep();
- s.friend.setName("喜洋洋");
- Sheep s1 = s.clone();
- System.out.println(s == s1);
- System.out.println(s.hashCode()+"---"+s.clone().hashCode());
- System.out.println(s.friend == s1.friend);
- System.out.println(s.friend.hashCode() + "---" +s1.friend.hashCode());
- false
- 621009875---1265094477
- true
- 2125039532---2125039532
深拷贝
现在我们知道 clone() 方法,只能对当前对象进行浅拷贝,引用类型依然是在传递引用。那如何进行一个深拷贝呢?
常见的深拷贝实现方式有两种:
浅拷贝和深拷贝只是相对的,如果一个对象内部只有基本数据类型,那用 clone() 方法获取到的就是这个对象的深拷贝,而如果其内部还有引用数据类型,那用 clone() 方法就是一次浅拷贝的操作。
分享标题:从原型模式到浅拷贝和深拷贝
网站地址:http://www.shufengxianlan.com/qtweb/news8/500358.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联