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


    用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。

    • 抽象原型类:规定了具体原型对象必须实现的clone()方法。JDK已经提供了java.lang.Cloneable抽象原型类。

    • 具体原型:实现抽象原型类的clone()方法,它是被复制的对象。

    • 访问类:使用具体原型类的clone()方法来复制新的对象。

    • 浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,扔指向原有属性锁指向的对象的内存地址。如JDK中的Object提供的clone()方法。

    • 深克隆:创建一个新对象,属性中应用的其他对象也会被克隆,不再指向原有对象的地址。

    public class Object {
    	protected native Object clone() throws CloneNotSupportedException;
    }
    
    public interface Cloneable {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    使用new关键字创建对象会调用构造方法,使用clone()方法克隆对象并不会调用构造方法。

    什么场景使用原型模式

    • 对象的创建非常复杂,可以使用原型模式快捷创建对象
    • 对性能和安全要求比较高。可以按照我们自己的逻辑来创建对象。

    浅克隆实现方式

    方式一:java.lang.Cloneable接口

    @Data
    @RequiredArgsConstructor
    @AllArgsConstructor
    public class Attribute {
        private int age;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    实现JDK自带的java.lang.Cloneable接口。

    @Data
    public class Sheep implements Cloneable {
        private String name;
        private Attribute attribute;
    
    
        public Sheep() {
            System.out.println("Sheep构造函数");
        }
    
        @Override
        protected Sheep clone() throws CloneNotSupportedException {
            System.out.println("开始克隆羊");
            return (Sheep)super.clone();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    public class Client {
        public static void main(String[] args) throws Exception {
            Attribute attribute = new Attribute(2);
    
            Sheep prototype = new Sheep();
            prototype.setName("原型羊");
            prototype.setAttribute(attribute);
    
            Sheep copy = prototype.clone();
            copy.setName("多莉(Dolly)");
            attribute.setAge(3);
    
            // 原型羊:3
            System.out.println(prototype.getName() +":"+prototype.getAttribute().getAge());
            // 多莉(Dolly):3
            System.out.println(copy.getName()+ ":" + copy.getAttribute().getAge());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    java.lang.Cloneable#clone()方法就是浅拷贝,所以当拷贝对象的时候只是拷贝对象的内存地址,所以一旦修改原型中的引用对象的属性,那么拷贝出来的对象也会跟着改变。

    方式二:BeanUtils.copyProperties(Object source, Object target)

    注意事项:

    • BeanUtils.copyProperties() 属于浅拷贝,不同的是它也能拷贝两个不同类型的对象。浅拷贝注意事项
      • 如果对象都是基本类型那么随便使用。
      • 如果对象中的属性有引用类型,只要不去修改对象属性的字段也没什么问题。
      • target 中的存在的属性,source中一定要有,但是source中可以有多余的属性。
      • target、source中的属性要名字相同,才能被赋值,不然的话需要手动赋值。
      • Spring的BeanUtils的copyProperties方法需要对应的属性有getter/setter和无参构造方法。
    public class Main {
        public static void main(String[] args) {
            Sheep prototype = new Sheep();
            prototype.setName("原型羊");
            prototype.setAttribute(new Attribute(2));
    
            Sheep target = new Sheep();
            BeanUtils.copyProperties(prototype, target);
            target.getAttribute().setAge(3);
    
            System.out.println(prototype.getName() +":"+prototype.getAttribute().getAge());
            System.out.println(target.getName()+ ":" + target.getAttribute().getAge());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    深拷贝实现

    方案一:通过对象序列化实现(每个对象都要实现序列化接口Serializable)。

    @Data
    @RequiredArgsConstructor
    @AllArgsConstructor
    public class Attribute implements Serializable {
        private int age;
    }
    
    @Data
    public class Sheep implements Serializable {
        private String name;
        private Attribute attribute;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    public class Client {
        public static void main(String[] args) throws Exception {
            Attribute attribute = new Attribute(2);
    
            Sheep prototype = new Sheep();
            prototype.setName("原型羊");
            prototype.setAttribute(attribute);
    
            // 对象序列化到文件
            String file = "/Users/mengday/Downloads/oos.txt";
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
            oos.writeObject(prototype);
            oos.close();
    
            // 序列化文件转对象
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
            Sheep copy = (Sheep)ois.readObject();
            copy.getAttribute().setAge(3);
    
            // 原型羊:2
            System.out.println(prototype.getName() +":"+prototype.getAttribute().getAge());
            // 多莉(Dolly):3
            System.out.println(copy.getName()+ ":" + copy.getAttribute().getAge());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
  • 相关阅读:
    Doris安装(一)之docker编译+fe和be的配置与启动
    小主机折腾记17
    Threejs之射线拾取模型
    基于Python的书店销售管理系统
    对你的第一个推荐计划的期望
    JumpServer开源堡垒机与万里安全数据库完成兼容性认证
    Docker启动SpringBoot简单例子
    使用 Docker-compose 搭建lnmp
    Go template详解(上)- 注释、作用域、空格和空行、管道、{{range .}}、{{with .}}(helm进阶语法)
    LeetCode 290. 单词规律
  • 原文地址:https://blog.csdn.net/vbirdbest/article/details/125453139