设计模式十:原型模式

1、原型模式

原型模式就是从一个对象再创建另外一个可定制的对象, 而且不需要知道任何创建的细节。
所谓原型模式, 就是 Java 中的克隆技术, 以某个对象为原型。 复制出新的对象。 显然新的对象具备原型对象的特点, 效率高(避免了重新执行构造过程步骤)

1.1 类创建过程

回顾一下 new一个对象的流程

10.1、类创建过程

  1. 加载类。虚拟机首先检查参数是否能定位到一个类的符号引用,并确认这个类是否已经被加载、解析和初始化过。如果没有,它会尝试加载这个类,这包括查找类的.class文件,并在加载过程中完成类的验证、准备和解析工作。
  2. 分配内存。虚拟机为对象分配内存。分配内存的方式取决于垃圾收集器使用的算法。如果内存是规整的,使用指针碰撞法,即移动指针来分配内存。如果内存不规整,则使用空闲列表法,即维护一个内存可用列表来分配内存。
  3. 设置对象头。虚拟机会设置对象头信息,包括对象的类元信息、哈希码、GC分代年龄等.
  4. 初始化成员变量。虚拟机会将分配到的内存空间初始化为零值。
  5. 执行构造函数。虚拟机会执行构造函数,根据传入的属性值给对象的属性赋值。
  6. 返回对象引用。在栈中创建一个对象引用,并将其指向堆中新创建的对象实例。

而通过拷贝的方式,没有执行构造函数的步骤(如果对象没有其他类的属性,则也不涉及类加载过程),所以轻量级对象(构造函数里面没有复杂的操作)的创建,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()方法进入到AbstractBeanFactorygetBean()方法,最终执行doGetBean()方法

10.2doGetBean

方法中会判断是否为单例还是原型模式

;

进入到createBean()方法,可以看到doCreateBean()方法

在这里插入图片描述

doCreateBean方法中判断如果是单例,则从缓存中删除;如果是原型,则调用createBeanInstance创建实例

在这里插入图片描述

createBeanInstance方法中最后使用类的构造方法创建了实例

在这里插入图片描述


spring中使用原型模式的场景:

当bean中的属性会有数据的时候,在不同位置使用该对象的时候其内部属性的数据不同时,使用单例是不安全的,这个时候使用原型模式

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值