当前位置: 移动技术网 > IT编程>软件设计>设计模式 > Prototype 设计模式-原型模式-创建型

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

2020年08月01日  | 移动技术网IT编程  | 我要评论
介绍了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

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网