一、實質
- 通過
new
產生一個對象有時需要非常頻繁的數組準備或訪問權限,則可以使用原型模式。就是java
中的克隆技術,以某個對象為原型,復制出新的對象。顯然,新的對象具備原型對象的特點。 - 優勢:效率高(直接克隆,避免了重新執行構造過程步驟)
- 克隆類似于
new
,但是不同于new
。new
創建新的對象屬性采用的是默認值。克隆出的對象的屬性值完全和原型對象相同。并且克隆出的新對象改變不會影響原型對象。然后,再修改克隆對象的值。
二、原型模式的實現
- 實現
Cloneable
接口和覆寫clone
方法 -
Prototype
模式中實現起來最困難的地方就是內存復制操作,所幸在java
中提供了clone()
方法替我們做了絕大部分事情。
三、應用場景
spring
中bean
的創建實際就兩種:單例模式和原型模式(當然原型模式需要和工廠模式搭配起來)
Sheep.java
package cn.itcast.day233.prototype;
import java.util.Date;
//克隆羊多利,實現Cloneable接口,此接口是一個空接口
public class Sheep implements Cloneable {
private String name;
private Date birthday;
public Sheep() {
}
public Sheep(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();//直接使用父類的克隆方法
//添加如下代碼實現深復制
Sheep s = (Sheep) obj;
s.birthday = (Date) this.birthday.clone();//將屬性中的對象(非基本數據類型)也進行拷貝
return obj;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
說明:上面我們給出了淺克隆和深克隆的兩種情況,下面進行測試。
Client.java
package cn.itcast.day233.prototype;
import java.util.Date;
//測試原型模式(淺克隆或者深克隆,兩者有一些區別)
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Date date = new Date(1241341651L);
Sheep s1 = new Sheep("多利", date);
System.out.println(s1);
System.out.println(s1.getName() + "," + s1.getBirthday());
//通過s1克隆一個對象,可以看到這是兩個不同的對象,但是它們屬性的值是相同的,我們可以自己修改
Sheep s2 = (Sheep) s1.clone();
System.out.println(s2);
System.out.println(s2.getName() + "," + s2.getBirthday());
date.setTime(44444444L);
//下面在淺克隆的情況下輸出為:Thu Jan 01 20:20:44 CST 1970,Thu Jan 01 20:20:44 CST 1970
//在深克隆的情況下輸出為:Thu Jan 01 20:20:44 CST 1970,Thu Jan 15 16:49:01 CST 1970
System.out.println(s1.getBirthday() + "," + s2.getBirthday());
}
}
說明:當一個對象中的屬性有其他對象類型時,淺克隆和深克隆就會表現出區別。在測試例子中我們可以看到當改變第一個對象的日期屬性的時候,如果是淺復制,那么原對象和克隆對象的日期屬性都會發生改變,而如果是深復制,那么只會影響原對象,而不會影響克隆對象。上面是通過克隆的方式來達到復制的目的的,下面我們看通過序列化和反序列化實現對象的復制。
Sheep1.java
package cn.itcast.day233.prototype;
import java.io.Serializable;
import java.util.Date;
//克隆羊多利,實現Cloneable接口,此接口是一個空接口
public class Sheep1 implements Cloneable, Serializable {
private String name;
private Date birthday;
public Sheep1() {
}
public Sheep1(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
//此類自己本身沒有實現深克隆
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();//直接使用父類的克隆方法
return obj;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
說明:上述對象本身并沒有實現深復制,下面我們通過序列化和反序列化實現深復制。
Client1.java
package cn.itcast.day233.prototype;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;
//利用序列化和反序列化技術實現深克隆
public class Client1 {
public static void main(String[] args) throws Exception {
Date date = new Date(1241341651L);
Sheep1 s1 = new Sheep1("多利", date);
System.out.println(s1.getName() + "," + s1.getBirthday());
//使用序列化和反序列化實現
//序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(s1);
byte[] bytes = bos.toByteArray();
//反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis);
Sheep1 s2 = (Sheep1) ois.readObject();
System.out.println(s2.getName() + "," + s2.getBirthday());
date.setTime(44444444L);
System.out.println(s1.getBirthday() + "," + s2.getBirthday());
}
}
說明:可以看到和之前的深復制的測試結果是一致的。下面我們測試比較new
和克隆的效率。
Client2.java
package cn.itcast.day233.prototype;
//測試克隆比new的效率高
public class Client2 {
public static void testNew(int size){
long start = System.currentTimeMillis();
for(int i = 0; i < size; i++){
Laptop t = new Laptop();
}
long end = System.currentTimeMillis();
System.out.println("new耗時" + (end - start));
}
public static void testClone(int size) throws Exception{
long start = System.currentTimeMillis();
Laptop t = new Laptop();
Laptop[] arr = new Laptop[size];
for(int i = 0; i < size; i++){
arr[i] = (Laptop) t.clone();
}
long end = System.currentTimeMillis();
System.out.println("clone耗時" + (end - start));
}
public static void main(String[] args) throws Exception {
testClone(1000);
testNew(1000);
}
}
class Laptop implements Cloneable{
public Laptop(){
try {
Thread.sleep(10);//假設一個筆記本對象的創建過程很耗時
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
說明:測試結果為:
clone耗時12
new耗時10483
可以看到差距非常明顯,當然這只是在構造一個對象非常耗時的情況下的結果,如果一個類本身構造就很便捷,那么就不必使用原型模式了。