java对象的clone


 

克隆的目的:快速创建一个已有对象的副本。

克隆的步骤: 

  1. 创建一个对象
  2. 将原有对象的数据导入到新创建的数据中

clone方法首先会判对象是否实现了Cloneable接口,若无则抛出CloneNotSupportedException, 最后会调用internalClone. intervalClone是一个native方法,一般来说native方法的执行效率高于非native方法。

 源码:

 

当某个类要复写clone方法时,要继承Cloneable接口。通常的克隆对象都是通过super.clone()方法来克隆对象。

 

2.浅克隆(shadow clone)

   克隆就是复制一个对象的复本.若只需要复制对象的字段值(对于基本数据类型,如:int,long,float等,则复制值;对于复合数据类型仅复制该字段值,如数组变量则复制地址,对于对象变量则复制对象的reference。

 

  1. 实现Cloneable接口的类都具备被拷贝的能力,拷贝在内存中进行,比new生成对象性能明显提升;
  2. clone方法是Object类的protected方法,也就是在用户编写的代码中不能直接调用。为此必须重写定义clone方法,并声明为public,
  3. Cloneable接口的出现与接口正常使用没有关系,是一个空接口,它只是一个标记表示一个对象需要克隆,如果一个对象需要克隆,而没有实现Cloneable接口,就会产生一个CloneNotSupportedException异常
  4. Clone()方法存在与Object对象中,但是其不会将对象的全部属性都拷贝,而是有选择的拷贝
    1. 基本类型:
    1. 对象:如果变量是一个实例对象,只拷贝地址引用;
    2. String字符串:拷贝地址引用,但是修改时,会从字符串池中重新生成新的字符串,原来的保持不变;
  1. 对于Clone实现对象的拷贝:需要新建大量的对象,工程量大可以使用序列化来实现对象的拷贝

 

需要clone的对象:

package com.gzu.pyu.thinking.in.java.practice0211;

import java.io.Serializable;

public class Student implements Cloneable ,Serializable {
    public String name;
    public int age;
    public Address address;

    public Student() {
    }

    public Student(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public Object clone()  {
        Student student=null;
        try {
            //淺拷貝
            student= (Student)super.clone();
        } catch (CloneNotSupportedException e) {
            return new Student();
        }
        //支持深度克隆
        student.address=address.clone();
        return student;
    }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("Student{");
        sb.append("name='").append(name).append('\'');
        sb.append(", age=").append(age);
        sb.append(", address=").append(address);
        sb.append('}');
        return sb.toString();
    }
}

class Address implements Cloneable,Serializable{
    String city;
    String town;

    public Address() {
    }

    public Address(String city, String town) {
        this.city = city;
        this.town = town;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getTown() {
        return town;
    }

    public void setTown(String town) {
        this.town = town;
    }

    @Override
    public Address clone()  {

        Address address=null;
        try {
            address= (Address) super.clone();
        } catch (CloneNotSupportedException e) {
            return new Address();
        }
        return address;
    }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("Address{");
        sb.append("city='").append(city).append('\'');
        sb.append(", town='").append(town).append('\'');
        sb.append('}');
        return sb.toString();
    }
}

 

测试方法如下:
package com.gzu.pyu.thinking.in.java.practice0211;

import com.gzu.pyu.thinking.in.java.utils.CloneUtils;

public class TestMain {
    public static void main(String ... args){
        Address address=new Address("ShengZheng","LongGang");
        //将address指向的区域赋值给addressNew,使得addressNew和address同时指向了同一个区域
        Address addressNew=address;

        //address指向的内容改变了,addressNew指向的内容也跟着改变
        System.out.println(address==addressNew);// true
        address.setCity("GuiYang");
        System.out.println(address);
//        Address{city='GuiYang', town='LongGang'}
        System.out.println(addressNew);
//        Address{city='GuiYang', town='LongGang'}

        //address進行克隆,將address進行了复制了,产生了一个新的对象
        Address addressClone = address.clone();
        System.out.println(address==addressClone);// false
        address.setTown("HuaXi");
        System.out.println(address);
//        Address{city='GuiYang', town='HuaXi'}
        System.out.println(addressClone);
//        Address{city='GuiYang', town='LongGang'}

        //在内存中通过字节流的拷贝是比较容易的,把木对象写入到一个字节流中,再从字节流中读出来
        Address addr = CloneUtils.clone(address);
        System.out.println(address==addr);// false
        addr.setCity("PanZhou");
        System.out.println(address);
//        Address{city='GuiYang', town='HuaXi'}
        System.out.println(addr);
//        Address{city='PanZhou', town='HuaXi'}
    }
}

 

二、使用序列化来实现对象的拷贝

  1. 在内存中通过字节流的拷贝是比较容易的,把木对象写入到一个字节流中,再从字节流中毒出来,这样可以创建一个新的对象,且新对象与木对象不存在引用共享的问题,实现的对象的深拷贝   
package com.gzu.pyu.thinking.in.java.utils;

import java.io.*;

/**
 * 在内存中通过字节流的拷贝是比较容易的,把木对象写入到一个字节流中,再从字节流中读出来,
 * 这样可以创建一个新的对象,且新对象与木对象不存在引用共享的问题,实现的对象的深拷贝
 */
public class CloneUtils {
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T clone(T obj){
        T cloneObj = null;
            //写入字节流
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ObjectOutputStream obs = new ObjectOutputStream(out);
            obs.writeObject(obj);
            obs.close();

            //分配内存,写入原始对象,生成新对象
            ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(ios);
            //返回生成的新对象
            cloneObj = (T) ois.readObject();
            ois.close();
        }catch(IOException e){
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return cloneObj;
    }
}

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM