02 原型模式

02 原型模式

Java中怎么拷贝一个对象呢?

可以通过调用这个对象类型的构造器构造一个新对象,然后将要拷贝对象的属性设置到新对象里面。
1.Java中也有另一种不通过构造器来拷贝对象的方式,这种方式称为克隆。
2.Java提供了java.lang.Cloneable和java.lang.Object中的clone()方法来支持克隆。

使用条件

是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

写法

如果希望一个类拥有克隆的行为,要做一下几步:
1.该类实现java.lang.Cloneable接口。
2.覆盖Object的clone方法,将访问修饰符改为public,修改返回类型。
3.clone方法内部调用父类的clone方法。

/*
* 1.实现cloneable接口
* */
class Graphy implements Cloneable{
    Person writer;
    /*2.重写clone方法,并注意将访问修饰符改为public,修改返回类型。*/
     @Override
    public  Graphy clone() throws CloneNotSupportedException {
         /*3. clone方法内部调用父类的clone方法。父类执行对对象的字段的克隆操作,强转修改 返回类型*/
        return (Graphy)super.clone();
    }
    public Person getWriter() {
        return writer;
    }
    public void setWriter(Person writer) {
        this.writer = writer;
    }
    @Override
    public String toString() {
        return "graphy{" +
                "writer=" + writer +
                '}';
    }
}

# 为啥这么写

## 原理剖析

由于这段源码很简单而且注释写得太好了,先直接粘贴出来,免得写得不确切,影响读者阅读。

