关于java中Cloneable及Comparable接口的使用

本文深入探讨了Java中对象拷贝的概念,详细解释了浅拷贝与深拷贝的区别,以及如何实现这两种拷贝方法。通过实例展示了实现深拷贝的三种方式,包括直接克隆引用类型、创建新引用类型赋值,以及通过序列化和反序列化实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  clone方法就是返回一个原对象的拷贝,默认走的是浅拷贝。克隆的目的是复制对象,但是新的对象是独立于原来的对象的,一般我们克隆出来的对象都在一些属性做了更改,这个时候需要小心一点,如果更改的属性是引用数据类型,可能会影响到原来的对象,如果都是基本数据类型则不怕。使用clone方法的前提是继承Cloneable接口,数组默认实现了Cloneable接口,默认走的是浅拷贝。深克隆与浅克隆的区别:浅克隆后的对象中的引用类型属性的修改会影响原对象中的内容。深克隆不会影响

克隆方法的调用方法:实现Cloneable接口,重写Object的clone()方法,并把protected修饰符改为public

 

那么问题来了,什么是浅拷贝?什么是深拷贝呢?

1.浅克隆(shadow clone)    只用clone方法


   克隆就是复制一个对象的复本.若只需要复制对象的字段值(对于基本数据类型,如:int,long,float等,则复制值;对于引用数据类型仅复制该字段值,如数组变量则复制地址,对于对象变量则复制对象的reference,因此是引用传递,复制的对象若改变其中复合数据,会影响被复制对象中的复合数据,因为他们指向的是同一个地址。

 

2.深克隆(deep clone) 

    可以通过三种方式实现,前两种适用于引用类型变量较少的时候。

     方法一:简单clone对象后,对象中的引用类型变量再次进行clone。

     方法二:简单clone对象后,创建新的引用类型变量进行赋值。

     方法三:序列化对象,所有类都需要实现Serializable接口。类中的所有引用类型变量对应的类都需要实现序列化。

关于序列化,任何类型只要实现了Serializable接口,就可以被保存到文件中,或者作为数据流通过网络发送到别的地方。也可以用管道来传输到系统的其他程序中。可以参考文章——>

java序列化及反序列化

实现序列化接口时,private static final long serialVersionUID 的作用

    深克隆与浅克隆的区别在于对复合数据类型的复制。若对象中的某个字段为复合类型,在克隆对象的时候,需要为该字段重新创建一个对象,两个对象指向两个不同的引用。

自己写的例子如下:

import java.util.Arrays;
import java.util.Date;

public class FileTest extends DeepClone implements Comparable<FileTest>,Cloneable{
    private static final long serialVersionUID = 1L;
    private int a = 1;
    private int b = 2;
    private int[] c = new int[]{4,5,6};
    private Date date = new Date() ;

    public int getSize(){
        return this.getA()*this.getA()*2;
    }
    public FileTest(int a,int b,int[] c){
        this.a = a;
        this.b = b;
        this.c = c;
    }
    @Override
    public Object clone() {//重写Object的clone方法
        try {
            //只执行这一步,是浅克隆(shadow clone)
            FileTest f = (FileTest)super.clone();
            //若想深克隆(deep clone),需要将复合成员变量进行值复制,而不是引用复制
            //深克隆方法一(直接再将引用对象clone)
            int[] d1 = (int[])c.clone();
            f.setC(d1);

            //深克隆方法二(相对来说比较笨拙)
            int[] c = f.getC();
            int[] d2 = new int[c.length];
            for(int i=0;i<c.length;i++) {
                d2[i] = c[i];
            }
            f.setC(d2);

            //深克隆方法一(对复合对象date进行深克隆)
            Date date = f.getD();
            f.date = (Date) this.getD().clone();

            return f;
        }catch (CloneNotSupportedException e){
            return null;
        }
    }
    public static void main(String[] args) throws Exception {
        FileTest f1 = new FileTest(5,7,new int[]{1,2,3});
        FileTest f2 = new FileTest(1,2,new int[]{4,5,6});
        FileTest f3 = new FileTest(4,3,new int[]{7,8,9});
        //使用compareTo方法
        System.out.println(f1.compareTo(f2));

        //因为实现了compareTo方法,因此可以直接调用Arrays的sort方法
        FileTest[] array = new FileTest[3];
        array[0] = f1;
        array[1] = f2;
        array[2] = f3;
        Arrays.sort(array);
        for(FileTest f:array){
            System.out.println(f.getSize());
        }

        //FileTest实现了Cloneable接口并重写了Object的clone方法,重定义了clone方法
        FileTest f4 = (FileTest) f1.clone();
//        System.out.println(f1.getC().toString());
//        System.out.println(f4.getC().toString());
        System.out.println(f1.getD()==f4.getD());
        //重写了equals方法
        System.out.println(f1.equals(f4));

        //深克隆方法三(序列化)
        FileTest f5 = (FileTest) f1.deepClone();
        System.out.println(f1.getD()==f5.getD());
        System.out.println(f1 == f5);

    }


    @Override
    public int compareTo(FileTest o) {//注意:这里传参需要传自定义类,因为Compareable是泛型接口,compareTo方法形参也是泛型
        if(this.getSize() > o.getSize()){ //根据getSize()方法比较两个对象的大小
            return 1;
        }else if(this.getSize() == o.getSize()){
            return 0;
        }else{
            return -1;
        }

    }
    @Override
    public boolean equals(Object o){ //如果想要比较两个对象,最好也重写Object的equals方法,且实现逻辑与compareTo保持一致
        FileTest f = (FileTest)o;
        if(f.getSize() == this.getSize()){
            return true;
        }
        return false;
    }

    public int getA() {
        return a;
    }

    public int getB() {
        return b;
    }

    public int[] getC() {
        return c;
    }

    public void setA(int a) {
        this.a = a;
    }

    public void setB(int b) {
        this.b = b;
    }

    public void setC(int[] c) {
        this.c = c;
    }

    public Date getD() {
        return date;
    }
}

 

import java.io.*;

public class DeepClone implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 利用序列化和反序列化进行对象的深拷贝
     * @return
     * @throws Exception
     */
    protected Object deepClone() throws Exception{
        //序列化
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);

        oos.writeObject(this);

        //反序列化
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);

        return ois.readObject();
    }
}

一些深度思考(来自《Java语言程序设计与数据结构》P452):

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值