设计模式之—原型模式(深拷贝与浅拷贝)

设计模式之原型模式

1.原型模式概述

原型模式 (Prototype Pattern) 是用于创建重复的对象,同时又能保证性能。属于创建型模式,它提供了一种创建对象的最佳方式。

原型模式使用Java实现比较简单,只需要实现一个接口Cloneable,并重写接口中的 clone() 方法,就完成了原型模式

2.代码实现

2.1实现原型模式

import java.util.List;

/*
 * 为了减少代码量,使用了lombok自动生成代码。
 */

@Data //生成set/get方法
@ToString //重写toString()
@NoArgsConstructor //无参构造器
@AllArgsConstructor //全参构造器


//第一步:实现Cloneable接口
public class Student implements Cloneable {

    private int age;
    private char gender;
    private List<Integer> scores;
	
    /*
     * 第二步,重写clone方法,直接调用父类(Object)的clone()方法。
     * Object内部的clone()方法是native修饰的。
     */
    @Override
    protected Student clone() throws CloneNotSupportedException {
        return (Student) super.clone();
    }
}

2.2测试

public class Test {

    public static void main(String[] args) throws CloneNotSupportedException {
        List<Integer> scores = new ArrayList<>();
        scores.add(1);
        scores.add(2);
        scores.add(3);
		
        //原始对象
        Student student = new Student(14, '男', scores);
		
        //调用clone()方法,生成一个克隆对象
        Student cloneStudent = student.clone();
		
        //两个对象内部的信息完全相同
        System.out.println("原student信息: " + student);
        System.out.println("克隆的student: " + cloneStudent);
        
        
        //这里是false,说明克隆对象与原对象在堆中是两个不同的对象。
        System.out.println("克隆的对象和原对象是否是同一个对象: " + (cloneStudent == student));

        
        
// --- 下面我们修改一下克隆对象内部的一些属性,看一下会不会对原对象产生影响。

        //修改基本数据(int)类型属性 age
        cloneStudent.setAge(3);

        //修改引用类型(List)属性
        List<Integer> cscores = cloneStudent.getScores();
        cscores.add(4);

        System.out.println("修改克隆对象后,原student信息: " + student);
        System.out.println("修改克隆对象后,cloneStudet的信息: " + cloneStudent);
		
        //true
        System.out.println("原对象与克隆对象的scores(List)地址是否相同 : " + (student.getScores() == cloneStudent.getScores()));
    }
}

/*

原student信息: Student(age=14, gender=男, scores=[1, 2, 3])
克隆的student: Student(age=14, gender=男, scores=[1, 2, 3])
克隆的对象和原对象是否是同一个对象: false

//修改基本属性类型对原对象没有影响,但是修改内部的引用类型变量会影响原对象。
修改克隆对象后,原student信息: Student(age=14, gender=男, scores=[1, 2, 3, 4])
修改克隆对象后,cloneStudet的信息: Student(age=3, gender=男, scores=[1, 2, 3, 4])

//两个内部的List属性引用的对象是同一个
原对象与克隆对象的scores(List)地址是否相同 : true
*/

2.3总结

  • 当克隆完成时,实际上是在堆中新创建了一个对象,即克隆对象与原对象在堆中的地址不同。
  • 如果原对象内部有引用类型的变量(如上面的List),那么当进行克隆时,只会将原对象内部的引用类型对象的地址赋值给克隆对象内部的引用变量,即上面的原对象与克隆对象两者的list属性地址相同,而不会真正的在堆中创建一个List出来,这种情况被称为浅拷贝

3.深拷贝与浅拷贝

参考路飞

3.1什么是深拷贝与浅拷贝

  • 数据分为基本数据类型和引用数据类型。基本数据类型:数据直接存储在栈中;引用数据类型:存储在栈中的是对象的引用地址,真实的对象数据存放在堆内存里。
  • 浅拷贝:对于基础数据类型:直接复制数据值;对于引用数据类型:只是复制了对象的引用地址,新旧对象指向同一个内存地址,修改其中一个对象的值,另一个对象的值随之改变
  • 深拷贝:对于基础数据类型:直接复制数据值;对于引用数据类型:开辟新的内存空间,在新的内存空间里复制一个一模一样的对象,新老对象不共享内存,修改其中一个对象的值,不会影响另一个对象
  • 深拷贝相比于浅拷贝速度较慢并且花销较大。

3.2实现深拷贝

浅拷贝通过Cloneable接口实现,深拷贝通过序列化(Serializable)的方式实现。


/*
 * 注意要实现Serializable接口。
 */
public class Student implements Serializable {

    private int age;
    private char gender;
    private List<Integer> scores;

    /*
    *  自定义深拷贝方法。
    * */
    public Student deepClone() {
        try {
            // 输出 (序列化)
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this);
            // 输入 (反序列化)
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            Student Student = (Student) ois.readObject();
            return Student;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

测试

public class Test {

    public static void main(String[] args) throws CloneNotSupportedException {

        List<Integer> scores = new ArrayList<>(Arrays.asList(1, 2, 3));
        Student student = new Student(1, '男', scores);

        Student cloneStudent = student.deepClone();
		
        //false
        System.out.println(student == cloneStudent);
		
        //false 深拷贝成功!!!
        System.out.println(student.getScores() == cloneStudent.getScores());

    }
}

原对象内部的list内存地址与克隆后对象内部的list地址不同,说明深拷贝成功!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

shstart7

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

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

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

打赏作者

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

抵扣说明:

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

余额充值