拷贝的实现:
只有子类实现了Cloneable接口后才可以使用Object类提供的clone方法。
protected native Object clone() throws CloneNotSupportedException;
Cloneable也是一个标识接口:public interface Cloneable { }
要想让对象具有拷贝的功能,必须实现Cloneable接口,并且在类中自定义clone() 调用Object类提供的继承权限clone方法。
浅拷贝:浅拷贝就是对对象的值(对于基本数据类型而言,值就是内容,对于引用类型,值就是它所指向的地址)进行拷贝。
浅拷贝存在的的问题:牵一发而动全身
只要任何一个拷贝对象(或原对象)中的引用发生了变化,所有的对象都会受到影响
直接来看个例子:
class Teacher{
private String name;
private String job;
public Teacher(String name, String job) {
this.name = name;
this.job = job;
}
}
// 1,实现Cloneable接口
class Student implements Cloneable{
private String name;
private int age;
private Teacher teacher;
public Student(String name, int age, Teacher teacher) {
this.name = name;
this.age = age;
this.teacher = teacher;
}
// 2, 自己定义clone()
public Student clone(){
Student student = null;
try {
// 实现拷贝处理
// 产生一个新的student对象,并且拷贝原有所有属性值
student = (Student) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return student;
}
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;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
}
public static void main(String[] args) {
Teacher teacher = new Teacher("张三百","Teacher");
Student student = new Student("王五",10,teacher);
Student studentClone = student.clone();
System.out.println(student);
System.out.println(studentClone); // 两个地址不同,有拷贝了新的对象
System.out.println("-------------------------------------");
System.out.println(studentClone.getAge());
System.out.println(student.getAge());
System.out.println(studentClone.getName());
System.out.println(student.getName()); // 属性都相同,拷贝到了结果
System.out.println("---------------------------------------");
// 结果为true,拷贝对象和原对象共同引用teacher
System.out.println(teacher == studentClone.getTeacher());
}
}
从运行结果我们可以看到我们确实拷贝到了student对象的克隆对象studentClone,而且两者的所有属性都相同,并且也指向了同一个对象teacher。如下图。
深拷贝:理解了浅拷贝就不难理解深拷贝了,深拷贝拷贝出来的对象产生了所有引用的新对象。
特点:任何对象的改变不会对其他对象产生影响
深拷贝的实现有两种方式:
- 递归实现深拷贝:如果要拷贝的对象中包含对其他对象的引用,则让那个被引用的对象所在的类也实现Cloneable接口,和clone方法。
- 序列化实现深拷贝:使用序列化实现深拷贝时,无需再实现Clonable接口,只需要实现Serializable接口即可,但是clone()还是要写的。
对序列化有疑问的,可以看java对象的序列化与反序列化
看两个例子:
递归实现深拷贝:
// Student引用了Teacher,Teacher也需要序列化
class Teacher implements Cloneable{
private String name;
private String job;
public Teacher clone(){
Teacher teacher = null;
try {
teacher = (Teacher) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return teacher;
}
public Teacher(String name, String job) {
this.name = name;
this.job = job;
}
}
class Student implements Cloneable{
private String name;
private int age;
private Teacher teacher;
public Student(String name, int age, Teacher teacher) {
this.name = name;
this.age = age;
this.teacher = teacher;
}
public Student clone(){
Student student = null;
try {
student = (Student) super.clone();
// 产生新的引用
student.teacher = this.teacher.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return student;
}
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;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
}
public class Test {
public static void main(String[] args) {
Teacher teacher = new Teacher("张三百","Teacher");
Student student = new Student("王五",10,teacher);
Student studentClone = student.clone();
System.out.println(student);
System.out.println(studentClone);
System.out.println("-------------------------------------");
System.out.println(studentClone.getAge());
System.out.println(student.getAge());
System.out.println(studentClone.getName());
System.out.println(student.getName());
System.out.println("---------------------------------------");
System.out.println(teacher == studentClone.getTeacher());
}
}
从结果可以看到,拷贝对象与原对象确实对teacher的引用不同。
序列化实现深拷贝:
import java.io.*;
// 序列化实现深拷贝
class Teacher implements Serializable{
private String name;
private String job;
public Teacher clone(){
Teacher teacher = null;
try {
teacher = (Teacher) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return teacher;
}
public Teacher(String name, String job) {
this.name = name;
this.job = job;
}
}
class Student implements Serializable{
private String name;
private int age;
private Teacher teacher;
public Student(String name, int age, Teacher teacher) {
this.name = name;
this.age = age;
this.teacher = teacher;
}
public Student cloneObject() throws Exception{
Student student = null;
ByteOutputStream bos = new ByteOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//参数是从内存中把数据读进来
ByteArrayInputStream bis = new ByteArrayInputStream(bos.getBytes());
ObjectInputStream ois = new ObjectInputStream(bis);
// 从内存中反序列化出来
return (Student) ois.readObject();
}
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;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
}
public class Test {
public static void main(String[] args) throws Exception{
Teacher teacher = new Teacher("张三百","Teacher");
Student student = new Student("王五",10,teacher);
Student studentClone = student.cloneObject();
System.out.println(student);
System.out.println(studentClone);
System.out.println("-------------------------------------");
System.out.println(studentClone.getAge());
System.out.println(student.getAge());
System.out.println(studentClone.getName());
System.out.println(student.getName());
System.out.println("---------------------------------------");
System.out.println(teacher == studentClone.getTeacher());
}
}
其实除了深浅拷贝,还有延迟拷贝,我们可以理解为延迟拷贝就是深拷贝+浅拷贝
演出拷贝在对象只需要读取时采用浅拷贝,在需要对值做修改时采用深拷贝。