Java设计模式(四)—— 原型模式

目录

一、问题的提出

二、原型模式

三、原型模式具体实现方法

(1)利用构造函数方法

浅复制

深复制

(2)利用Cloneable接口方法

浅复制

深复制

        (3)利用Serializable序列化接口方法


        原型模式是指用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。适合原型模式的情景如下:

  • 程序需要从一个对象出发,得到若干个和其状态相同,并可独立变化其状态的对象时;
  • 对象创建需要独立于它的构造方法和表示时
  • 以原型对象为基础,克隆新的对象,并完善对象实例变量时

一、问题的提出

        在计算机程序开发过程中,有时会遇到为一个类创建多个实例的情况,这些实例内部成员往往完全相同或有细微的差异,而且实力的创建开销比较大或者需要输入较多参数。如果能通过复制一个已创建的对象实例来重复创建多个相同的对象,这就可以大大减少创建对象的开销,这个时候就需要原型模式。 

二、原型模式

        原型模式是指使用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象,原型模式是一种对象创建型模式

原型模式复制功能分为浅复制深复制

(1)浅复制

浅复制(Shallow Copy)是指在复制一个对象时,仅复制对象本身及其所有基本数据类型的成员变量。而对于非基本数据类型的成员变量,则只是将其引用复制过来,因此,原对象和复制后的对象会共享同一个引用对象。因此,如果原对象的引用对象发生改变,复制后的对象也会受到影响

(2)深复制

深复制(Deep Copy)是指在复制一个对象时,不仅复制对象本身及其所有基本数据类型的成员变量,而且会对所有的非基本数据类型的成员变量进行递归复制。因此,原对象和复制后的对象不会共享任何引用对象

三、原型模式具体实现方法

student类:

        student类包含两个基本数据类型name、age、一个引用类型变量add。

package shejimoshi.yuanxing;

public class Student {
    String name;
    int age;
    Address add;

    public Student(String name, int age, Address add) {
        this.name = name;
        this.age = age;
        this.add = add;  //籍贯
    }

    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 Address getAdd() {
        return add;
    }

    public void setAdd(Address add) {
        this.add = add;
    }

   
}

Address类:

package shejimoshi.yuanxing;

class Address{
        String pro;  //出生省
        String city; //出生市
        String zip;  //出生地邮编

        public Address(String pro, String city, String zip) {
            this.pro = pro;
            this.city = city;
            this.zip = zip;
        }

        public String getPro() {
            return pro;
        }

        public void setPro(String pro) {
            this.pro = pro;
        }

        public String getCity() {
            return city;
        }

        public void setCity(String city) {
            this.city = city;
        }

        public String getZip() {
            return zip;
        }

        public void setZip(String zip) {
            this.zip = zip;
        }
    }

原型复制常用方法有三种:利用构造函数方法、利用Cloneable接口方法、利用Serializable序列化接口方法

(1)利用构造函数方法

浅复制

所需类代码如下所示:

这里写了一个构造方法,传入一个Student对象进行复制。利用已经创建好的学生对象s,构建复制对象copy_s。

package shejimoshi.yuanxing;

public class Student {
    String name;
    int age;
    Address add;

    public Student(String name, int age, Address add) {
        this.name = name;
        this.age = age;
        this.add = add;  //籍贯
    }
    Student(Student s) {
        name = s.getName();
        age = s.getAge();
        add = s.getAdd();  //引用地址拷贝
    }

}

测试类:

public class Test {
    public static void main(String[] args) {
        Address address = new Address("辽宁","大连","1618020202");
        Student s = new Student("zhangyin",20,address);
        //在这里复制出一个对象
        Student copy_s = new Student(s);
        System.out.println(copy_s.getName());
        System.out.println(copy_s.getAge());
        System.out.println(copy_s.getAdd().city);
    }
}

深复制

在深复制中其实就是复制引用类型对象时创建了一个新的

public class Student {
    String name;
    int age;
    Address add;

    public Student(String name, int age, Address add) {
        this.name = name;
        this.age = age;
        this.add = add;  //籍贯
    }
     Student(Student s) {
        name = s.getName();
        age = s.getAge();
        add = new Address(s.getAdd());
    }
}
class Address{
        String pro;  //出生省
        String city; //出生市
        String zip;  //出生地邮编

        public Address(String pro, String city, String zip) {
            this.pro = pro;
            this.city = city;
            this.zip = zip;
        }
        public Address(Address address) {
            pro = address.getPro();
            city = address.getCity();
            zip = address.getZip();
        }
//省略setter getter

    }

(2)利用Cloneable接口方法

        Java类都继承自Object类。事实上,Object类提供了一个clone() 方法,可以将一个Java对象复制一份,因此在Java中可以直接用Object提供clone()方法来实现对象的克隆。但是clone是一个protected的方法,外部类不能直接调用。在此Java规定了对象复制规范:能够实现复制的Java类必须实现一个标识接口Cloneable。

        该接口中没有定义任何方法,因此它仅是起到一个标识作用,表达的语义是:该类用到了对象复制功能,因此抛开本模式而言,空接口有时也是非常有意义的。 

浅复制

public class Student implements Cloneable{
    String name;
    int age;
    Address add;

    public Student(String name, int age, Address add) {
        this.name = name;
        this.age = age;
        this.add = add;  //籍贯
    }
    
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

测试类:

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address address = new Address("辽宁","大连","1618020202");
        Student zy = new Student("zhangyin",20,address);
        //深复制
        Student copy_zy = (Student) zy.clone();
        System.out.println(copy_zy.getName());
        System.out.println(copy_zy.getAge());
        System.out.println(copy_zy.getAdd().city);
    }
}

