这是我参与更文挑战的第21天,活动详情查看: 更文挑战
设计模式
原型模式
原型模式其实就是一种克隆。用原型实例指定创建对象的种类,通过克隆这些原型,创建新的对象。
起初我们有一只羊叫多利,颜色为白色,类型为绵羊,后来我们有钱了,需要引进更多属性和多利一样的羊我们怎么写?
Sheep
package com.wangscaler.prototype;
/**
* @author wangscaler
* @date 2021.06.23 17:04
*/
public class Sheep {
private String name;
private String color;
private String type;
public Sheep(String name, String color, String type) {
this.name = name;
this.color = color;
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
复制代码
main
package com.wangscaler.prototype;
/**
* @author wangscaler
* @date 2021.06.23 17:04
*/
public class Prototype {
public static void main(String[] args) {
Sheep sheep = new Sheep("多利", "白色", "绵羊");
Sheep sheep1 = new Sheep(sheep.getName(), sheep.getColor(), sheep.getType());
Sheep sheep2 = new Sheep(sheep.getName(), sheep.getColor(), sheep.getType());
Sheep sheep3 = new Sheep(sheep.getName(), sheep.getColor(), sheep.getType());
Sheep sheep4 = new Sheep(sheep.getName(), sheep.getColor(), sheep.getType());
Sheep sheep5 = new Sheep(sheep.getName(), sheep.getColor(), sheep.getType());
Sheep sheep6 = new Sheep(sheep.getName(), sheep.getColor(), sheep.getType());
}
}
复制代码
我们虽然克隆出了一摸一样的羊,但是我们不仅每次都重新初始化对象,还重新获取原始对象的属性,如果对象比较复杂,会大大影响我们的开发效率。我们发现Object类有个方法叫clone,那么我们能不能通过实现Cloneavle接口的clone来达到我们的目的呢?
Sheep1
package com.wangscaler.prototype;
/**
* @author wangscaler
* @date 2021.06.23 17:04
*/
public class Sheep1 implements Cloneable {
private String name;
private String color;
private String type;
public Sheep1(String name, String color, String type) {
this.name = name;
this.color = color;
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Sheep1 sheep = null;
sheep = (Sheep1) super.clone();
return sheep;
}
}
复制代码
main
package com.wangscaler.prototype;
/**
* @author wangscaler
* @date 2021.06.23 17:04
*/
public class Prototype {
public static void main(String[] args) throws CloneNotSupportedException {
Sheep1 sheep = new Sheep1("多利", "白色", "绵羊");
Sheep1 sheep1 = (Sheep1) sheep.clone();
Sheep1 sheep2 = (Sheep1) sheep.clone();
Sheep1 sheep3 = (Sheep1) sheep.clone();
Sheep1 sheep4 = (Sheep1) sheep.clone();
Sheep1 sheep5 = (Sheep1) sheep.clone();
Sheep1 sheep6 = (Sheep1) sheep.clone();
}
}
复制代码
我们同样复制出了属性相同的羊。
浅拷贝
- 对于基本类型的成员变量,会进行值的传递
- 对于引用类型的变量,会进行引用的传递,即内存地址的传递,此时复制前后的对象其实是同一个内存地址,修改其中一个,另一个也会跟着修改(原因可看我的JAVA易错点1)
- 上述克隆羊中的clone方法就是浅拷贝
深拷贝
- 基本类型会产生值传递
- 引用类型变量会申请存储空间,并复制引用类型变量所引用的对象
实现方式
- 重写clone方法
- 对象序列化
重写clone的方法
package com.wangscaler.prototype;
import java.io.Serializable;
/**
* @author wangscaler
* @date 2021.06.24 10:14
*/
public class DeepClone implements Serializable,Cloneable {
private static final long serialVersionID = 1L;
private String name;
private String aclass;
public DeepClone(String name, String aclass) {
this.name = name;
this.aclass = aclass;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
复制代码
DeeProtoTypeClone
package com.wangscaler.prototype;
import java.io.Serializable;
/**
* @author wangscaler
* @date 2021.06.24 10:14
*/
public class DeeProtoTypeClone implements Serializable, Cloneable {
private String name;
private DeepClone deepClone;
public DeeProtoTypeClone() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public DeepClone getDeepClone() {
return deepClone;
}
public void setDeepClone(DeepClone deepClone) {
this.deepClone = deepClone;
}
@Override
public String toString() {
return "DeeProtoTypeClone{" +
"name='" + name + '\'' +
", deepClone=" + deepClone +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object deep = null;
deep = super.clone();
DeeProtoTypeClone deeProtoTypeClone = (DeeProtoTypeClone) deep;
deeProtoTypeClone.deepClone = (DeepClone) deepClone.clone();
return deeProtoTypeClone;
}
}
复制代码
main
package com.wangscaler.prototype;
/**
* @author wangscaler
* @date 2021.06.23 17:04
*/
public class Prototype {
public static void main(String[] args) throws CloneNotSupportedException {
DeeProtoTypeClone deeProtoTypeClone = new DeeProtoTypeClone();
deeProtoTypeClone.setName("乾隆");
DeepClone deepClone = new DeepClone("纪晓岚", "清官");
deeProtoTypeClone.setDeepClone(deepClone);
DeeProtoTypeClone deeProtoTypeClone1 = (DeeProtoTypeClone) deeProtoTypeClone.clone();
DeeProtoTypeClone deeProtoTypeClone2 = (DeeProtoTypeClone) deeProtoTypeClone.clone();
System.out.println(deeProtoTypeClone.toString());
System.out.println(deeProtoTypeClone1.toString());
System.out.println(deeProtoTypeClone2.toString());
}
}
复制代码
执行结果
DeeProtoTypeClone{name='乾隆', deepClone=com.wangscaler.prototype.DeepClone@1540e19d}
DeeProtoTypeClone{name='乾隆', deepClone=com.wangscaler.prototype.DeepClone@677327b6}
DeeProtoTypeClone{name='乾隆', deepClone=com.wangscaler.prototype.DeepClone@14ae5a5}
复制代码
我们可以发现和浅拷贝不同的是,我们成功的将DeepClone对象复制出了多个
对象序列化
DeeProtoTypeClone添加序列化的方法
package com.wangscaler.prototype;
import java.io.*;
/**
* @author wangscaler
* @date 2021.06.24 10:14
*/
public class DeeProtoTypeClone implements Serializable, Cloneable {
private String name;
private DeepClone deepClone;
public DeeProtoTypeClone() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public DeepClone getDeepClone() {
return deepClone;
}
public void setDeepClone(DeepClone deepClone) {
this.deepClone = deepClone;
}
@Override
public String toString() {
return "DeeProtoTypeClone{" +
"name='" + name + '\'' +
", deepClone=" + deepClone +
'}';
}
// @Override
// protected Object clone() throws CloneNotSupportedException {
// Object deep = null;
// deep = super.clone();
// DeeProtoTypeClone deeProtoTypeClone = (DeeProtoTypeClone) deep;
// deeProtoTypeClone.deepClone = (DeepClone) deepClone.clone();
// return deeProtoTypeClone;
// }
public Object deepClone() {
ObjectInputStream objectInputStream = null;
ObjectOutputStream objectOutputStream = null;
ByteArrayInputStream byteArrayInputStream = null;
ByteArrayOutputStream byteArrayOutputStream = null;
try {
byteArrayOutputStream = new ByteArrayOutputStream();
objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
objectInputStream = new ObjectInputStream(byteArrayInputStream);
DeeProtoTypeClone deeProtoTypeClone = (DeeProtoTypeClone) objectInputStream.readObject();
return deeProtoTypeClone;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
try {
objectInputStream.close();
objectOutputStream.close();
byteArrayInputStream.close();
byteArrayOutputStream.close();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
}
复制代码
也是可以实现的,推荐使用第二种方式。
源码中的原型模式
spring 中bean的创建
<bean id="id01" class="com.wangscaler.bean.Person" scope="prototype"/>
复制代码
我们给他指定为原型模式。当我们通过getBean()获取bean对象时,我们可以发现,创建出来的对象的属性是一样的。
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object bean =applicationContext.getBean("id01");
Object bean1 =applicationContext.getBean("id01");
System.out.println("bean");
System.out.println("bean1");
复制代码
当我们点进去getBean
的源码,我们发现
public Object getBean(String name) throws BeansException {
this.assertBeanFactoryActive();
return this.getBeanFactory().getBean(name);
}
复制代码
这里调用了getBeanFactory这个方法,点进去我们发现
private DefaultListableBeanFactory beanFactory;
public final ConfigurableListableBeanFactory getBeanFactory() {
synchronized(this.beanFactoryMonitor) {
if (this.beanFactory == null) {
throw new IllegalStateException("BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext");
} else {
return this.beanFactory;
}
}
}
复制代码
这里使用了synchronized同步来确保线程安全的返回这个工厂。
我们进到上一步的getBean方法里发现
public Object getBean(String name) throws BeansException {
return this.doGetdBean(name, (Class)null, (Object[])null, false);
}
复制代码
在这里调用了doGetdBean,继续进入
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
String beanName = this.transformedBeanName(name);
Object sharedInstance = this.getSingleton(beanName);
if (mbd.isSingleton()) {
sharedInstance = this.getSingleton(beanName, () -> {
try {
return this.createBean(beanName, mbd, args);
} catch (BeansException var5) {
this.destroySingleton(beanName);
throw var5;
}
});
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if (mbd.isPrototype()) {
var11 = null;
Object prototypeInstance;
try {
this.beforePrototypeCreation(beanName);
prototypeInstance = this.createBean(beanName, mbd, args);
} finally {
this.afterPrototypeCreation(beanName);
}
bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
复制代码
在这里我们发现了先判断是否是单例模式 if (mbd.isSingleton())
然后判断 else if (mbd.isPrototype())
是否是原型模式,如果是的话,会调用createBean创建原型实例。
总结
使用原型模式,可以提高我们的效率,无需初始化对象,而且当对象的属性发生变化时,通过克隆也可以直接复制出变化后的对象。当对象比较复杂时或者我们不知道运行时对象的参数时,推荐使用原型模式。
参考资料
- [JAVA设计模式](