原型模式
为什么需要原型模式
实例
- 使用原型模式解决上面问题:
-
- 必须让目标类实现cloneable接口,该接口中没有任何抽象方法。这样的接口仅仅是一个“标记接口”,作用是告诉jvm,任何实现该接口的对象可以被clone。
-
- 必须重写java.lang.Object的clone方法,一定要把该方法的访问修饰符重写为public!!不然无法调用clone方法。
-
import java.util.Date;
class WeekReport implements Cloneable{
private int id;
private String emp; // employer private员工
private String summary;
private String plan;
private String suggestion;
private Date time;
public WeekReport(){
System.out.println("构造方法!");
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public void setEmp(String emp) {
this.emp = emp;
}
public void setSummary(String summary) {
this.summary = summary;
}
public void setPlan(String plan) {
this.plan = plan;
}
public void setSuggestion(String suggestion) {
this.suggestion = suggestion;
}
public void setTime(Date time) {
this.time = time;
}
public String getEmp() {
return emp;
}
public String getSummary() {
return summary;
}
public String getPlan() {
return plan;
}
public String getSuggestion() {
return suggestion;
}
public Date getTime() {
return time;
}
@Override
public String toString() {
return "WeekReport{" +
"id=" + id +
", emp='" + emp + '\'' +
", summary='" + summary + '\'' +
", plan='" + plan + '\'' +
", suggestion='" + suggestion + '\'' +
", time=" + time +
'}';
}
@Override
public Object clone() throws CloneNotSupportedException { // 修饰符要比父类宽松,父类是protected
return super.clone();
}
}
public class Test00 {
public static void main(String[] args) throws Exception {
WeekReport wr = new WeekReport();
wr.setEmp("zgy");
wr.setSummary("eat");
wr.setPlan("no");
wr.setSuggestion("no");
wr.setTime(new Date());
wr.setId(1);
System.out.println(wr);
// 第二个周报只有Plan不一样,但是也要重新设置,不好
WeekReport wr2 = new WeekReport();
wr2.setEmp("zgy");
wr2.setSummary("eat");
wr2.setPlan("YES");
wr2.setSuggestion("no");
wr2.setTime(new Date());
wr2.setId(1);
System.out.println(wr2);
// 原型模式修改,clone是直接内存复制一份,不会重复调用构造方法。2份内存地址不同
WeekReport wr3 = (WeekReport) wr.clone();
// 只要设置变化的参数
wr3.setId(3);
System.out.println(wr3);
}
}
问题1:
WeekReport wr = new WeekReport();
wr.setEmp("zgy");
wr.setSummary("eat");
wr.setPlan("no");
wr.setSuggestion("no");
wr.setTime(new Date());
wr.setId(1);
System.out.println(wr);
WeekReport wr3 = (WeekReport) wr.clone();
wr3.setId(3);
wr3.getTime().setTime(0);
System.out.println(wr);
System.out.println(wr3);
构造方法!
WeekReport{id=1, emp='zgy', summary='eat', plan='no', suggestion='no', time=Fri Jul 09 15:45:54 CST 2021}
WeekReport{id=1, emp='zgy', summary='eat', plan='no', suggestion='no', time=Thu Jan 01 08:00:00 CST 1970}
WeekReport{id=3, emp='zgy', summary='eat', plan='no', suggestion='no', time=Thu Jan 01 08:00:00 CST 1970}
Data.setTime(0),设置毫秒数,自1970.1.1 00:00:00以来的
如上所示,wr3克隆自wr,wr3.getTime返回Data,然后调用setTime实际上是Data.setTime()。
因为这种克隆是浅拷贝,所以引用的对象还是同一个。
修改的是一个new Data();
- 如何解决上面问题1:
@Override
public Object clone() throws CloneNotSupportedException { // 修饰符要比父类宽松,父类是protected
// 克隆的是整个WeekReport,上图的长条部分
WeekReport clone_weekReport = (WeekReport)super.clone();
// 克隆的是new Data()部分
Date cloneData = (Date) clone_weekReport.getTime().clone();
// 修改克隆的WeekReport,把其中setTime部分引用到新克隆的Data,即图中300(Data的地址)
clone_weekReport.setTime(cloneData);
return clone;
}
上述方式是深拷贝
问题2:
- 上面方式虽然能够解决浅拷贝的问题,但是如果WeekReport中有XXX属性,而XXX属性又有别的属性,如此嵌套。就需要clone很多,不好。
- 使用序列化。
WeekReport implements Serializable
@Override
public Object clone() throws CloneNotSupportedException { // 修饰符要比父类宽松,父类是protected
try {
OutputStream out = new FileOutputStream("./a.txt");
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(this); // 序列化时候,对象的所有属性层级关系会被序列化自动处理
oos.close();
InputStream in = new FileInputStream("./a.txt");
ObjectInputStream ois = new ObjectInputStream(in);
Object clone = ois.readObject();
ois.close();
return clone;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
问题3:
- 写在硬盘上不好跨平台,最好不要操作硬盘(虽然可以相对路径)。
- 写入内存
总结
- 用原型实例指定创建对象的种类,通过拷贝这些原型创建的对象来代替new Object();
- java创建对象四种方式:1. new构造 2. 反射 3. 克隆 4. 反序列化