原型设计模式

原型模式

概念

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

实现

原型模式的克隆分为浅克隆和深克隆。

浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。

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

Java中已经提供了clone()的方法,他就是一个浅克隆,如果我们写的类,需要使用原型模式,只需要实现Cloneable接口即可。

但是对于String类型来说,String类型有不可变的特性,所以当你改变了克隆后的值后,其实是新建了一个新的对象,所以对原来的对象没有任何影响,所以下例中的username才会被克隆成功。

浅克隆的类

public class ShallowClone implements Cloneable{

    private String username;
    private int age;

    public ShallowClone() {
        System.out.println("无参构造方法");
    }

    public ShallowClone(String username, int age) {
        System.out.println("全参构造方法");
        this.username = username;
        this.age = age;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    protected ShallowClone clone() throws CloneNotSupportedException {
        System.out.println("克隆方法");
        return (ShallowClone) super.clone();
    }

    @Override
    public String toString() {
        return "ShallowClone{" +
                "username='" + username + '\'' +
                ", age=" + age +
                '}';
    }
}

测试代码

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        ShallowClone clone1 = new ShallowClone("张三", 12);
        ShallowClone clone2 = clone1.clone();
        clone2.setUsername("李四");
        clone2.setAge(18);

        System.out.println("clone1 = " + clone1);
        System.out.println("clone2 = " + clone2);
    }
}

image-20230625162304042

可以看到,调用clone方法的时候,他底层不是采用构造方法去新建一个对象的,也就是new的方式,他是直接给内存分配一个大小相同的空间,然后采用直接赋值的方式。

使用场景

  • 对象的创建非常复杂,可以使用原型模式快捷的创建对象。

  • 性能和安全要求比较高。

深克隆

浅克隆中的问题

我们先来看看浅克隆中存在的问题。

这里我们让原来的那个类中的姓名和年龄变成学生类,改造如下:

原型类

public class ShallowClone implements Cloneable{
    private Student student;

    public void setStudent(Student student) {
        this.student = student;
    }

    public Student getStudent() {
        return student;
    }

    @Override
    public String toString() {
        return "ShallowClone{" +
                "student=" + student +
                '}';
    }

    @Override
    protected ShallowClone clone() throws CloneNotSupportedException {
        return (ShallowClone) super.clone();
    }
}

学生类

public class Student {
    private String username;
    private int age;

    public void setUsername(String username) {
        this.username = username;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "username='" + username + '\'' +
                ", age=" + age +
                '}';
    }
}

测试方法

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        ShallowClone clone = new ShallowClone();
        Student student = new Student();
        student.setUsername("张三");
        student.setAge(12);
        clone.setStudent(student);

        ShallowClone clone1 = clone.clone();
        clone1.getStudent().setUsername("李四");
        clone1.getStudent().setAge(18);

        System.out.println("clone = " + clone);
        System.out.println("clone1 = " + clone1);
        System.out.println(clone.getStudent() == clone1.getStudent());
    }
}

输出结果如下:

image-20230625165653775

发现student学生类并没有clone成功,修改的还是同一个学生类,这就是浅克隆存在的问题。

实现深克隆方式一

多层嵌套clone方法即可。也就是在原型类的clone方法中,把里面的非基本数据类型也clone一次,然后复制即可。

原型类

public class DeepClone implements Cloneable{
    private Student student;

    public void setStudent(Student student) {
        this.student = student;
    }

    public Student getStudent() {
        return student;
    }

    @Override
    public String toString() {
        return "ShallowClone{" +
                "student=" + student +
                '}';
    }

    @Override
    protected DeepClone clone() throws CloneNotSupportedException {
        //注意这里,把非基本类型的学生类clone了一次,并赋值在新的clone对象中
        Student clone = student.clone();
        DeepClone deepClone = (DeepClone) super.clone();
        deepClone.student = clone;
        return deepClone;
    }
}

学生类

public class Student implements Cloneable{
    private String username;
    private int age;

    public void setUsername(String username) {
        this.username = username;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "username='" + username + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    protected Student clone() throws CloneNotSupportedException {
        //原型类中的非基本数据类型也需要重写clone方法
        return (Student) super.clone();
    }
}

测试方法和上面的一样,不需要做任何的修改。结果输出如下:

image-20230625170647395

可以看到,深克隆成功了,但是我们需要去手动维护这个clone方法。

实现深克隆方式二

还记得如何破坏单例模式吗,序列化和反序列化是其中的一种方式,所以我们只需要利用这一个特点,把对象写入文件中,然后再读取出来,那么就可以完成一次完美的深克隆啦。

原型类

public class DeepClone implements Cloneable, Serializable {
    private Student student;

    public void setStudent(Student student) {
        this.student = student;
    }

    public Student getStudent() {
        return student;
    }

    @Override
    public String toString() {
        return "ShallowClone{" +
                "student=" + student +
                '}';
    }

    @Override
    protected DeepClone clone() throws CloneNotSupportedException {
        DeepClone deepClone = (DeepClone) super.clone();
        ObjectOutputStream oos = null;
        ObjectInputStream ois = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream("F:\\a.txt"));
            oos.writeObject(deepClone);
            oos.close();
            ois = new ObjectInputStream(new FileInputStream("F:\\a.txt"));
            DeepClone clone = (DeepClone) ois.readObject();
            ois.close();
            return clone;
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}

学生类

public class Student implements Serializable {
    private String username;
    private int age;

    public void setUsername(String username) {
        this.username = username;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "username='" + username + '\'' +
                ", age=" + age +
                '}';
    }
}

最后测试输出结果如下:

image-20230625204738198

可以看到,也实现了一次深拷贝。

实现深克隆方法三

调用第三方工具类库,比如Json工具类(先转换为Json字符串,然后再转换为对象)、Spring的BeanUtils(Spring项目中使用比较方便)、Cglib的BeanCopier(速度最快)、Apache的BeanUtils(该工具类比较慢,谨慎使用!)。原理同实现方法二,只不过人家帮你封装好了。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