java浅拷贝与深拷贝

文章目录

    • 概述
    • 浅拷贝的实现
    • 深拷贝的实现
    • 写在最后

概述

简单来说,浅拷贝对于基本数据类型,会拷贝他们的值,而对于引用类型,会拷贝他们的引用地址。
下图为浅拷贝。
java浅拷贝与深拷贝_第1张图片
而对于深拷贝来说,会对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容
java浅拷贝与深拷贝_第2张图片
基本类型(8大基本类型):整形:byte(1字节)、short(2字节)、int(4字节)、long(8字节);浮点型:float(4字节,单精度浮点型),double(8字节,双精度浮点型);一个字符型:char(占用2字节);一个布尔型:boolean(可能占用4字节或1字节,如果是boolean变量则4字节,因为是用int存储,而如果是boolean数组,则每个boolean变量都是1字节,因为使用byte存储)
引用类型:类、接口、数组。

浅拷贝的实现

首先说一点,浅拷贝和深拷贝都可以使用Cloneable接口,并实现clone()方法,在方法中直接调用super.clone方法即可。我们创建一个Father类,其中包含一个String,一个int,以及一个对象。

class Father implements Cloneable{
    Example example;
    String s;
    int a;
    public  Father(){
    }
    ...
    @Override
    public Object clone(){
        try {
            return super.clone();
        }catch (CloneNotSupportedException e){
            e.printStackTrace();
        }
        return  null;
    }
    ...
}

我们继续看效果,我们创建一个Father对象,同时通过clone方法浅拷贝一个Father对象。

public static void main(String[]args){
        Father father = new Father();
        father.setA(10);
        father.setExample(new Example(100));
        father.setS("aaa");
        Father fatherb = (Father) father.clone();
        System.out.println("father==fatherb? "+(father==fatherb));
        System.out.println("father.equals(fatherb)? "+father.equals(fatherb));
        father.setA(5);
        father.example.setExa(50);
        System.out.println("father a:"+father.a+",fatherb a:"+fatherb.a);
        System.out.println("father example:"+father.example+",fatherb example"+fatherb.example);
        father.setS("nnn");
        System.out.println("father s:"+father.s+",fatherb s:"+fatherb.s);
    }

结果:可以看到拷贝之后的对象是一个新对象,所以==返回的是false。而equals返回true,我这里冲洗了下equals就是比较值是否相等。之后我们改变了father的int类型变量a,可以看到father中a变更为5,而fatherb中的a没有改变,由此可以证明浅拷贝对应基本类型是拷贝的值。然后我们将father的example进行更改(将example中的数据域进行更改),可以看到fatherb也跟着改变。因此浅拷贝对应引用类型拷贝的是引用。

最后,为什么同样是引用类型的String也会改变呢,因为String的引用是指向了常量池中的字符串,而我们创建新的字符串常量“sss”,并让father中的s指向它,而fatherb 的s还是指向“sss”,因此fatherb没有跟着fathera改变。

father==fatherb? false
father.equals(fatherb)? true
father a:5,fatherb a:10
father example:Example exa 50,fatherb exampleExample exa 50
father s:nnn,fatherb s:aaa

深拷贝的实现

我们可以继续利用 clone() 方法,既然 clone() 方法,是我们来重写的,实际上我们可以对其内的引用类型的变量,再进行一次 clone()

首先是Example类,我们需要让他也实现Cloneable接口。

class Example implements Cloneable{
	...
    @Override
    public Object clone(){
        try {
           return super.clone();
        }
        catch (CloneNotSupportedException e)
        {
            e.printStackTrace();
        }
        return null;
    }
    
}

我们要做的就是将Father类中的对象再通过clone拷贝一次,并返回。

class Father implements Cloneable{
	...
    @Override
    public Object clone(){
        try {
            Father fatherb = (Father) super.clone();
            fatherb.example = (Example) this.example.clone();
            return  fatherb;
            //return super.clone();
        }catch (CloneNotSupportedException e){
            e.printStackTrace();
        }
        return  null;
    }
}

写在最后

在复习一下equals的标准写法吧。注意equasl传入的是Object对象
标准的四步:(1)直接用==判断,是否直接是同一块地址;
(2)如果传入的对象为null,可以直接返回false;
(3)如果不是同一个类加载器加载的,就不可能相等。
(4)最后在比较属性(基本类型通过==,引用类型使用equals方法)

@Override
    public boolean equals(Object o1){
        //直接存在同一块地址,
        if(o1==this)
            return  true;
        //为空可以直接返回false
        if(o1==null)
            return false;
        //class不匹配肯定不可能相等
        if(o1.getClass()!=this.getClass())
            return  false;
        Father o2 = (Father) o1;
        return this.a==o2.a&&this.s.equals(o2.s)&&this.example.equals(example);
    }

你可能感兴趣的:(java技术)