深复制

public class Student implements Cloneable{
    String name;
    int age;
    Address add;

    public Student(String name, int age, Address add) {
        this.name = name;
        this.age = age;
        this.add = add;  //籍贯
    }

    protected Object clone() throws CloneNotSupportedException {
        Student s = (Student)super.clone();
        s.setAdd((Address) add.clone());
        return s;
    }
}
class Address implements Cloneable{
        String pro;  //出生省
        String city; //出生市
        String zip;  //出生地邮编

        public Address(String pro, String city, String zip) {
            this.pro = pro;
            this.city = city;
            this.zip = zip;
        }
        protected Object clone() throws CloneNotSupportedException{
            return (Address)super.clone();
        }
}

(3)利用Serializable序列化接口方法

        利用构造方法、Cloneable接口方法实现对象深复制都稍显复杂,而利用Serializable序列化接口方法实现深复制要简单的多。Serializable接口同样是一个空接口,表示该对象支持序列化技术。

        这是一个 Java 中的 clone() 方法的实现,它创建了一个与原始对象状态相同的新对象实例。该方法创建了一个新的 ByteArrayOutputStream 和 ObjectOutputStream 对象,用于将原始对象序列化为字节流。然后,使用 ByteArrayInputStream 和 ObjectInputStream 对字节流进行反序列化,并将结果对象作为克隆返回。

        需要注意的是,该实现仅适用于被克隆的类可序列化,这意味着它实现了 java.io.Serializable 接口。如果类不可序列化,则会抛出异常。

        此外,在序列化或反序列化过程中可能发生任何异常,需要进行异常处理。在此实现中,任何异常都会被捕获并使用 printStackTrace() 方法打印到标准错误输出。但这并不是一个好习惯,因为它会使错误诊断变得困难。更好的做法是抛出一个 CloneNotSupportedException 异常,并提供有意义的错误消息。

public class Student implements Cloneable, Serializable {
    String name;
    int age;
    Address add;

    public Student(String name, int age, Address add) {
        this.name = name;
        this.age = age;
        this.add = add;  //籍贯
    }
    protected Object clone() throws CloneNotSupportedException {
        //在这一行,我们创建了一个 obj 对象,用于存储克隆后的对象。
        Object obj = null;
        try {
            /*
            * 这三行代码使用 Java 的序列化机制将当前对象 this 序列化为字节数组并存储在 bos 中。
            * 首先,我们创建了一个 ByteArrayOutputStream 对象 bos,用于存储序列化后的字节数组。
            * 接着,我们创建了一个 ObjectOutputStream 对象 oos,它的作用是将对象序列化为字节流。
            * 最后,我们将当前对象 this 写入 oos 对象中,从而将其序列化为字节数组并存储在 bos 中。
            * */
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
            //从流里读回来
            /*
            * 这三行代码将 bos 中的字节数组反序列化为对象。
            * 我们首先使用 ByteArrayInputStream 对象 bis 将字节数组包装为输入流。
            * 接着,我们使用 ObjectInputStream 对象 ois,它的作用是将字节流反序列化为对象。
            * 最后,我们使用 ois 对象的 readObject() 方法从输入流中读取对象并将其存储在 obj 中。
            * */
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            obj = ois.readObject();
        }catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }
class Address implements Serializable {
        String pro;  //出生省
        String city; //出生市
        String zip;  //出生地邮编

        public Address(String pro, String city, String zip) {
            this.pro = pro;
            this.city = city;
            this.zip = zip;
        }
}

Serializable接口是Java中的一个标记接口,它不包含任何方法或字段,仅仅是用于标识一个类可以被序列化。序列化是指将对象转换为可存储或传输的格式的过程,反序列化则是将这种格式的数据还原成原始的对象。

要利用Serializable接口进行序列化和反序列化,需要完成以下步骤:

  1. 让类实现Serializable接口 需要让需要序列化的类实现Serializable接口,这样就可以将其对象序列化为字节流并传输或存储。

  2. 创建ObjectOutputStream对象 在进行序列化时,需要将对象序列化为字节流并传输或存储,这需要使用ObjectOutputStream对象来实现。创建ObjectOutputStream对象时需要传入一个OutputStream对象,OutputStream对象可以是文件输出流或网络输出流。

  3. 调用ObjectOutputStream的writeObject方法 使用ObjectOutputStream对象的writeObject方法将需要序列化的对象写入到输出流中。

  4. 创建ObjectInputStream对象 在进行反序列化时,需要将字节流还原为原始对象,这需要使用ObjectInputStream对象来实现。创建ObjectInputStream对象时需要传入一个InputStream对象,InputStream对象可以是文件输入流或网络输入流。

  5. 调用ObjectInputStream的readObject方法 使用ObjectInputStream对象的readObject方法将从输入流中读取序列化的对象,并返回原始的对象。

下面是一个使用Serializable接口进行序列化和反序列化的示例代码:

 

import java.io.*;

public class SerializableExample {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 创建一个需要序列化的对象
        Person person = new Person("Tom", 20);

        // 创建ObjectOutputStream对象,将对象序列化到文件中
        FileOutputStream fileOutputStream = new FileOutputStream("person.dat");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        objectOutputStream.writeObject(person);
        objectOutputStream.close();

        // 创建ObjectInputStream对象,将文件中的字节流反序列化为对象
        FileInputStream fileInputStream = new FileInputStream("person.dat");
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
        Person person2 = (Person) objectInputStream.readObject();
        objectInputStream.close();

        // 输出反序列化后的对象信息
        System.out.println(person2.getName()); // Tom
        System.out.println(person2.getAge()); // 20
    }
}

class Person implements Serializable {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小印z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值