JAVA设计模式-原型模式

提出问题

我们需要创建大量的 Phone 对象,并且还要对较多 成员变量赋值,而且 值相同

Phone

public class Phone{

    private String name;
    private String cpu;
    private int ram;
    private int rom;
    private float screen;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCpu() {
        return cpu;
    }

    public void setCpu(String cpu) {
        this.cpu = cpu;
    }

    public int getRam() {
        return ram;
    }

    public void setRam(int ram) {
        this.ram = ram;
    }

    public int getRom() {
        return rom;
    }

    public void setRom(int rom) {
        this.rom = rom;
    }

    public float getScreen() {
        return screen;
    }

    public void setScreen(float screen) {
        this.screen = screen;
    }
}

测试类

public class Test {

    public static void main(String[] args) {
        Phone phone=new Phone();
        phone.setName("华为");
        phone.setCpu("海思麒麟");
        phone.setRam(12);
        phone.setRom(256);
        phone.setScreen(6.2f);

        //每次创建成员变量相同的对象,都需要设置成员变量,而且成员变量的值是相同的
        Phone phone2=new Phone();
        phone2.setName("华为");
        phone2.setCpu("海思麒麟");
        phone2.setRam(12);
        phone2.setRom(256);
        phone2.setScreen(6.2f);
    }

}

问题总结

大量创建 成员变量相同 的对象,都需要给成员变量赋值,而且成员变量的值是相同的,非常麻烦

原型模式

根据一个对象,克隆一个新对象,该新对象的成员变量 与 原对象 相同,而且 克隆的方法非常简单

应用场景

  1. 简化创建对象。尤其是频繁创建对象,需要对较多成员变量赋值,而且 成员变量相同
  2. 克隆一个对象,比用 new 创建对象,效率更高。因为 Object 类的 clone 方法是一个本地方法,它直接 操作内存,特别是复制大对象时,性能的差别非常明显

真实应用

spring框架的原型模式

实现

实现 Cloneable 接口

在运行时通知虚拟机可以使用 clone 方法
只有实现了这个接口的类才可以被克隆,否则抛出 CloneNotSupportedException 异常。

重写 Object 类中的 clone() 方法

Object 类中有 clone 方法,作用是返回对象的一个克隆
但是 protected 访问权限,一般的类无法调用,实现类需要将 clone方法修改为 public 类型

案例-原型模式

Phone2

让类实现 Cloneable 接口,并重写 clone() 方法

class Phone2 implements Cloneable{

    private String name;
    private String cpu;
    private int ram;
    private int rom;
    private float screen;

