欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Prototype 设计模式-原型模式-创建型

程序员文章站 2022-03-10 17:13:25
介绍了Prototype原型模式设计模式的实现方式和关键点。深拷贝与浅拷贝问题如果不不注意,容易导致程序问题。最后给出了ArrayList中实现原型模式的方法。...


目录

1、基本实现方式与代码

2、关键

3、源码中的应用


1、基本实现方式与代码

原型模式的目的是快速复制对象,二进制流的复制要快于对象new创建。

如果想让一个类支持原型模式,需要:

  • 实现标记型接口Cloneable(接口中没有定义任何抽象方法和属性,所以叫标记型)。
  • 重写clone()方法

如果只是重写clone()方法而没有实现接口,调用时会报异常。

下面是一个基本代码实现:

package creational.prototype.example1;

public class Person implements Cloneable {
    int age = 8;
    int score = 100;

    Location loc = new Location("bj", 22);
    @Override
    public Object clone() throws CloneNotSupportedException {
        Person p = (Person)super.clone();
        p.loc = (Location)loc.clone();//支持Person对象深克隆
        return p;
    }
}
package creational.prototype.example1;

public class Location implements Cloneable {
    String street;
    int roomNo;

    @Override
    public String toString() {
        return "Location{" +
                "street.hashCode='" + street.hashCode() + '\'' +
                ", roomNo=" + roomNo +
                '}'+super.toString();
    }

    public Location(String street, int roomNo) {
        this.street = new String("123");
        this.roomNo = roomNo;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
} 
package creational.prototype.example1;


public class Test {
    public static void main(String[] args) throws Exception {
        Person p1 = new Person();
        Person p2 = (Person)p1.clone();
        System.out.println(p1.loc);
        System.out.println(p2.loc);

        System.out.println(p1.loc.street == p2.loc.street);//true,"123"=="123"
        p1.loc.street = "sh";
        System.out.println(p1.loc.street == p2.loc.street);//false,"sh"!="123"
        System.out.println(p1.loc.street);//sh
        System.out.println(p2.loc.street);//123

        //p1.loc.street.replace("sh", "sz");
       // System.out.println(p2.loc.street);
    }
} 

所有代码在这里:

https://github.com/phs999/DesignPatterns/tree/b8797dae753a8602e853fef26555a80196682e18/design_pattern/src/creational/prototype

2、关键

关键点是需要区分深克隆与浅克隆(深拷贝浅拷贝),克隆得到的对象的引用类型属性值B源对象的引用类型属性值A。

我们都了解浅拷贝只拷贝对象的引用(两个人拿着同一房间的两张房卡),深拷贝拷贝的是实际对象的存放空间(两个人拿着两个房间的各自不同的房卡)。

就像上面Person类中的Location loc属性,如果Location类没有实现标记型接口Cloneable和重写clone()方法,则源对象p1和克隆对象p2中的Location loc属性引用值相同,指向的是同一对象所在的内存空间(浅拷贝)。

反之如果Location类实现了实现标记型接口Cloneable和重写clone()方法,则源对象p1和克隆对象p2中的Location loc属性引用值不同,指向的是完全不同的两个内存空间(深拷贝)。

另一个需要注意的点是String,由于String对象值存放在常量池这一特性,我们看到Test类的11-13行,本来String的拷贝只是浅拷贝,即俩对象指向同一内存区域。但之后由于变更了其中一个String street的值,因为String是final类型不可更改,所以变更至相当于重新开辟内存空间并更新street引用值。如此操作之后,两个street的引用指向就不同了。

当然,如果是StringBuilder street,在13行无论怎么改,两个street的引用指向一直是相同的。

如果引用类型的属性类没有实现原型模式的支持(就是上面两点),则克隆得到的对象的引用类型的属性值B与源对象的引用类型的属性值A完全相同,改其中一个的值,另一个的值也改变,因为根本就是一个;反正则引用值不同。

3、源码中的应用

(1)JDK的ArrayList

该类实现了Cloneable接口,同时重写了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);
        }
    }

本文地址:https://blog.csdn.net/phs999/article/details/108212420