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

 

目录

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);
        }
    }

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值