- 什么是克隆
获得一个对象和原对象一样 - 用到的场景
在java中对引用类型的对象进行赋值运算时,不会创建新的对象,只会传递当前对象的一个引用。假如现在已经有了一个对象,该对象中存在一些属性,这时候就要用到克隆来得到一个和他一样的全新对象。而且clone是native方法,效率远高于new - 使用的基本条件
要使用克隆方法,必须实现Cloneable接口,并重写object类中的clone()方法。Cloneable这个接口是一个空接口,只是一个表示,如果没有实现该接口,进行克隆的时候会抛出CloneNotSupportedException异常。
基本使用如下:
public class Student implements Cloneable{
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
- 深拷贝与浅拷贝
浅拷贝是指只拷贝对象本身(包括对象中的基本变量),而不对对象中的引用进行二次拷贝。举例说明:现在有个Student类,有一个Person类,在person类中包含了一个Student类的属性,如果对person类的一个对象进行克隆,克隆得到的对象中的Student属性的引用和原person对象是指向同一个Student对象。测试如下:
Student类:
public class Student implements Cloneable{
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Person类:
public class Person implements Cloneable{
int age;
String name;
Student student;
public Person(int age, String name, Student student) {
this.age = age;
this.name = name;
this.student = student;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
测试类:
public class Test {
public static void main(String []args) throws Exception{
Student student = new Student("小明", 12);
Person person = new Person( 13,"李四", student);
Person person1 =(Person) person.clone();
System.out.println("改变之前");
System.out.println("原对象的基本属性:" + person.name + "," + person.age);
System.out.println("拷贝对象的基本属性:" + person1.name + "," + person1.age);
System.out.println("原对象的student:" + person.student);
System.out.println("拷贝对象的student:" + person1.student);
//对拷贝对象的student属性改变
person1.student.age = 100;
person1.student.name = "小红";
System.out.println("改变之后");
System.out.println("改变后原对象的student:" + person.student);
System.out.println("改变后拷贝对象的student:" + person1.student);
}
}
测试结果:
改变之前
原对象的基本属性:李四,13
拷贝对象的基本属性:李四,13
原对象的student:Student{name='小明', age=12}
拷贝对象的student:Student{name='小明', age=12}
改变之后
改变后原对象的student:Student{name='小红', age=100}
改变后拷贝对象的student:Student{name='小红', age=100}
可以看到,原对象的Student属性随着克隆对象的Student属性的改变而进行了改变,说明没有对Student属性进行克隆,只是传递了一个引用。这就是浅拷贝,如果没有对clone()方法进行改写,那么默认就是浅拷贝。那么如何进行深拷贝呢?下面给出示例
在Person类中对clone()方法进行改写:
public class Person implements Cloneable{
int age;
String name;
Student student;
public Person(int age, String name, Student student) {
this.age = age;
this.name = name;
this.student = student;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Student student =(Student) this.student.clone();
Person person =(Person) super.clone();
person.student = student;
return person;
}
}
测试结果:
改变之前
原对象的基本属性:李四,13
拷贝对象的基本属性:李四,13
原对象的student:Student{name='小明', age=12}
拷贝对象的student:Student{name='小明', age=12}
改变之后
改变后原对象的student:Student{name='小明', age=12}
改变后拷贝对象的student:Student{name='小红', age=100}
可见,实现了深拷贝。
实现深拷贝还有另外一种方法,就是通过序列化获取原对象
Student类:
public class Student implements Serializable {
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Person类:
import java.io.*;
public class Person implements Serializable {
int age;
String name;
Student student;
public Person(int age, String name, Student student) {
this.age = age;
this.name = name;
this.student = student;
}
public Person deepCopy(){
try {
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(this);
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
return (Person) oi.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
测试类:
public class Test {
public static void main(String []args) throws Exception{
Student student = new Student("小明", 12);
Person person = new Person( 13,"李四", student);
Person person1 =person.deepCopy();
System.out.println("改变之前");
System.out.println("原对象的基本属性:" + person.name + "," + person.age);
System.out.println("拷贝对象的基本属性:" + person1.name + "," + person1.age);
System.out.println("原对象的student:" + person.student);
System.out.println("拷贝对象的student:" + person1.student);
//对拷贝对象的student属性改变
person1.student.age = 100;
person1.student.name = "小红";
System.out.println("改变之后");
System.out.println("改变后原对象的student:" + person.student);
System.out.println("改变后拷贝对象的student:" + person1.student);
}
}
测试结果:
改变之前
原对象的基本属性:李四,13
拷贝对象的基本属性:李四,13
原对象的student:Student{name='小明', age=12}
拷贝对象的student:Student{name='小明', age=12}
改变之后
改变后原对象的student:Student{name='小明', age=12}
改变后拷贝对象的student:Student{name='小红', age=100}
但是这样很耗时,不建议