• 设计模式(三)原型模式


    定义:通过一个已经存在的对象,复制出更多的具有与此对象具有相同类型的新的对象。

    Java中的对象复制/克隆分为浅复制和深复制。

    一、浅复制:

    我们知道,一个类的定义中包括属性和方法。属性用于表示对象的状态,方法用于表示对象所具有的行为。其中,属性既可以是Java中基本数据类型,也可以是引用类型。Java中的浅复制通常使用clone()方式完成。

    当进浅复制时,clone函数返回的是一个引用,指向的是新的clone出来的对象,此对象与原对象分别占用不同的堆空间。同时,复制出来的对象具有与原对象一致的状态。

    此处对象一致的状态是指:复制出的对象与原对象中的属性值完全相等==。

    下面以复制一本书为例:

    1.定义Book类和Author类

    Author.java

    class Author {
    
        private String name;
        private int age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
    }
    View Code

    Book.java

    class Book implements Cloneable {
    
        private String title;
        private int pageNum;
        private Author author;
    
        public Book clone() {
            Book book = null;
            try {
                book = (Book) super.clone();
            } catch (CloneNotSupportedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return book;
        }
    
        public String getTitle() {
            return title;
        }
    
        public void setTitle(String title) {
            this.title = title;
        }
    
        public int getPageNum() {
            return pageNum;
        }
    
        public void setPageNum(int pageNum) {
            this.pageNum = pageNum;
        }
    
        public Author getAuthor() {
            return author;
        }
    
        public void setAuthor(Author author) {
            this.author = author;
        }
    
    }
    View Code

    2.测试:

    package com.qqyumidi;
    
    public class PrototypeTest {
    
        public static void main(String[] args) {
            Book book1 = new Book();
            Author author = new Author();
            author.setName("corn");
            author.setAge(100);
            book1.setAuthor(author);
            book1.setTitle("好记性不如烂博客");
            book1.setPageNum(230);
    
            Book book2 = book1.clone();
            
            System.out.println(book1 == book2);  // false
            System.out.println(book1.getPageNum() == book2.getPageNum());   // true
            System.out.println(book1.getTitle() == book2.getTitle());        // true
            System.out.println(book1.getAuthor() == book2.getAuthor());        // true
            
        }
    }

    由输出的结果可以验证说到的结论。由此我们发现:虽然复制出来的对象重新在堆上开辟了内存空间,但是,对象中各属性确保持相等。对于基本数据类型很好理解,但对于引用数据类型来说,则意味着此引用类型的属性所指向的对象本身是相同的, 并没有重新开辟内存空间存储。换句话说,引用类型的属性所指向的对象并没有复制。

    由此,我们将其称之为浅复制。当复制后的对象的引用类型的属性所指向的对象也重新得以复制,此时,称之为深复制。

     

    二、深复制:

     Java中的深复制一般是通过对象的序列化和反序列化得以实现。序列化时,需要实现Serializable接口。

    下面还是以Book为例,看下深复制的一般实现过程:

    1.定义Book类和Author类(注意:不仅Book类需要实现Serializable接口,Author同样也需要实现Serializable接口!!

    Author.java

    class Author implements Serializable{
    
        private String name;
        private int age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
    }
    View Code

    Book.java

    class Book implements Serializable {
    
        private String title;
        private int pageNum;
        private Author author;
    
        public Book deepClone() throws IOException, ClassNotFoundException{
            // 写入当前对象的二进制流 
            ByteArrayOutputStream bos = new ByteArrayOutputStream();  
            ObjectOutputStream oos = new ObjectOutputStream(bos);  
            oos.writeObject(this);
            
            // 读出二进制流产生的新对象  
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());  
            ObjectInputStream ois = new ObjectInputStream(bis);  
            return (Book) ois.readObject();
        }
        
        public String getTitle() {
            return title;
        }
    
        public void setTitle(String title) {
            this.title = title;
        }
    
        public int getPageNum() {
            return pageNum;
        }
    
        public void setPageNum(int pageNum) {
            this.pageNum = pageNum;
        }
    
        public Author getAuthor() {
            return author;
        }
    
        public void setAuthor(Author author) {
            this.author = author;
        }
    
    }
    View Code

    2.测试:

    public class PrototypeTest {
    
        public static void main(String[] args) throws ClassNotFoundException, IOException {
            Book book1 = new Book();
            Author author = new Author();
            author.setName("corn");
            author.setAge(100);
            book1.setAuthor(author);
            book1.setTitle("好记性不如烂博客");
            book1.setPageNum(230);
    
            Book book2 = book1.deepClone();
            
            System.out.println(book1 == book2);  // false
            System.out.println(book1.getPageNum() == book2.getPageNum());   // true
            System.out.println(book1.getTitle() == book2.getTitle());        // false
            System.out.println(book1.getAuthor() == book2.getAuthor());        // false
            
        }
    }

    从输出结果中可以看出,深复制不仅在堆内存上开辟了空间以存储复制出的对象,甚至连对象中的引用类型的属性所指向的对象也得以复制,重新开辟了堆空间存储。

     

    三.原型模式的使用场景

    • 如果类的初始化需要耗费较多的资源,那么可以通过原型拷贝避免这些消耗。
    • 通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
    • 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以拷贝多个对象供调用者使用,即保护性拷贝。

    四.原型模式的优缺点

    优点:

    原型模式是在内存中二进制流的拷贝,要比new一个对象的性能要好,特别是需要产生大量对象时。

    缺点:

    直接在内存中拷贝,构造函数是不会执行的,这样就减少了约束,这既是优点也是缺点,需要在实际应用中去考量。

  • 相关阅读:
    关于idea的目录, mybatis里mapper无法用resource获取 和 驼峰命令规则
    直接调用类方法 和 new再调用方法 的区别
    腾讯笔试题
    linux安装包
    centos 学习笔记一
    putty链接l虚拟机linux centos
    单链表的一般处理(C语言)
    华为2011机试题
    【转】函数返回类型为指针类型时的一些问题
    在 Windows Server 2012 上安装 dotNET Framework v3.5
  • 原文地址:https://www.cnblogs.com/ganchuanpu/p/6680693.html
Copyright © 2020-2023  润新知