概述
原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。调用者不需要知道任何创建细节,不调用构造函数。
代码模拟
场景是需要获取同一个姓名的20个学生实例。
创建学生,根据姓名获取学生的时候需要去数据库查询获取学生的分数,耗时100ms
public class Student {
private String name;
private int score;
//根据姓名获取学生的时候需要去数据库查询获取学生的分数,耗时100ms
public Student(String name) {
this.name = name;
try {
System.out.println("正在获取学生对应的得分");
Thread.sleep(100);
System.out.println("学生对应的得分是:" + 100);
this.score = 100;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public Student() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
定义接口,可以通过原型的方式获取Student对象
public interface Prototype {
Student cloneStudent();
}
定义可利用原型模式获取学生对象的实现类
public class ClonableStudent extends Student implements Prototype{
public ClonableStudent(String name) {
super(name);
}
public ClonableStudent() {
}
@Override
public Student cloneStudent() {
//利用无参构造获取空对象,通过属性拷贝的方式初始化新对象
Student stu = new Student();
stu.setName(this.getName());
stu.setScore(this.getScore());
return stu;
}
}
测试类
public class ProtoTypeTest {
public static void main(String[] args) {
String name = "zhangsan";
Student stu = new Student(name);
long start = System.currentTimeMillis();
for(int i=0;i<20;i++){
Student stu2 = new Student(name);
}
long end = System.currentTimeMillis();
System.out.println("创建20个对象耗时:"+(end-start));
//控制台输出:创建20个对象耗时:2184
ClonableStudent clonableStudent = new ClonableStudent(name);
long start2 = System.currentTimeMillis();
for(int i=0;i<20;i++){
Student stu2 = clonableStudent.cloneStudent();
System.out.println("创建完第"+i+"个对象");
}
long end2 = System.currentTimeMillis();
System.out.println("创建20个对象耗时:"+(end2-start2));
//控制台输出:创建20个对象耗时:2184
}
}
总结
原型模式的目的是减少频繁创建对象时候不必要的资源消耗(所谓不必要是指同一个姓名的学生的成绩是相同的,不需要重复去查询数据库,进行这些耗时耗资源的操作)。
浅克隆
创建hobby类,在student类增加hobby属性
public class Hobby {
private String show;
public Hobby(String show) {
this.show = show;
}
public String getShow() {
return show;
}
public void setShow(String show) {
this.show = show;
}
}
public class Student {
private String name;
private int score;
private Hobby hobby ;
...
public Hobby getHobby() {
return hobby;
}
public void setHobby(Hobby hobby) {
this.hobby = hobby;
}
}
public class ClonableStudent extends Student implements Prototype{
public ClonableStudent(String name) {
super(name);
}
public ClonableStudent() {
}
@Override
public Student cloneStudent() {
Student stu = new Student();
stu.setName(this.getName());
stu.setScore(this.getScore());
stu.setHobby(this.getHobby());
return stu;
}
}
测试类
public class SimpleCloneTest {
public static void main(String[] args) {
ClonableStudent student = new ClonableStudent();
student.setName("www");
student.setScore(100);
student.setHobby(new Hobby("sing"));
Student student1 = student.cloneStudent();
System.out.println("源对象修改前得分:"+student1.getScore());//100
System.out.println("源对象修改前姓名:"+student1.getName());//www
System.out.println("源对象修改前兴趣:"+student1.getHobby().getShow());//sing
student.setScore(60);
student.setName("aaa");
student.getHobby().setShow("ball");
System.out.println("源对象修改后得分:"+student1.getScore());//100
System.out.println("源对象修改后姓名:"+student1.getName());//www
System.out.println("源对象修改后兴趣:"+student1.getHobby().getShow());//ball
}
}
源对象的引用类型对象改变时,目标对象对应的属性会被修改,就像兴趣从唱歌变为了打球,但是非引用类型的属性改变时不会引起目标类的属性变化。原因是引用类型复制的是引用。
深克隆
public class DeepTest {
public static void main(String[] args) {
Student student = new Student();
student.setName("www");
student.setScore(100);
student.setHobby(new Hobby("sing"));
Student clonedStudent = deepcloneObj(student);
System.out.println("源对象修改前得分:"+clonedStudent.getScore());//100
System.out.println("源对象修改前姓名:"+clonedStudent.getName());//www
System.out.println("源对象修改前兴趣:"+clonedStudent.getHobby().getShow());//sing
student.setScore(60);
student.setName("aaa");
student.getHobby().setShow("ball");
System.out.println("源对象修改后得分:"+clonedStudent.getScore());//100
System.out.println("源对象修改后姓名:"+clonedStudent.getName());//www
System.out.println("源对象修改后兴趣:"+clonedStudent.getHobby().getShow());//sing
}
/**
* 对象深拷贝,新的对象的修改不会影响原始对象的值
*/
public static <T>T deepcloneObj(T obj) {
ByteArrayOutputStream byteOut = new ByteArrayOutputStream() ;
ObjectInputStream objIn;
try
{
ObjectOutputStream objOut =new ObjectOutputStream(byteOut);
objOut.writeObject(obj);
ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
objIn = new ObjectInputStream(byteIn);
return (T) objIn.readObject();
}
catch (Exception e)
{
System.out.println(e);
}
return null ;
}
}
深克隆的特点是源目标的任何属性变化不会影响克隆后的对象。
值传递与引用传递
值传递
java的基本类型和String都是值传递。意思是在把一个参数传递给方法执行的时候传了值,本身新开辟了一个空间,值和传入的一样。所以在执行完方法后原来的值不变,因为你方法里面相当于在操作一个和你值相同的另一个对象,原来的值当然不变。
引用传递
引用传递意思是传递参数的时候传递的是对象的地址,方法执行前后操作的是根据地址指向的同一个对象。
这么做的原因我猜想是对象的大小不固定,值传递浪费内存太严重。但是值传递的都是基本类型或者是String,大小相对固定而且较小。