原型模式
找工作难呐
大家好,我原本是神剑山庄的铸剑师,名叫小赵,本来干的好好的,后来一时兴起,睡了三少爷的小姨子,与其一直提心吊胆,干脆来个逃之夭夭。
但是,我也要吃饭的呀,工作也得找,神剑山庄去不得,还有断剑山庄、藏剑山庄、荡剑山庄、等等等等大型企业,说不定这次跳槽,能跳出个飞黄腾达!
为了提高我投简历的准确性,我觉得简历要写的多样化,不能全写一模一样,比如说我的期望薪资不能写成一样,因为我希望能够根据目标企业的情况来投递合适薪资的简历,这样中标概率大一点。
这是我的简历类:
public class Resume { private String name; private String position; private int salary; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPosition() { return position; } public void setPosition(String position) { this.position = position; } public int getSalary() { return salary; } public void setSalary(int salary) { this.salary = salary; } @Override public String toString() { return "Resume{" + "name='" + name + '\'' + ", position='" + position + '\'' + ", salary=" + salary + '}'; } }
现在,我正在熬夜写简历。。。。。
public static void main(String[] args) { Resume resume1 = new Resume(); resume1.setName("小赵"); resume1.setPosition("高级铸剑工程师"); resume1.setSalary(1000); Resume resume2 = new Resume(); resume2.setName("小赵"); resume2.setPosition("高级铸剑工程师"); resume2.setSalary(1200); Resume resume3 = new Resume(); resume2.setName("小赵"); resume3.setPosition("高级铸剑工程师"); resume3.setSalary(1500); //..... }
当我写到20多份的时候,我已经睡着了。。。。。
唉,找工作难啊。。。。。。
打印机跑起来
第二天,我去万达广场买了个打印机回来,直接循环打印!
public static void main(String[] args) { int num = 5; while (num > 0){ Resume resume1 = new Resume(); int salary = (int)(1000+Math.random()*(2000-1000+1)); resume1.setName("小赵"); resume1.setPosition("高级铸剑工程师"); resume1.setSalary(salary); System.out.println(resume1.toString()); num --; } }
输出:
Resume{name='小赵', position='高级铸剑工程师', salary=1389}
Resume{name='小赵', position='高级铸剑工程师', salary=1383}
Resume{name='小赵', position='高级铸剑工程师', salary=1345}
Resume{name='小赵', position='高级铸剑工程师', salary=1446}
Resume{name='小赵', position='高级铸剑工程师', salary=1221}
先试了一下,打印5份简历,感觉没问题。
有了这个打印机之后,我也接些私活,帮别人打印简历,生意越做越火,一个月后,我工作没找到,但打印机一天到晚倒是没停过。
到了现在,我的打印机一天到晚要打印一百万张简历,每次打印都要耗电耗内存耗机器寿命,我的打印机已经报废了好几台,并且在不断的购买新的打印机。
作为一个工程师,我决定要研究一下有没有更好的解决办法。
复印机跑起来
听说Cloneable接口有个复印机可以用,我决定去试试,效果的好的话就把复印机装到打印机里面去,搞个多功能一体机。
这是修改后的简历类:
public class Resume implements Cloneable{ private String name; private String position; private int salary; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPosition() { return position; } public void setPosition(String position) { this.position = position; } public int getSalary() { return salary; } public void setSalary(int salary) { this.salary = salary; } @Override protected Resume clone(){ Resume resume = null; try{ resume = (Resume) super.clone(); }catch (CloneNotSupportedException e){ e.printStackTrace(); } return resume; } @Override public String toString() { return "Resume{" + "name='" + name + '\'' + ", position='" + position + '\'' + ", salary=" + salary + '}'; } }
复印机跑起来:
public static void main(String[] args) { int num = 5; Resume resume = new Resume(); while (num > 0){ Resume resume1 = resume.clone(); int salary = (int)(1000+Math.random()*(2000-1000+1)); resume1.setName("小赵"); resume1.setPosition("高级铸剑工程师"); resume1.setSalary(salary); System.out.println(resume1.toString()); num --; } }
和打印机的效果是一样的,但是这里事实上只打印了一次,然后其他的都是拷贝复印出来的。
。。。。。。
多年之后,我才恍然大悟,当初我用复印机复印简历的做法,其实就是设计模式中的原型模式。
原型模式的使用场景
大体上有两种使用场景
1.在需要一个类的大量对象的时候,使用原型模式是最佳选择,因为原型模式是在内存中对这个对象进行拷贝,要比直接new这个对象性能要好很多,在这种情况下,需要的对象越多,原型模式体现出的优点越明显。
2.如果一个对象的初始化需要很多其他对象的数据准备或其他资源的繁琐计算,那么可以使用原型模式。
3.当需要一个对象的大量公共信息,少量字段进行个性化设置的时候,也可以使用原型模式拷贝出现有对象的副本进行加工处理。
构造函数的问题
写个程序测一下构造函数:
public class AAA implements Cloneable { public AAA() { System.out.println("我来了。。"); } @Override protected AAA clone(){ AAA aaa = null; try { aaa = (AAA) super.clone(); }catch (CloneNotSupportedException e){ e.printStackTrace(); } return aaa; } }
public static void main(String[] args) { AAA aaa = new AAA(); AAA aaa1 = aaa.clone(); }
输出:
我来了。。
就输出一次,这里可以证明对象拷贝的时候构造函数是不会执行的,原因在于拷贝是直接在堆中进行,这其实也可以理解,new的时候,JVM要走一趟类加载流程,这个流程非常麻烦,在类加载流程中会调用构造函数,最后生成的对象会放到堆中,而拷贝就是直接拷贝堆中的现成的二进制对象,然后重新一个分配内存块。
浅拷贝
上个例子展示什么是浅拷贝
这里新加一个类BBB:
public class BBB{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
AAA类修改为:
public class AAA implements Cloneable { private BBB bbb = new BBB(); public void setName(String name) { this.bbb.setName(name); } public String getName() { return this.bbb.getName(); } @Override protected AAA clone(){ AAA aaa = null; try { aaa = (AAA) super.clone(); }catch (CloneNotSupportedException e){ e.printStackTrace(); } return aaa; } }
main方法:
public static void main(String[] args) { AAA aaa = new AAA(); aaa.setName("张三"); System.out.println(aaa.getName()); AAA aaa1 = aaa.clone(); System.out.println(aaa1.getName()); }
输出:
张三
张三
这种情况就是浅拷贝,java只拷贝你指定的对象,至于你指定的对象里面的别的对象,它不拷贝,还是把引用给你,共享变量,这是一种非常不安全的方式,需要特别注意。
内部的数组和引用对象不会拷贝,其他的原始基本类型和String类型会被拷贝。
如果要实现深拷贝呢?
那就只能在AAA的clone方法里把BBB实例化出来了:
@Override protected AAA clone(){ AAA aaa = null; try { aaa = (AAA) super.clone(); aaa.bbb = new BBB(); }catch (CloneNotSupportedException e){ e.printStackTrace(); } return aaa; }