设计模式-原型模式

原型模式

该设计模式主要用于对象的复制

此设计模式有多种实现:

  1. jdk内置cloneable接口
  2. 自定义接口

jdk内置cloneable接口

我们都知道object中有一个protected方法,叫clone,可以看到这是一个native方法(即该方法是由C++编写的,具体实现是根据操作系统而定)

1
2
@HotSpotIntrinsicCandidate
protected native Object clone() throws CloneNotSupportedException;

我们来自己重写一个这个方法试一试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class Video {
private String name;
private Date gmtCreated;

public Video(String name, Date gmtCreated) {
this.name = name;
this.gmtCreated = gmtCreated;
}

public Video() {
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Date getGmtCreated() {
return gmtCreated;
}

public void setGmtCreated(Date gmtCreated) {
this.gmtCreated = gmtCreated;
}

@Override
protected Video clone() throws CloneNotSupportedException {
return (Video)super.clone();
}

@Override
public String toString() {
return "Video{" +
"name='" + name + '\'' +
", gmtCreated=" + gmtCreated +
'}';
}
}

主程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) throws CloneNotSupportedException {
Video video = new Video();
video.setName("视频一号");
video.setGmtCreated(new Date());
System.out.println(video);
System.out.println(video.hashCode());
System.out.println(video.getGmtCreated().hashCode());
System.out.println("=============克隆后================");
Video clone = (Video) video.clone();
System.out.println(clone);
System.out.println(clone.hashCode());
System.out.println(clone.getGmtCreated().hashCode());
System.out.println();
}

控制台输出

1
2
3
4
5
6
7
8
Video{name='视频一号', gmtCreated=Fri Feb 25 10:31:04 CST 2022}
269468037
784235907
=============克隆后================
Exception in thread "main" java.lang.CloneNotSupportedException: com.lizhi.demo1.Video
at java.base/java.lang.Object.clone(Native Method)
at com.lizhi.demo1.Video.clone(Video.java:35)
at com.lizhi.demo1.CloneApplication.main(CloneApplication.java:13)

可以发现,报错了,这是怎么回事,我不是已经重写了方法吗?

其实原因就是该类没有一个标记接口Cloneable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* A class implements the <code>Cloneable</code> interface to
* indicate to the {@link java.lang.Object#clone()} method that it
* is legal for that method to make a
* field-for-field copy of instances of that class.
* <p>
* Invoking Object's clone method on an instance that does not implement the
* <code>Cloneable</code> interface results in the exception
* <code>CloneNotSupportedException</code> being thrown.
* <p>
* By convention, classes that implement this interface should override
* {@code Object.clone} (which is protected) with a public method.
* See {@link java.lang.Object#clone()} for details on overriding this
* method.
* <p>
* Note that this interface does <i>not</i> contain the {@code clone} method.
* Therefore, it is not possible to clone an object merely by virtue of the
* fact that it implements this interface. Even if the clone method is invoked
* reflectively, there is no guarantee that it will succeed.
*
* @author unascribed
* @see java.lang.CloneNotSupportedException
* @see java.lang.Object#clone()
* @since 1.0
*/
public interface Cloneable {
}

将video实现该接口后,启动程序,控制台输出

1
2
3
4
5
6
7
Video{name='视频一号', gmtCreated=Fri Feb 25 10:37:07 CST 2022}
269468037
784153335
=============克隆后================
Video{name='视频一号', gmtCreated=Fri Feb 25 10:37:07 CST 2022}
1681595665
784153335

我们可以惊奇的发现,

两者的内容打印出来一模一样,两者的对象主体的hashcode是不同的,说明video和clone是货真价实的两个对象。

但是我们也发现他们的gmtCreated变量的hashcode是一样的,这说明两者指向的是同一个gmtCreated,有时候我们会有这样的需求,有时候我们不需要将复制后的对象内部指向的对象还是原来的那个对象,这时候我们就要修改clone方法

1
2
3
4
5
6
7
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
Video v = (Video) obj;
v.setGmtCreated((Date) v.getGmtCreated().clone());
return obj;
}

自定义接口

1
2
3
public interface Copyable {
Object copy();
}

具体实现就不写啦,本质是一样的。

给作者买杯咖啡吧~~~