前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java原型模式

java原型模式

作者头像
码农王同学
发布2020-03-25 11:51:20
2720
发布2020-03-25 11:51:20
举报
文章被收录于专栏:后端Coder后端Coder
代码语言:javascript
复制
文章来源于网络文件

原型模式是用于创建重复的对象,同时又能保证性能,通过复制现有实例来创建新的实例对象,无需知道类的信息。

与通过对一个类进行实例化来构造新对象不同的是原型模式是通过拷贝一个现有实例对象生成新对象的。那么java中是如何实现原型模式的呢?原型模式的本质就是克隆,拷贝一个一模一样的对象。

java中的实现原型模式可以分为两种,一种是浅拷贝,一种是深拷贝。浅拷贝实现原型模式就是实现了一个克隆接口,该接口就是用于创建当前对象的克隆,下面通过代码来实现浅拷贝。

首先定义一个类,这个类实现Cloneable接口里面的clone()方法。

代码语言:javascript
复制
package com.wpw.iteratorpro;

import java.io.Serializable;
import java.util.Arrays;

public class Person implements Cloneable, Serializable {
    String name;
    int age;
    int [] a;

    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;
    }

    public int[] getA() {
        return a;
    }

    public void setA(int[] a) {
        this.a = a;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person person= (Person) super.clone();
        return person;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", a=" + Arrays.toString(a) +
                '}';
    }
}
代码语言:javascript
复制
package com.wpw.iteratorpro;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author pc
 */
