定义:
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
结构:(书中图,侵删)
一个申明克隆自己的接口
若干具体的需要克隆自己的类
这个结构很简单,而且在Java中那个接口是不需要自己写的。
Java类库中有现成的Cloneable接口,这只是一个标记接口,里面没有任何方法,但如果不加这个标记,会抛CloneNotSupportedException异常。
真正的clone()方法继承自Object类,但是必须要重写。
这里的clone()方法是浅克隆。
这里需要先解释一下深克隆和浅克隆的区别:
克隆,顾名思义,就是复制一个一模一样的。
java clone()方法的操作方式就是,开辟一块和当前对象一样的空间,然后把内容原样拷贝过去。
这导致的结果就是:
基础类型(char、int、long等)会是直接的值拷贝过去。
但是像对象这些拷贝过去的就只是引用,导致,所有克隆出来的孩子里的引用类型都指向同一个地方,而不是每人新建了一份。
这就是浅克隆。
深克隆就是把上述指向同一个地方的引用换成了每个人都指向一个新的地方,里面的值依旧是一样的。(下面的例子会使用深克隆)
实例:
一个歌手,他有基本信息:姓名、年龄;
热门歌曲信息:歌名、发行时间。
歌手类:
package designpattern.prototype; public class Singer implements Cloneable { String name; int age; HotMusic hotMusic; public Singer(String name, int age, HotMusic hotMusic) { super(); this.name = name; this.age = age; this.hotMusic = hotMusic; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public HotMusic getHotMusic() { return hotMusic; } public void setHotMusic(HotMusic hotMusic) { this.hotMusic = hotMusic; } @Override protected Object clone() throws CloneNotSupportedException { Singer singerClone = (Singer) super.clone(); singerClone.hotMusic = (HotMusic) hotMusic.clone();// 这一句,下面解释 return singerClone; } @Override public String toString() { return "Singer [name=" + name + ", age=" + age + ", hotMusic=" + hotMusic + "]"; } }
歌曲信息类:
package designpattern.prototype; public class HotMusic implements Cloneable { String name; String date; public HotMusic(String name, String date) { super(); this.name = name; this.date = date; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDate() { return date; } public void setDate(String date) { this.date = date; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "HotMusic [name=" + name + ", date=" + date + "]"; } }
客户端:
package designpattern.prototype; public class Client { public static void main(String[] args) { Singer singer1 = new Singer("周杰伦", 40, new HotMusic("告白气球", "2016")); System.out.println(singer1); try { Singer singer2 = (Singer) singer1.clone(); singer2.hotMusic.setName("等你下课"); singer2.hotMusic.setDate("2018"); System.out.println("========================================================"); System.out.println(singer1); System.out.println(singer2); } catch (CloneNotSupportedException e) { e.printStackTrace(); } } }
输出结果:(深克隆)
Singer [name=周杰伦, age=40, hotMusic=HotMusic [name=告白气球, date=2016]] ======================================================== Singer [name=周杰伦, age=40, hotMusic=HotMusic [name=告白气球, date=2016]] Singer [name=周杰伦, age=40, hotMusic=HotMusic [name=等你下课, date=2018]]
如果把歌手类中注释的那一句去掉,结果会是:(浅克隆)
Singer [name=周杰伦, age=40, hotMusic=HotMusic [name=告白气球, date=2016]] ======================================================== Singer [name=周杰伦, age=40, hotMusic=HotMusic [name=等你下课, date=2018]] Singer [name=周杰伦, age=40, hotMusic=HotMusic [name=等你下课, date=2018]]
总结:
其实之前我从来都没有用过这个方法,之前看到文中说,要创建多个一模一样的对象,直接循环new不就得了?还搞那么麻烦。
看到书中的话对我有很大的启发,我就直接整理一下敲上来了:
每new一次,都要执行一次构造函数,如果构造函数的执行时间很长,那么多次执行会很低效。
一般在初始化的信息不发生变化的情况下,克隆是最好的办法。这既隐藏了对象的创建细节,又对性能是大大的提高。
结合我上面说的,克隆是直接复制内存的,显然比执行构造方法高效的多。