    @Override
    public Phone2 clone() {
        Phone2 ret=null;
        try {
            ret = (Phone2)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return ret;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCpu() {
        return cpu;
    }

    public void setCpu(String cpu) {
        this.cpu = cpu;
    }

    public int getRam() {
        return ram;
    }

    public void setRam(int ram) {
        this.ram = ram;
    }

    public int getRom() {
        return rom;
    }

    public void setRom(int rom) {
        this.rom = rom;
    }

    public float getScreen() {
        return screen;
    }

    public void setScreen(float screen) {
        this.screen = screen;
    }
}

测试类

public class Test2 {

    public static void main(String[] args) {
        Phone2 phone=new Phone2();
        phone.setName("华为");
        phone.setCpu("海思麒麟");
        phone.setRam(12);
        phone.setRom(256);
        phone.setScreen(6.2f);

        //再次创建成员变量相同的对象,可以克隆

        Phone2 phone2=phone.clone();
        System.out.println(phone2.getName());

    }

}

实现原理

克隆对象 不会 调用类的 构造方法。

克隆是通过调用 Object 类的 clone 方法来完成的,它直接 在内存中复制数据,因此不会调用到类的构造方法。

private 构造方法

private 构造方法,也不会影响克隆,因为 克隆 不调用构造方法,所以,单例模式与原型模式是冲突的,在使用时要特别注意。

验证

Phone类

class Phone3 implements Cloneable{

    private String name;
    private String cpu;
    private int ram;
    private int rom;
    private float screen;

    public Phone3() {
        System.out.println("测试克隆调用构造方法!!!");
    }

    @Override
    public Phone3 clone() {
        Phone3 ret=null;
        try {
            ret = (Phone3)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return ret;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCpu() {
        return cpu;
    }

    public void setCpu(String cpu) {
        this.cpu = cpu;
    }

    public int getRam() {
        return ram;
    }

    public void setRam(int ram) {
        this.ram = ram;
    }

    public int getRom() {
        return rom;
    }

    public void setRom(int rom) {
        this.rom = rom;
    }

    public float getScreen() {
        return screen;
    }

    public void setScreen(float screen) {
        this.screen = screen;
    }
}

测试类

public class 原型模式3 {

    public static void main(String[] args) {
        Phone3 phone=new Phone3();
        phone.setName("华为");
        phone.setCpu("海思麒麟");
        phone.setRam(12);
        phone.setRom(256);
        phone.setScreen(6.2f);

        //再次创建成员变量相同的对象,可以克隆

        Phone3 phone2=phone.clone();
        System.out.println(phone2.getName());

    }

}

执行结果:

测试克隆调用构造方法!!!

总结

只有 new 的时候才会调用构造方法,克隆 不会调用构造方法

浅拷贝

Object类的 clone 方法只会 克隆 对象中的 基本数据类型 和 包装类

对于 数组、对象 等都不会拷贝,这就是浅拷贝

如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝

案例

phone类:

class Phone4 implements Cloneable{

    private String name;
    //电话号码
    private List<String> numberList =new ArrayList<>();

    @Override
    public Phone4 clone() {
        Phone4 ret=null;
        try {
            ret = (Phone4)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return ret;
    }
    public void addNumber(String number){
        numberList.add(number);
    }

    public List<String> getNumberList() {
        return numberList;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }


}

测试类

public class Test {

    public static void main(String[] args) {
        Phone4 lileiPhone=new Phone4();
        lileiPhone.setName("李雷的大宝剑");
        //给 李雷的phone 添加电话号码
        lileiPhone.addNumber("137");
        lileiPhone.addNumber("138");
        lileiPhone.addNumber("139");

        //再次创建成员变量相同的对象,可以克隆

        Phone4 lucyPhone=lileiPhone.clone();
        lucyPhone.setName("lucy的手机");
        //给lucy的phone添加电话号码
        lucyPhone.addNumber("110");

        //修改lucy的phone中的name,不会干扰李雷的phone,因为是基本数据类型 或字符串
        System.out.println(lileiPhone.getName());
        //遍历打印李雷的phone的电话号码,居然打印除了110,说明成员变量是对象类型时,是共享的,会互相干扰
        for(String item:lileiPhone.getNumberList()){
            System.out.println(item);
        }

    }

}

执行结果:

李雷的大宝剑
137
138
139
110

总结

根据 李雷的phone,克隆 lucy的phone,修改 lucy的phone的 name (字符串类型),不会干扰 李雷的phone

修改 lucy的phone的 其他对象(除了 基本数据类型、字符串以外),会干扰 李雷的phone

深拷贝

深拷贝可以解决上面的问题,克隆后,还要将 对象类型 的成员变量克隆

注意: 成员 是 基本数据类型、字符串 时,不需要做此处理

案例

phone类:

class Phone5 implements Cloneable{

    private String name;
    //电话号码
    private List<String> numberList =new ArrayList<>();

    @Override
    public Phone5 clone() {
        Phone5 ret=null;
        try {
            ret = (Phone5)super.clone();
            //当成员变量的类型不是 基本数据类型、字符串时,就重新创建对象 或 克隆(数组可直接克隆)
            ret.numberList = new ArrayList<>();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return ret;
    }
    public void addNumber(String number){
        numberList.add(number);
    }

    public List<String> getNumberList() {
        return numberList;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }


}

测试类

public class 原型模式_深拷贝 {

    public static void main(String[] args) {
        Phone5 lileiPhone=new Phone5();
        lileiPhone.setName("李雷的大宝剑");
        //给 李雷的phone 添加电话号码
        lileiPhone.addNumber("137");
        lileiPhone.addNumber("138");
        lileiPhone.addNumber("139");

        //再次创建成员变量相同的对象,可以克隆

        Phone5 lucyPhone=lileiPhone.clone();
        lucyPhone.setName("lucy的手机");
        //给lucy的phone添加电话号码
        lucyPhone.addNumber("110");

        System.out.println("lilei phone=================");
        //修改lucy的phone中的name,不会干扰李雷的phone,因为是基本数据类型 或字符串
        System.out.println(lileiPhone.getName());
        //遍历打印李雷的phone的电话号码,居然打印除了110,说明成员变量是对象类型时,是共享的,会互相干扰
        for(String item:lileiPhone.getNumberList()){
            System.out.println(item);
        }

        System.out.println("lucy phone=================");
        for(String item:lucyPhone.getNumberList()){
            System.out.println(item);
        }

    }

}

执行结果:

lilei phone=================
李雷的大宝剑
137
138
139
lucy phone=================
110

总结

深克隆后,2个对象的 List 就不会互相干扰

List不支持 克隆,只能创建
如果 成员变量 是 数组、自定类(且实现 Cloneable 接口),可 直接克隆


原文出处:/show_1IX2wN13lrjf.html