public class Test {
    private static final Logger logger=LoggerFactory.getLogger(Test.class);
    public static void main(String[] args) {
        Person person=new Person();
        person.setAge(10);
        person.setName("Jack");
        person.setA(new int[]{1});
       logger.info("克隆前p的值:{}",person);
        try {
            Person p2= (Person) person.clone();
            logger.info("克隆前未修改p2的值:{}",p2);
            p2.setAge(11);
            p2.setName("Tom");
            int[] a = p2.getA();
            a[0]=2;
            p2.setA(a);
            logger.info("克隆后修改p2的值,p2的值为:{}",p2);
            logger.info("克隆后修改p2的值,person的值为:{}",person);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

控制台输出的内容

代码语言:javascript
复制
/*
* 提示:该行代码过长,系统自动注释不进行高亮。一键复制会移除系统注释 
* "C:\Program Files\Java\jdk1.8.0_25\bin\java.exe" -Dvisualvm.id=94464324889000 "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.1.2\lib\idea_rt.jar=4497:C:\Program Files\JetBrains\IntelliJ IDEA 2019.1.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_25\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_25\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_25\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_25\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_25\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_25\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_25\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_25\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_25\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_25\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_25\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_25\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_25\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_25\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_25\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_25\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_25\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_25\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_25\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_25\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_25\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_25\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_25\jre\lib\rt.jar;D:\iterator-pro\target\classes;C:\Users\pc\workspace\repository\repository\org\springframework\boot\spring-boot-starter-web\2.2.4.RELEASE\spring-boot-starter-web-2.2.4.RELEASE.jar;C:\Users\pc\workspace\repository\repository\org\springframework\boot\spring-boot-starter\2.2.4.RELEASE\spring-boot-starter-2.2.4.RELEASE.jar;C:\Users\pc\workspace\repository\repository\org\springframework\boot\spring-boot\2.2.4.RELEASE\spring-boot-2.2.4.RELEASE.jar;C:\Users\pc\workspace\repository\repository\org\springframework\boot\spring-boot-autoconfigure\2.2.4.RELEASE\spring-boot-autoconfigure-2.2.4.RELEASE.jar;C:\Users\pc\workspace\repository\repository\org\springframework\boot\spring-boot-starter-logging\2.2.4.RELEASE\spring-boot-starter-logging-2.2.4.RELEASE.jar;C:\Users\pc\workspace\repository\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;C:\Users\pc\workspace\repository\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;C:\Users\pc\workspace\repository\repository\org\apache\logging\log4j\log4j-to-slf4j\2.12.1\log4j-to-slf4j-2.12.1.jar;C:\Users\pc\workspace\repository\repository\org\apache\logging\log4j\log4j-api\2.12.1\log4j-api-2.12.1.jar;C:\Users\pc\workspace\repository\repository\org\slf4j\jul-to-slf4j\1.7.30\jul-to-slf4j-1.7.30.jar;C:\Users\pc\workspace\repository\repository\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;C:\Users\pc\workspace\repository\repository\org\yaml\snakeyaml\1.25\snakeyaml-1.25.jar;C:\Users\pc\workspace\repository\repository\org\springframework\boot\spring-boot-starter-json\2.2.4.RELEASE\spring-boot-starter-json-2.2.4.RELEASE.jar;C:\Users\pc\workspace\repository\repository\com\fasterxml\jackson\core\jackson-databind\2.10.2\jackson-databind-2.10.2.jar;C:\Users\pc\workspace\repository\repository\com\fasterxml\jackson\core\jackson-annotations\2.10.2\jackson-annotations-2.10.2.jar;C:\Users\pc\workspace\repository\repository\com\fasterxml\jackson\core\jackson-core\2.10.2\jackson-core-2.10.2.jar;C:\Users\pc\workspace\repository\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.10.2\jackson-datatype-jdk8-2.10.2.jar;C:\Users\pc\workspace\repository\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.10.2\jackson-datatype-jsr310-2.10.2.jar;C:\Users\pc\workspace\repository\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.10.2\jackson-module-parameter-names-2.10.2.jar;C:\Users\pc\workspace\repository\repository\org\springframework\boot\spring-boot-starter-tomcat\2.2.4.RELEASE\spring-boot-starter-tomcat-2.2.4.RELEASE.jar;C:\Users\pc\workspace\repository\repository\org\apache\tomcat\embed\tomcat-embed-core\9.0.30\tomcat-embed-core-9.0.30.jar;C:\Users\pc\workspace\repository\repository\org\apache\tomcat\embed\tomcat-embed-el\9.0.30\tomcat-embed-el-9.0.30.jar;C:\Users\pc\workspace\repository\repository\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.30\tomcat-embed-websocket-9.0.30.jar;C:\Users\pc\workspace\repository\repository\org\springframework\boot\spring-boot-starter-validation\2.2.4.RELEASE\spring-boot-starter-validation-2.2.4.RELEASE.jar;C:\Users\pc\workspace\repository\repository\jakarta\validation\jakarta.validation-api\2.0.2\jakarta.validation-api-2.0.2.jar;C:\Users\pc\workspace\repository\repository\org\hibernate\validator\hibernate-validator\6.0.18.Final\hibernate-validator-6.0.18.Final.jar;C:\Users\pc\workspace\repository\repository\org\jboss\logging\jboss-logging\3.4.1.Final\jboss-logging-3.4.1.Final.jar;C:\Users\pc\workspace\repository\repository\com\fasterxml\classmate\1.5.1\classmate-1.5.1.jar;C:\Users\pc\workspace\repository\repository\org\springframework\spring-web\5.2.3.RELEASE\spring-web-5.2.3.RELEASE.jar;C:\Users\pc\workspace\repository\repository\org\springframework\spring-beans\5.2.3.RELEASE\spring-beans-5.2.3.RELEASE.jar;C:\Users\pc\workspace\repository\repository\org\springframework\spring-webmvc\5.2.3.RELEASE\spring-webmvc-5.2.3.RELEASE.jar;C:\Users\pc\workspace\repository\repository\org\springframework\spring-aop\5.2.3.RELEASE\spring-aop-5.2.3.RELEASE.jar;C:\Users\pc\workspace\repository\repository\org\springframework\spring-context\5.2.3.RELEASE\spring-context-5.2.3.RELEASE.jar;C:\Users\pc\workspace\repository\repository\org\springframework\spring-expression\5.2.3.RELEASE\spring-expression-5.2.3.RELEASE.jar;C:\Users\pc\workspace\repository\repository\org\slf4j\slf4j-api\1.7.30\slf4j-api-1.7.30.jar;C:\Users\pc\workspace\repository\repository\org\springframework\spring-core\5.2.3.RELEASE\spring-core-5.2.3.RELEASE.jar;C:\Users\pc\workspace\repository\repository\org\springframework\spring-jcl\5.2.3.RELEASE\spring-jcl-5.2.3.RELEASE.jar" com.wpw.iteratorpro.Test
*/
 [main] INFO com.wpw.iteratorpro.Test - 克隆前p的值:Person{name='Jack', age=10, a=[1]}
 [main] INFO com.wpw.iteratorpro.Test - 克隆前未修改p2的值:Person{name='Jack', age=10, a=[1]}
 [main] INFO com.wpw.iteratorpro.Test - 克隆后修改p2的值,p2的值为:Person{name='Tom', age=11, a=[2]}
 [main] INFO com.wpw.iteratorpro.Test - 克隆后修改p2的值,person的值为:Person{name='Jack', age=10, a=[1]}

通过上面的一段代码,我们发现,修改由person克隆出来的person2的String和int类型的值,person的对应的类型的值并没有发生改变,但是修改引用类型int[]的值,person对应的类型也发生了改变,这就说明,对于基本类型而言,浅拷贝,对值类型的成员变量进行值的复制,对于引用类型的变量进行引用的复制,不复制引用的对象。String是一个特殊的类型,它的数据是放在常量池中的,对person2的修改是将它String类型的引用从"jack"指向了"Tom",而person的String类型的指向依然是"Jack",并没有发生改变。

那么,有很多时候,我并不希望对拷贝对象的修改会影响到原来的对象,这时候我们就需要进行深拷贝了。

深拷贝:对值类型的成员变量进行值拷贝,对引用类型的变量进行引用的复制,并且复制引用的对象。简单点来说,所谓的深拷贝就是对于那些只是拷贝引用而不拷贝对象的属性进行单独拷贝。

第一种方法,将引用类型的对象再单独拷贝一下。

代码语言:javascript
复制
package com.wpw.iteratorpro;

import java.io.Serializable;
import java.util.Arrays;

public class Person implements Cloneable, Serializable {
    String name;
    int age;
    int [] a;

    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;
    }

    public int[] getA() {
        return a;
    }

    public void setA(int[] a) {
        this.a = a;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person person= (Person) super.clone();
        person.a=this.a.clone();
        return person;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", a=" + Arrays.toString(a) +
                '}';
    }
}

直接在clone()方法中将int[]数组类型的引用单独再拷贝一份,即可实现深拷贝。

第二种方法,使用java IO流的方式,将对象序列化为字节序列后,默认会将该对象的整个对象图进行序列化,再通过反序列即可完美地实现深拷贝。

代码语言:javascript
复制
package com.wpw.iteratorpro;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;

/**
 * @author pc
 */
public class Test2 {
    private static final Logger logger=LoggerFactory.getLogger(Test2.class);
    public static void main(String[] args) {
        Person person=new Person();
        person.setAge(10);
        person.setName("Jack");
        person.setA(new int[]{1});
        ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
        try {
            ObjectOutputStream objectOutputStream=new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(person);
            objectOutputStream.flush();
            ObjectInputStream objectInputStream=new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
            Person person2= (Person) objectInputStream.readObject();
            logger.info("深拷贝后修改person2的值:{}",person2);
            person2.setAge(12);
            person2.setName("Tom");
            int[] a = person2.getA();
            a[0]=3;
            person2.setA(a);
            logger.info("深拷贝后修改person2的值:{}",person2);
            logger.info("深拷贝后修改person2的值,person的值为:{}",person);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

我们看到,深拷贝后,修改person2的值并未影响到person的值。需要注意的是,如果有属性transient关键字修饰的话,这个属性是不会序列化的。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-02-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农王同学 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档