java面试(笔)题:如何实现对象克隆——详解

文章目录

  • 如何实现对象克隆?
    • 1.答案:
    • 2.浅克隆和深克隆的区别
    • 3.实现Cloneable接口实现深克隆和浅克隆
      • 3.1 浅克隆实现
      • 3.2 深克隆实现
    • 4.实现Serializable接口,实现深克隆
    • 5.org.apache.commons中BeanUtils和PropertyUtils工具类实现深克隆

如何实现对象克隆?

1.答案:

答:有两种方式:(第三种可不答)

  • 实现Cloneable接口并重写Object类中的clone()方法
  • 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆
  • 通过org.apache.commons中的工具类BeanUtils和PropertyUtils进行对象复制

2.浅克隆和深克隆的区别

  • 现有Employee类,其中有成员属性为Person类对象
public class Employee implements Cloneable{
     
   
   
       private String name;
       private int age;
       private Person person;
}
  • 当我们进行浅克隆的时候,例如将e1 克隆给 e2 ,这时候输出e1,e2发现是实现了克隆,但是实际上e2和e1中的person对象却还是指向了同一个地址
  • 也就是说但我们修改了e1中的person属性,那么e2中的person属性也会被修改
  • 而深克隆实现后e1,和e2就是完全独立的两个对象,不会相互干扰

3.实现Cloneable接口实现深克隆和浅克隆

  • 首先定义Person类
public class Person {
     

    private String name;
    private int age;
    private int sex;
    
    //下面省略构造器,get set toString方法
    }
  • 定义Employee类,实现Cloneable接口,重写clone方法
  • 这里要注意Employee类中有一个成员属性是Person对象,也就是引用对象
public class Employee implements Cloneable{
     


    private String name;
    private int age;
    private Person person;

    public Employee() {
     
    }

    public Employee(String name, int age, Person person) {
     
        this.name = name;
        this.age = age;
        this.person = person;
    }
    
    @Override
    public Object clone() throws CloneNotSupportedException {
     

       return super.clone();
}
 //下面省略get set toString方法
}

3.1 浅克隆实现

  • 首先我们创建employee 实例对象,然后进行克隆复制给employee1
  • 分别对employee ,employee1 进行输出发现无任何问题
  • 但是当我们对employee中的person成员属性进行修改之后,再分别对employee ,employee1进行输出,发现两者都中的值都修改了!这就是浅克隆
  • 浅克隆:在进行克隆时,克隆后的两个对象中的引用属性都是指向同一个地址,也就是employee ,employee1中的person是指向同一个对象地址,一个修改了,另一个也会修改
		Person person = new Person("name",10,1);
        Employee employee = new Employee("fanglei",20,person);
        System.out.println("employee:  "+employee);

        try {
     
        	//浅克隆
            Employee employee1 = (Employee) employee.clone();
            System.out.println("employee1:  "+ employee1);
			
			//修改employee中person的属性,employee1中的属性也修改了
            employee.getPerson().setAge(100);
            System.out.println("employee:  "+employee);
            System.out.println("employee1:  "+ employee1);
        } catch (CloneNotSupportedException e) {
     
            e.printStackTrace();
        }
  • 输出结果:可以看到两个都修改为了100

java面试(笔)题:如何实现对象克隆——详解_第1张图片

3.2 深克隆实现

  • 通过实现Clonable接口实现深克隆,那么就要该类中的引用对象属性也要实现Clonable接口并且重写clone方法
  • 也就是说Person类也要实现Clonable接口并且重写clone方法
  • 并在Employee类的clone方法中调用Person的clone方法进行克隆复制

下图为修改之后的Person类:其实就是实现Clonable接口并且重写clone方法

public class Person implements  Cloneable{
     

    private String name;
    private int age;
    private int sex;

    @Override
    protected Object clone() throws CloneNotSupportedException {
     
        return super.clone();
    }
}

下图为Employee类clone方法修改之后:

@Override
public Object clone() throws CloneNotSupportedException {
     

    Employee employee = null;
    employee = (Employee) super.clone();
    employee.person = (Person) this.person.clone();
    return  employee;
}

再调用刚刚的测试克隆代码:

		Person person = new Person("name",10,1);
        Employee employee = new Employee("fanglei",20,person);
        System.out.println("employee:  "+employee);

        try {
     
        	//浅克隆
            Employee employee1 = (Employee) employee.clone();
            System.out.println("employee1:  "+ employee1);
			
			//修改employee中person的属性,employee1中的属性也修改了
            employee.getPerson().setAge(100);
            System.out.println("employee:  "+employee);
            System.out.println("employee1:  "+ employee1);
        } catch (CloneNotSupportedException e) {
     
            e.printStackTrace();
        }

输出结果如下:可以看到一个为10,一个为100
java面试(笔)题:如何实现对象克隆——详解_第2张图片

4.实现Serializable接口,实现深克隆

  • Employee修改只需要实现Serializable接口
public class Employee implements  Serializable{
     }
  • Person类修改也只需要实现Serializable接口
public class Person implements  Serializable{
     }
  • 新增克隆工具类
public class CloneUtil {
     
    private CloneUtil() {
     
        throw new AssertionError();
    }

    /**
     * Clone.
     * 调用ByteArrayInputStream或ByteArrayOutputStream对象的close方法没有任何意义,
     * 这两个基于内存的流只要垃圾回收器清理对象时就能够释放资源,这一点不同于对外部资源(如文件流)的释放。
     *
     * @param obj The object.
     * @param  The type.
     * @return The cloned object.
     * @throws Exception The exception.
     */
    public static <T> T clone(T obj) throws Exception {
     
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(obj);

        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        return (T) objectInputStream.readObject();
    }


}
  • 调用测试:
 		Person p1 = new Person("name",10,1);
        Employee e1 = new Employee("fanglei",20,p1);
        System.out.println("e1:  "+e1);

        try {
     
            Employee e2 = CloneUtil.clone(e1);
            System.out.println("e2:  "+ e2);

            e1.getPerson().setAge(100);
            System.out.println("e1:  "+e1);
            System.out.println("e2:  "+ e2);
        } catch (CloneNotSupportedException e) {
     
            e.printStackTrace();
        }
  • 输出结果:发现的确实现了深克隆
    java面试(笔)题:如何实现对象克隆——详解_第3张图片

5.org.apache.commons中BeanUtils和PropertyUtils工具类实现深克隆

  • BeanUtils提供类型转换功能,即发现两个JavaBean的同名属性为不同类型时,在支持的数据类型范围内进行转换,而PropertyUtils不支持这个功能,但是速度会更快一些。在实际开发中,BeanUtils使用更普遍一点,犯错的风险更低一点。
try {
     
  Student stu1 = new Student();
  stu1.setNumber(12345);
  Student stu2 = new Student();
  BeanUtils.copyProperties(stu2, stu1);
} catch (IllegalAccessException e) {
     
  e.printStackTrace();
} catch (InvocationTargetException e) {
     
  e.printStackTrace();
}
try {
     
  Student stu1 = new Student();
  stu1.setNumber(12345);
  Student stu2 = new Student();
  PropertyUtils.copyProperties(stu2, stu1);
  catch (NoSuchMethodException e) {
     
    e.printStackTrace();
  }

你可能感兴趣的:(java面试题,java,对象克隆,克隆,深克隆,浅克隆)