• Java浅克隆和深克隆


    一、何为克隆

    在Java的体系中,数据类型分为基本数据类型引用数据类型

    基本数据类型包括byte,short,int,long,float,double,boolean,char 8种,其克隆可通过赋值运算实现,比如

    int a = 1;
    int b = a;

    引用类型的克隆的实现方式有以下两种:

    1)实现Cloneable接口,重写clone() 方法,修改clone() 方法的修饰符为public。其分为浅克隆深克隆

    2)  实现Serializable接口,对实例进行序列化,通过二进制流反序列化。其为真正的克隆

    注:若类没实现Cloneable接口,调用对象的clone方法会抛出CloneNotSupportedException异常

    二、重写clone实现克隆

    克隆就是获得当前对象的副本,其与当前对象是内存地址不同的两个对象。

    浅克隆指的是当前对象的实例成员为引用类型时,副本的该实例成员只是复制了其引用值,指向同一个对象。

    深克隆则是对该引用指向的对象进行克隆,然后引用指向了该副本,指向不同的对象。

    浅克隆导致了一个问题就是,对实例或克隆副本做出的改变会影响彼此,从而导致错误的结果,下面的例子可以说明:

    汽车类 Car,包含一个引用类型成员brand:

    /**
     * Car类
     * 实现Cloneable接口,重写clone方法
     * @author zhangyj
     *
     */
    public class Car implements Cloneable{
        //使用年限
        private int year;
        //品牌
        private Brand brand; 
        
        public int getYear() {
            return year;
        }
        public void setYear(int year) {
            this.year = year;
        }
        public Brand getBrand() {
            return brand;
        }
        public void setBrand(Brand brand) {
            this.brand = brand;
        }
        
        //构造器
        public Car(int year, Brand brand) {
            this.year = year;
            this.brand = brand;
        }
        
        /**
         * 浅克隆,调用父类的clone方法
         */
        @Override
        public Object clone() {
            Object obj = null;
            try {
                obj = super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return obj;
        }
        
        /**
         * 深克隆,其引用类型成员也要调用clone
         * @return
         */
        public Car deepClone() {
            Car car = null;
            try {
                car = (Car)super.clone();
                car.setBrand((Brand)brand.clone());
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return car;
        }
        
        @Override
        public String toString() {
            return "[year=" + year + ", brand=" + brand + "]";
        }
    }
    /**
     * 品牌类
     * @author zhangyj
     *
     */
    class Brand implements Cloneable{
        //名称
        private String name;
    
        public String getName() {
            return name;
        }
        
        public void setName(String name) {
            this.name = name;
        }
        
        @Override
        protected Object clone() {
            Object obj = null;
            try {
                obj = super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return obj;
        }
        
        public Brand(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "Brand [name=" + name + "]";
        }
    }

    测试类 CarCloneTest :

    public class CarCloneTest {
        public static void main(String[] args) {
            Brand brand = new Brand("BMW");
            Car car = new Car(5, brand);
            Car carClone;
            carClone = (Car) car.clone();  //(1)
            //carClone = (Car) car.deepClone(); //(2)
            
            System.out.println("******************************************");
            System.out.println("car "+car);
            System.out.println("carClone "+carClone);
            
            System.out.println("******************************************");
            System.out.println("将car年限改为 7,品牌名称改为Banz");
            brand.setName("Banz");
            car.setYear(7);
            
            System.out.println("******************************************");
            System.out.println("car "+car);
            System.out.println("carClone "+carClone);
        }
    }

    浅克隆测试,运行(1)代码,得到结果如下:

    ******************************************
    car [year=5, brand=Brand [name=BMW]]
    carClone [year=5, brand=Brand [name=BMW]]
    ******************************************
    将car年限改为 7,品牌名称改为Banz ****************************************** car [year=7, brand=Brand [name=Banz]] carClone [year=5, brand=Brand [name=Banz]]

    可见当修改car中brand对象的名称为Banz时,carClone也跟着改变,可见其引用的brand对象为同一个,显然这不是我们想要的,而深克隆就解决了这个问题。

    深克隆测试,运行(2)代码,得到结果如下:

    ******************************************
    car [year=5, brand=Brand [name=BMW]]
    carClone [year=5, brand=Brand [name=BMW]]
    ******************************************
    将car年限改为 7,品牌名称改为Banz ****************************************** car [year=7, brand=Brand [name=Banz]] carClone [year=5, brand=Brand [name=BMW]]

    三、序列化实现深克隆

    序列化对象必须实现Serializable接口,使对象可序列化。

    public class Car implements Serializable{
        private static final long serialVersionUID = 7883197573658810857L;
        private int year; //使用年限
        private Brand brand; //品牌
        
        public int getYear() {
            return year;
        }
        public void setYear(int year) {
            this.year = year;
        }
        public Brand getBrand() {
            return brand;
        }
        public void setBrand(Brand brand) {
            this.brand = brand;
        }
        
        public Car(int year, Brand brand) {
            this.year = year;
            this.brand = brand;
        }
        
        @Override
        public String toString() {
            return "[year=" + year + ", brand=" + brand + "]";
        }
    }
    class Brand implements Serializable{
        private static final long serialVersionUID = -6505899377489945908L;
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
        
        public Brand(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "Brand [name=" + name + "]";
        }
    }

    克隆工具类 CloneUtil: 

    public final class CloneUtil {
        private CloneUtil() { throw new AssertionError(); }
        
        /**
         * 序列化实现深克隆
         * @param t 泛型对象
         * @return
         */
        @SuppressWarnings("unchecked")
        public static <T> T deepClone(T t) {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ObjectOutputStream objOut;
            ObjectInputStream objIn;
            T tClone = null ;
            try {
                objOut = new ObjectOutputStream(out);
                 objOut.writeObject(t);  //将对象以二进制流形式写入
                ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
                objIn = new ObjectInputStream(in);
                tClone = (T)objIn.readObject(); //反序列化读取对象
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return tClone;
        }
    }

    测试类 CarCloneTest: 

    public class CarCloneTest {
        public static void main(String[] args) {
            Brand brand = new Brand("BMW");
            Car car = new Car(5, brand);
            Car carClone;
    //        carClone = (Car) car.clone();  //(1)
    //        carClone = (Car) car.deepClone(); //(2)
            carClone = CloneUtil.deepClone(car);
            
            System.out.println("******************************************");
            System.out.println("car "+car);
            System.out.println("carClone "+carClone);
            
            System.out.println("******************************************");
            System.out.println("将car年限改为 7,品牌名称改为Banz");
            brand.setName("Banz");
            car.setYear(7);
            
            System.out.println("******************************************");
            System.out.println("car "+car);
            System.out.println("carClone "+carClone);
        }
    }

    运行测试程序,得到结果如下:

    ******************************************
    car [year=5, brand=Brand [name=BMW]]
    carClone [year=5, brand=Brand [name=BMW]]
    ******************************************
    将car年限改为 7,品牌名称改为Banz
    ****************************************** 
    car [year
    =7, brand=Brand [name=Banz]] carClone [year=5, brand=Brand [name=BMW]]

     可见通过反序列化对象进行克隆也能得到我们想要的结果。

    以上!

  • 相关阅读:
    java:产生小数位数为2的随机概率,使得和为1
    大数据网络分析规划
    java碎笔
    mysql修改记录
    mysql导入导出数据
    Centos中hive/hbase/hadoop/mysql实际操作及问题总结
    linux后台运行程序
    Centos搭建mysql/Hadoop/Hive/Hbase/Sqoop/Pig
    ARM-LINUX自动采集温湿度传感器数据
    java中枚举类型的使用
  • 原文地址:https://www.cnblogs.com/zhangyuejia/p/8625698.html
Copyright © 2020-2023  润新知