文章目录
- 1、原型模式
- 1.1 类创建过程
- 1.2 浅拷贝
- 1.3 深拷贝
- 2、示例
- 2.1 简单形式
- 2.2 复杂形式
- 3、spring中的原型模式
- 3.1 ArrayList的原型模式
- 3.2 spring中的原型模式
1、原型模式
原型模式就是从
一个对象再创建另外一个可定制的对象
, 而且不需要知道任何创建的细节。
所谓原型模式, 就是 Java 中的克隆技术
, 以某个对象为原型。 复制出新的对象。 显然新的对象具备原型对象的特点, 效率高(避免了重新执行构造过程步骤)
1.1 类创建过程
回顾一下 new
一个对象的流程
- 加载类。虚拟机首先检查参数是否能定位到一个类的符号引用,并确认这个类是否已经被加载、解析和初始化过。如果没有,它会尝试加载这个类,这包括查找类的.class文件,并在加载过程中完成类的验证、准备和解析工作。
- 分配内存。虚拟机为对象分配内存。分配内存的方式取决于垃圾收集器使用的算法。如果内存是规整的,使用指针碰撞法,即移动指针来分配内存。如果内存不规整,则使用空闲列表法,即维护一个内存可用列表来分配内存。
- 设置对象头。虚拟机会设置对象头信息,包括对象的类元信息、哈希码、GC分代年龄等.
- 初始化成员变量。虚拟机会将分配到的内存空间初始化为零值。
- 执行构造函数。虚拟机会执行构造函数,根据传入的属性值给对象的属性赋值。
- 返回对象引用。在栈中创建一个对象引用,并将其指向堆中新创建的对象实例。
而通过拷贝的方式,没有执行构造函数的步骤(如果对象没有其他类的属性,则也不涉及类加载过程),所以轻量级对象(构造函数里面没有复杂的操作)的创建,new创建会比较快;而如果构造函数中有一些简单的操作,深拷贝完胜new。
原型模式有简单形式(浅拷贝)和复杂形式(深拷贝)
1.2 浅拷贝
浅拷贝仅仅是返回对象的引用,两变量使用同一堆内存中的对象实例
1.3 深拷贝
深拷贝需要实现**Cloneable
接口,并重写clone
**方法,会分配堆存,并进行数据的拷贝,两个变量指向的是不同的堆内对象实例
注:深拷贝仅对该对象实例进行了拷贝,使得两个变量执行不同的堆内对象实例,但是其内部的引用对象使用的仍是浅拷贝,若需要其饮用对象属性也需要进行深拷贝,则在clone()
方法内部进行操作
2、示例
UserService
内部有一个 orderService
引用对象属性,对UserService
采用原型模式创建对象
2.1 简单形式
public class OrderService {
String value = "sxf";
}
public class UserService {
public OrderService orderService;
public UserService(OrderService orderService) {
this.orderService = orderService;
}
public void test(){
System.out.println(orderService);
}
public Object clone(){
return this;
}
}
//测试类
public class Test {
public static void main(String[] args) {
OrderService orderService = new OrderService();
UserService userService1 = new UserService(orderService);
System.out.println("userService1: " + userService1);
System.out.println("userService1.orderService: " + userService1.orderService);
UserService userService2 = (UserService) userService1.clone();
System.out.println("userService2: " + userService2);
System.out.println("userService2.orderService: " + userService2.orderService);
}
}
输出结果:
userService1: com.designPattern.prototype.simple.UserService@c355be
userService1.orderService: com.designPattern.prototype.simple.OrderService@8cf4c6
userService2: com.designPattern.prototype.simple.UserService@c355be
userService2.orderService: com.designPattern.prototype.simple.OrderService@8cf4c6
简单的原型模式,返回的仅是原型对象的引用,当
userService2
改变时,会相应的改动userService1
2.2 复杂形式
public class OrderService{
String value = "sxf";
}
//实现Cloneable接口,并重写Object的clone方法
public class UserService implements Cloneable{
public OrderService orderService;
public UserService(OrderService orderService) {
this.orderService = orderService;
}
public void test(){
System.out.println(orderService);
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
//测试类
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
OrderService orderService = new OrderService();
UserService userService1 = new UserService(orderService);
System.out.println("userService1: " + userService1);
System.out.println("userService1.orderService: " + userService1.orderService);
UserService userService2 = (UserService) userService1.clone();
System.out.println("userService2: " + userService2);
System.out.println("userService2.orderService: " + userService2.orderService);
}
}
输出结果:
userService1: com.designPattern.prototype.complex.UserService@c355be
userService1.orderService: com.designPattern.prototype.complex.OrderService@8cf4c6
userService2: com.designPattern.prototype.complex.UserService@edcd21
userService2.orderService: com.designPattern.prototype.complex.OrderService@8cf4c6
可以发现,该方式会对对象进行深拷贝
但是其内部的引用对象仍然相同,所以深拷贝要考虑深度的问题,比如,我们可以在对
orderService
进行深拷贝,但是如果orderService
中也有引用类型,需不需要在进行深拷贝,这个是要考虑的
对内部的引用类型进行拷贝:
public class OrderService implements Cloneable{
String value = "sxf";
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class UserService implements Cloneable{
public OrderService orderService;
public UserService(OrderService orderService) {
this.orderService = orderService;
}
public void test(){
System.out.println(orderService);
}
@Override
protected Object clone() throws CloneNotSupportedException {
UserService userService = (UserService) super.clone();
userService.orderService = (OrderService) userService.orderService.clone();
return userService;
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
OrderService orderService = new OrderService();
UserService userService1 = new UserService(orderService);
System.out.println("userService1: " + userService1);
System.out.println("userService1.orderService: " + userService1.orderService);
UserService userService2 = (UserService) userService1.clone();
System.out.println("userService2: " + userService2);
System.out.println("userService2.orderService: " + userService2.orderService);
}
}
执行结果:
userService1: com.designPattern.prototype.complex.UserService@c355be
userService1.orderService: com.designPattern.prototype.complex.OrderService@8cf4c6
userService2: com.designPattern.prototype.complex.UserService@edcd21
userService2.orderService: com.designPattern.prototype.complex.OrderService@c45dca
3、spring中的原型模式
3.1 ArrayList的原型模式
java中ArrayList
重写了clone()
方法
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
虽然其进行了深拷贝,但是仅仅是对基本类型数据的深拷贝,其针对引用类型的数据,仍然是浅拷贝
3.2 spring中的原型模式
我们知道一般spring中的bean
都是单例,但是我们可以将bean设置为原型模式(以前傻傻地叫这个为多例模式…)
@Service
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) // 或者@Scope("prototype")
public class UserService{}
当spring启动后,当需要一个bean的时候,从IOC容器中查找出来,判断是单例还是原型,如果是原型模式,则生成一个对象实例,而不是将IOC容器管理的对象实例返回给用户,创建实例的方式为调用类的构造方法
SpringContextUtil类,方便执行getBean()方法
@Component
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static Object getBean(String beanName) {
return applicationContext.getBean(beanName);
}
public static Object getBean(Class c) {
return applicationContext.getBean(c);
}
}
通过getBean()
方法进入到AbstractBeanFactory
的getBean()
方法,最终执行doGetBean()
方法
方法中会判断是否为单例还是原型模式
进入到createBean()
方法,可以看到doCreateBean()
方法
doCreateBean
方法中判断如果是单例,则从缓存中删除;如果是原型,则调用createBeanInstance
创建实例
createBeanInstance
方法中最后使用类的构造方法创建了实例
spring中使用原型模式的场景:
当bean中的属性会有数据的时候,在不同位置使用该对象的时候其内部属性的数据不同时,使用单例是不安全的,这个时候使用原型模式