修炼Java开发技术,学习笔记:原型模式

作为一种另类的对象创建模式,原型模式是指:
使用原型实例来指定创建对象的种类,并通过拷贝这些原型创建新的对象。

  • 使用原型模式创建对象比直接new一个对象在性能上要好的多,因为Object类的clone方法是一个本地方法,它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。
  • 使用原型模式的另一个好处是简化对象的创建,使得创建对象就像我们在编辑文档时的复制粘贴一样简单。
  • 显然,核心是如何在原型类中实现拷贝方法,那么作为核心的原型类,需要满足:
    • 实现Cloneable接口。在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。
    • 重写Object类中的clone方法。Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此,Prototype类需要将clone方法的作用域修改为public类型。

1. 结构

  1. ProtoType抽象原型类:所有具体原型类的父类,可以是接口,抽象类,甚至是实现类;
  2. ConcreteProtoType具体原型类:包含拷贝方法,该拷贝方法需要返回一个拷贝自己的对象;
  3. Client客户类:创建原型对象,调用其拷贝方法。注意:客户端通常针对抽象原型类编程;

2. 拷贝方法的实现:

通用实现方法,和Java语言提供的clone()方法

1.首先来看通用实现方法:

class ConcreteProtoType implements ProtoType{
    private String attr;
    public void setAttr(String attr){
        this.attr = attr;
    }
    public String getAttr(){
        return this.attr;
    }
    //拷贝方法
    public ProtoType clone(){
        ProtoType protoType = new ConcreteProtoType();
        protoType.setAttr(this.attr);
        return protoType;
    }
}
class Client{
    public void init(){
        //面向抽象原型类编程
        ProtoType productA = new ConcreteProtoType();
        productA.setAttr("This is for productB.");
        ProtoType productB = productA.clone();
    }
}

2.java.lang.Object类提供了clone()方法,可以直接使用:

class ConcreteProtoType implements Cloneable,ProtoType{
    public ProtoType clone(){
        Object obj = null;
        try{
            obj = super.clone()
        } catch (CloneNotSupportedException e){
            System.err.println("Cloneable is not supported!");
        }
        return (ProtoType)obj;
    }
}

class Client{
    ProtoType obj1 = new ConcreteProtoType();
    ProtoType obj2 = obj1.clone();
}

java提供的clone()方法产生的对象有如下特点:
1. x.clone()!=x;
2. x.clone().getClass()==x.getClass();
3. x.clone.equals(x)==true;(equals()方法不被重写)

3. 浅拷贝和深拷贝

深拷贝和浅拷贝的区别就在于:对其他对象的引用如何处理。

java中Object类的clone()方法其实做的是“偷懒的”浅拷贝动作,只拷贝对象本身(包括八种基本数据类型和它们的封装类,String类型),不拷贝对象内部的数组,引用对象。

public class Student implements Cloneable{
    private ArrayList<String> al = new ArrayList<String>();
    @Override
    public Student clone(){
        Student student = null;
        try{
            student = (Student) super.clone();
        } catch (CloneNotSupportedException e){
            System.err.println("This cloneable is not supported!");
        }
        return student;
    }
    public void setList(String name){
        this.al.add(name);
    }
    public ArrayList<String> getList(){
        return this.al;
    }
}

public class Client{
    Student studentA = new Student();
    studentA.setList("BJTShang");
    //拷贝对象
    Student studentB = studentA.clone();
    studentB.setList("FETShang");
    System.out.println(studentB.getList());
}

输出:”BJTShang”,”FETShang”
因为调用的super.clone()方法是Object类的,只复制对象,不复制其中的对象引用al!
这种共享私有变量的方法其实是不安全的,实际项目中使用较少。
那么如何进行深拷贝呢?将需要的数组或者引用在clone()方法中手动clone()!

public class Student implements Cloneable{
    private ArrayList<String> al = new ArrayList<String>();
    @Override
    public Student clone(){
        Student student = null;
        try{
            student = (Student) super.clone();
            student.al = (ArrayList<String>)this.al.clone();//将拷贝对象的私有变量也拷贝一份
        } catch (CloneNotSupportedException e){
            System.err.println("This cloneable is not supported!");
        }
        return student;
    }
    public void setList(String name){
        this.al.add(name);
    }
    public ArrayList<String> getList(){
        return this.al;
    }
}

public class Client{
    Student studentA = new Student();
    studentA.setList("BJTShang");
    //拷贝对象
    Student studentB = studentA.clone();
    studentB.setList("FETShang");
    System.out.println(studentB.getList());
}

你可能感兴趣的:(java,技术)