本篇内容主要讲解“Spring中的原型模式是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Spring中的原型模式是什么”吧!
其定义为: >使用原型实例指定将要创建的对象类型,通过复制这个实例创建新的对象。
具体来说就是,通过给出一个原型对象来指明所创建的对象的类型,然后使用自身实现的克隆接口来复制这个原型对象,该模式就是用这种方式来创建出更多同类型的对象。
这样的好处是: >Object 类的 clone() 方法是一个本地方法,它可以直接操作内存中的二进制流,所以性能相对 new 实例化来说,更加优秀。
一个对象通过 new 实例化创建过程为:
在内存中开辟一块空间。
在开辟的内存空间中创建对象。
调用对象的构造函数进行初始化对象。
而一个对象通过 clone() 创建过程为:
根据原对象内存大小开辟一块内存空间。
复制已有对象,克隆对象中所有属性值。
相对 new 来说,clone() 少了调用构造函数。如果构造函数中存在大量属性初始化或大对象,则使用 clone() 的复制对象的方式性能会好一些。
让我们通过一个例子来具体了解一下:
/** * 实现Cloneable 接口的原型抽象类Prototype */ public class Prototype implements Cloneable { /** * 重写 clone() 方法 */ @Override public Prototype clone() { Prototype prototype = null; try { prototype = (Prototype) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return prototype; } } /** * 实现原型类 */ public class ConcretePrototype extends Prototype { public void show() { System.out.println("原型模式实现类"); } } /** * 测试类 */ public class Client { public static void main(String[] args) { ConcretePrototype cp = new ConcretePrototype(); for (int i = 0; i < 10; i++) { ConcretePrototype cloneCp = (ConcretePrototype) cp.clone(); cloneCp.show(); } } }
当我们实现原型抽象类时,需要注意三点:
实现 Cloneable 接口:Cloneable 接口与序列化接口的作用类似,它只是告诉虚拟机可以安全地在实现了这个接口的类上使用 clone() 方法。在 JVM 中,只有实现了 Cloneable 接口的类才可以被拷贝,否则会抛出 CloneNotSupportedException 异常。
重写 Object 类中的 clone() 方法:在 Java 中,所有类的父类都是 Object 类,而 Object 类中有一个 clone() 方法,作用是返回对象的一个拷贝。
在重写的 clone() 方法中调用 super.clone():默认情况下,类不具备复制对象的能力,需要调用 super.clone() 来实现。
谈到了拷贝,就不得不说到一个经典的问题:深拷贝与浅拷贝
,有的地方也叫深克隆与浅克隆
。
在上面的原型模式中,在调用 super.clone() 方法之后,首先会检查当前对象所属的类是否支持 clone,也就是看该类是否实现了 Cloneable 接口。
如果支持,则创建当前对象所属类的一个新对象,并对该对象进行初始化,使得新对象的成员变量的值与当前对象的成员变量的值一模一样,但对于其它对象的引用以及 List 等类型的成员属性,则只能复制这些对象的引用了
。所以简单调用 super.clone() 这种克隆对象方式,就是一种浅拷贝
。
为了让大家更加清楚浅拷贝
的弊端,举个具体的例子:
Student 类中有一个 Teacher 对象,我们让这两个类都实现 Cloneable 接口:
@Getter @Setter public class Student implements Cloneable{ /** * 学生姓名 */ private String name; /** * 学生所属的老师 */ private Teacher teacher; /** * 重写克隆方法,对学生进行克隆 */ public Student clone() { Student student = null; try { student = (Student) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return student; } } @Getter @Setter public class Teacher implements Cloneable{ /** * 老师姓名 */ private String name; /** * 重写克隆方法,对老师类进行克隆 */ public Teacher clone() { Teacher teacher= null; try { teacher= (Teacher) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return teacher; } }
测试的时候,我们先定义一个学生和一个老师,并让其关联在一起。然后复制之前的学生,生成一个新的学生,修改新学生的老师。
public class Test { public static void main(String args[]) { // 定义老师1 Teacher teacher = new Teacher(); teacher.setName("刘老师"); // 定义学生1 Student stu1 = new Student(); stu1.setName("test1"); // 老师1和学生1进行关联 stu1.setTeacher(teacher); // 复制学生1,生成学生2 Student stu2 = stu1.clone(); stu2.setName("test2"); // 修改学生2的老师 stu2.getTeacher().setName("王老师"); // 查看修改结果 System.out.println("学生" + stu1.getName() + "的老师是:" + stu1.getTeacher().getName()); System.out.println("学生" + stu1.getName() + "的老师是:" + stu2.getTeacher().getName()); } }
我们想要的结果是:
学生test1的老师是:刘老师 学生test2的老师是:王老师
但实际结果是:
学生test1的老师是:王老师 学生test2的老师是:王老师
观察以上运行结果,我们可以发现:在我们给学生2修改老师的时候,学生1的老师也跟着被修改了。这就是浅拷贝带来的问题。
我们可以通过深拷贝
的方式解决这类问题,修改 Student 类的 clone() 方法:
/** * 重写克隆方法,对学生和老师都进行克隆 */ public Student clone() { Student student = null; try { student = (Student) super.clone(); // 克隆 teacher 对象 Teacher teacher = this.teacher.clone(); student.setTeacher(teacher); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return student; }
此时,我们再次运行 Test 中的 main() 方法,就可以得到我们预想的结果了。
在一些重复创建对象的场景下,我们就可以使用原型模式来提高对象的创建性能。例如:循环体内创建对象时,我们就可以考虑用 clone() 的方式来实现。
除此之外,原型模式在开源框架中的应用也非常广泛。例如 Spring 中,@Service 默认都是单例的。用了私有全局变量,若不想影响下次注入或每次上下文获取 bean,就需要用到原型模式,我们可以通过以下注解来实现,@Scope("prototype")。有兴趣的朋友深入了解一下其中的原理。
原型模式,就是针对需要大量复制同一对象的场景,比如用户获取商品、循环体内创建对象等,都是不错的选择,且效率好。
到此,相信大家对“Spring中的原型模式是什么”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。