Java深拷贝和浅拷贝

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


一、数据类型

基本数据类型: byte,short,int,long,boolean,char,float,double
引用数据类型: 类的实例对象,枚举,接口和数组类型

  1. 基本数据类型的变量,存储的就是数据本身的值;
  2. 引用数据类型的变量,存储的则是数据的地址,这个地址指向了数据的值。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获取实体。
  3. 基本数据类型为值传递,引用数据类型为引用传递

二、赋值与浅拷贝

1. 一个由于赋值引发的错误

public void consume(Param param) {
    try {
        Thread thread = new Thread(() -> {
            KafkaConsumer<String, String> consumer = new KafkaConsumer<>(getConsumerProps());
            consumer.subscribe(param.getTopics());
            while (flagMap.get(param.getId())) {
                ConsumerRecords<String, String> consumerRecords = consumer.poll(Duration.ofMillis(100));
                if (consumerRecords.iterator().hasNext()) {
                    for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
                        param.setRecord(String.valueOf(consumerRecord));
                        WorkerQueue.offer(param);
                    }
                }
            }
            consumer.close();
        });
        thread.start();
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
    }
}

consume方法是将Kafka中的数据拼接入参后置入一个队列中。队列中存储的是param的引用,因此当队列长度>0时,param的record属性改变,会使队列中所有元素都改变为当前param。

2. 浅拷贝方式

public class Param implements Cloneable {

	private String reocrd;

	private List<SegmentMatchParam> list;
	
    @Override
    public HitIntelligenceParam clone() {
        HitIntelligenceParam hitIntelligenceParam = null;
        try {
            hitIntelligenceParam = (HitIntelligenceParam) super.clone();
        } catch (CloneNotSupportedException e) {
            logger.error(e.getMessage(), e);
        }
        return hitIntelligenceParam;
    }
}

Param对象重写clone方法,浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。

Param record = param.clone();
record.setRecord(String.valueOf(consumerRecord.value()));
WorkerQueue.offer(new Worker(record));

3. 赋值与浅拷贝

当我们把一个对象赋值给一个新的变量时,赋的时该对象在栈中的地址,而在堆中的数据只有一份。两个对象指向同一块存储空间,无论哪个对象发生改变,改变的都是存储空间中的内容。

浅拷贝是按位拷贝对象,它会创建一个新的对象,这个对象是原对象的属性精准拷贝,即如果是属性是基本类型,拷贝的就是基本属性的值,如果属性是引用类型(内存地址),拷贝的就是内存地址。浅拷贝中,如果其中一个对象改变了这个地址,就会影响到另一个对象,即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。

三、浅拷贝与深拷贝

如果在拷贝一个对象的时候,只对基本数据类型进行了拷贝,而对引用数据类型只是进行了引用的传递,而没有真实的创建一个新的对象,则认为是浅拷贝。反之,在对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量,则认为是深拷贝

1. 浅拷贝

对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝.
在这里插入图片描述

2. 深拷贝

对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。
在这里插入图片描述

四、实现

浅拷贝

public class Param implements Cloneable {

	private String reocrd;

	private SegmentMatchParam segment;
	
    @Override
    public Param clone() {
        Param param = null;
        try {
            param = (Param) super.clone();
        } catch (CloneNotSupportedException e) {
            logger.error(e.getMessage(), e);
        }
        return param;
    }
}

Param类实现Clonable接口,重写clone方法。拷贝record的值和segment的内存地址。

2. 深拷贝

常用方案:
1、序列化(serialization)这个对象,再反序列化回来,就可以得到这个新的对象,无非就是序列化的规则需要我们自己来写。
2、继续利用 clone() 方法,既然 clone() 方法,是我们来重写的,实际上我们可以对其内的引用类型的变量,再进行一次 clone()。针对以上例子,SegmentMatchParam 类也需实现Clonable接口并重写clone()方法。

public class SegmentMatchParam implements Cloneable {
	
    @Override
    public SegmentMatchParamclone() {
        SegmentMatchParam param = null;
        try {
            param = (SegmentMatchParam ) super.clone();
        } catch (CloneNotSupportedException e) {
            logger.error(e.getMessage(), e);
        }
        return param;
    }
}

总结

赋值操作:不创建对象,赋值内存地址,一个对象改变,内存中的数据则改变。
浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝。
深拷贝:对基本数据类型进行值传递,对引用数据类型,创建新对象,存储其内容。


原文地址:https://blog.csdn.net/weixin_41832813/article/details/118090534

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值