```java
package java.lang;
/**
 * A class implements the <code>Cloneable</code> interface to
 * indicate to the {@link java.lang.Object#clone()} method that it
 * is legal for that method to make a
 * field-for-field copy of instances of that class.
 * <p>
 * Invoking Object's clone method on an instance that does not implement the
 * <code>Cloneable</code> interface results in the exception
 * <code>CloneNotSupportedException</code> being thrown.
 * <p>
 * By convention, classes that implement this interface should override
 * <tt>Object.clone</tt> (which is protected) with a public method.
 * See {@link java.lang.Object#clone()} for details on overriding this
 * method.
 * <p>
 * Note that this interface does <i>not</i> contain the <tt>clone</tt> method.
 * Therefore, it is not possible to clone an object merely by virtue of the
 * fact that it implements this interface.  Even if the clone method is invoked
 * reflectively, there is no guarantee that it will succeed.
 *
 * @author  unascribed
 * @see     java.lang.CloneNotSupportedException
 * @see     java.lang.Object#clone()
 * @since   JDK1.0
 */
public interface Cloneable {
}

一个类实现Cloneable接口表示可以有对象的克隆的行为的合法性,如果不实现会抛错CloneNotSupportedException
但这个接口中却没有提供clone方法(Cloneable接口中没有任何方法,看起来更像是一个标记接口。 )
按照注解提示 我们通常的做法是重写Object的clone方法并把其改为public方法。
那么我们来看看Object的clone方法的源码和注释吧

/**
 * Creates and returns a copy of this object.  The precise meaning
 * of "copy" may depend on the class of the object. The general
 * intent is that, for any object {@code x}, the expression:
 * <blockquote>
 * <pre>
 * x.clone() != x</pre></blockquote>
 * will be true, and that the expression:
 * <blockquote>
 * <pre>
 * x.clone().getClass() == x.getClass()</pre></blockquote>
 * will be {@code true}, but these are not absolute requirements.
 * While it is typically the case that:
 * <blockquote>
 * <pre>
 * x.clone().equals(x)</pre></blockquote>
 * will be {@code true}, this is not an absolute requirement.
 * <p>
 * By convention, the returned object should be obtained by calling
 * {@code super.clone}.  If a class and all of its superclasses (except
 * {@code Object}) obey this convention, it will be the case that
 * {@code x.clone().getClass() == x.getClass()}.
 * <p>
 * By convention, the object returned by this method should be independent
 * of this object (which is being cloned).  To achieve this independence,
 * it may be necessary to modify one or more fields of the object returned
 * by {@code super.clone} before returning it.  Typically, this means
 * copying any mutable objects that comprise the internal "deep structure"
 * of the object being cloned and replacing the references to these
 * objects with references to the copies.  If a class contains only
 * primitive fields or references to immutable objects, then it is usually
 * the case that no fields in the object returned by {@code super.clone}
 * need to be modified.
 * <p>
 * The method {@code clone} for class {@code Object} performs a
 * specific cloning operation. First, if the class of this object does
 * not implement the interface {@code Cloneable}, then a
 * {@code CloneNotSupportedException} is thrown. Note that all arrays
 * are considered to implement the interface {@code Cloneable} and that
 * the return type of the {@code clone} method of an array type {@code T[]}
 * is {@code T[]} where T is any reference or primitive type.
 * Otherwise, this method creates a new instance of the class of this
 * object and initializes all its fields with exactly the contents of
 * the corresponding fields of this object, as if by assignment; the
 * contents of the fields are not themselves cloned. Thus, this method
 * performs a "shallow copy" of this object, not a "deep copy" operation.
 * <p>
 * The class {@code Object} does not itself implement the interface
 * {@code Cloneable}, so calling the {@code clone} method on an object
 * whose class is {@code Object} will result in throwing an
 * exception at run time.
 *
 * @return     a clone of this instance.
 * @throws  CloneNotSupportedException  if the object's class does not
 *               support the {@code Cloneable} interface. Subclasses
 *               that override the {@code clone} method can also
 *               throw this exception to indicate that an instance cannot
 *               be cloned.
 * @see java.lang.Cloneable
 */
protected native Object clone() throws CloneNotSupportedException;

创建并返回这个类的克隆,准确的描述是这个类的实例对象的克隆。
流程是 如果这个类的对象已经存在,直接复制一份这个对象,里面的属性原封不动的拷贝过来,这涉及到了地址的变化。
如果一个对象没有实现Cloneable接口,调用它的clone方法会抛出CloneNotSupportedException异常。但数组例外,可以认为数组实现了Cloneable,所以可以直接调用其clone方法获取克隆对象。

深克隆与浅克隆(深拷贝与浅拷贝的区别)

按照Object克隆方法描述中的规范,克隆得到对象应该独立于被克隆对象,从根本上说,它们的内存地址(不严格的说)应该不同。但是如果有这种情况,一个可克隆类中包含其他的引用类型属性,那么克隆后会是什么情况呢?举个例子说明一下:
package xzc._04prototype;

/*
 * 总结:
 * 1.一个类实现cloneable接口
 * 1.1点接口源码进去 发现这是一个 标记型号接口 就是 里面没有任何需要我们实现的接口
 * 1.2这是一个标记型接口 用于声明合法性 不写抛错
 *
 * 2.重写clong方法
 * 2.1 点开源码会看到 这是一个 protected native 修饰的方法(意味着更底层是c++写的方法)
 * 2.2 提示我们使用clong方法 必须写实现Cloneable接口 可以看到所有数组默认已经实现了Cloneable接口可以不用我们去写
 * 2.3 重写Clone方法并在里面用 super.clone()实现,将访问修饰符改成public (非必须)修改返回类型为该类的类型。
 *
 * 3.深拷贝于浅拷贝
 *
 * */
/*
 * 1.实现cloneable接口
 * */
class Graphy implements Cloneable {
    Person writer;
    /*2.重写clone方法,并注意将访问修饰符改为public,修改返回类型。*/
    @Override
    public Graphy clone() throws CloneNotSupportedException {
        /*3. clone方法内部调用父类的clone方法。父类执行对对象的字段的克隆操作,强转修改返回类型*/
        return (Graphy) super.clone();
    }
    public Person getWriter() {
        return writer;
    }
    public void setWriter(Person writer) {
        this.writer = writer;
    }
    @Override
    public String toString() {
        return "graphy{" +
                "writer=" + writer +
                '}';
    }
}
public class prototypeMode {
    public static void main(String[] args) throws CloneNotSupportedException {
        Graphy graphy = new Graphy();
        Person person = new Person();
        Graphy graphy_clone = graphy.clone();
        //graphy和graphy_clone是否指向相同的内存地址
        System.out.println("graphy_clone == graphy");
        System.out.println(graphy_clone == graphy);
        //graphy的writer和graphy_clone的writer是否指向相同的内存地址
        System.out.println("graphy_clone.getWriter() == graphy.getWriter()");
        System.out.println(graphy_clone.getWriter() == graphy.getWriter());
    }
}
class Person {
    String name = null;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "person{" +
                "name='" + name + '\'' +
                '}';
    }
}

执行结果

在这里插入图片描述

可见clone方法默认的行为就是浅克隆。那么也很容易想到,如果克隆一个对象,并递归的克隆其所有的引用类型属性,这样的方式就是深克隆了。

理解了原理我们做如下修改

@Override
public Graphy clone() throws CloneNotSupportedException {
    /*3. clone方法内部调用父类的clone方法。父类执行对对象的字段的克隆操作,强转修改返回类型*/
    Graphy graphy= (Graphy) super.clone();
    Person person= graphy.getWriter().clone();
    graphy.setWriter(person);
    return graphy;
}

设置了下一层的递归克隆

对其属性的类实现克隆接口

class Person implements Cloneable{
    String name = null;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public Person clone() throws CloneNotSupportedException {
        /*3. clone方法内部调用父类的clone方法。父类执行对对象的字段的克隆操作,强转修改返回类型*/
        return (Person)super.clone();
    }
    @Override
    public String toString() {
        return "person{" +
                "name='" + name + '\'' +
                '}';
    }
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值