1、引用拷贝
将对象的引用赋值给新的对象,也就是两个变量其实指向的是同一个对象实例。
public class Main {
static class Teacher {
private String name;
public Teacher(String name) {
this.name = name;
}
// 省略 getter setter
}
public static void main(String[] args) {
Teacher teacher1 = new Teacher("张三");
// 这里只是把 teacher1 对象的引用赋值给 teacher2
Teacher teacher2 = teacher1;
// 不管怎么运行,以下输出的两个对象地址是相同的
System.out.println(teacher1);
System.out.println(teacher2);
// 修改 teacher1 同样也会影响 teacher2
teacher1.setName("李四");
// 输出结果为 李四
System.out.println(teacher2.getName());
}
}
teacher1 和 teacher2 输出的对象地址是相同的,说明其指向的对象是相同的。teacher1 只是把引用地址赋值给了 teacher2,这就是引用拷贝。
2、浅拷贝
浅拷贝需要实现 Cloneable 接口,浅拷贝会把当前对象复制一份,但不会拷贝其成员变量的引用指向的对象,也就是说:浅拷贝只拷贝当前的对象,而不关心其他的对象。
2.1、示例 1:
public class Main {
static class Teacher implements Cloneable {
private String name;
// 省略构造函数,getter setter
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public static void main(String[] args) throws CloneNotSupportedException {
Teacher teacher1 = new Teacher("张三");
Teacher teacher2 = (Teacher) teacher1.clone();
// 会看到不同的地址
System.out.println(teacher1);
System.out.println(teacher2);
// 修改 teacher1 的值,会看到不同的地址
teacher1.setName("李四");
System.out.println(teacher1.getName());
System.out.println(teacher2.getName());
}
}
可以看到两天不同的对象地址,修改 teacher1.setName("李四") 的值不会影响 teacher2。
2.2、示例2,如果类中还有对象的引用
像下面示例中 Student 中有 Teacher 对象的引用。
public class Main {
static class Teacher implements Cloneable {
private String name;
// 省略构造函数,getter,setter
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
static class Student implements Cloneable{
private String name;
private Teacher teacher;
// 省略构造函数,getter setter
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public static void main(String[] args) throws CloneNotSupportedException {
Teacher teacher = new Teacher("张三");
Student student1 = new Student("李四",teacher);
Student student2 = (Student) student1.clone();
// 修改 student1 的 teacher
student1.getTeacher().setName("王五");
// 获取 student2 的teacher 名字,会输出 王五
System.out.println(student2.getTeacher().getName());
// 比较两者的地址,会输出 true,说明 teacher 对象没有拷贝
System.out.println(student1.getTeacher() == student2.getTeacher())
}
}
最后的结果输出是 “王五”,不是已经拷贝 student1 指向的对象实例了吗?浅拷贝的一个特点就是只拷贝当前的对象,也就是 Student ,并不会继续拷贝当前对象以外的其他对象,其他对象依旧是使用引用拷贝。例如 private Teacher teacher;
3、深拷贝
相对于浅拷贝,深拷贝不只拷贝当前的对象,也会把成员变量中引用指向的对象也拷贝一份,如下图所示:
- 深拷贝会把当前对象所引用的所有的对象都会拷贝一份。
3.1、深拷贝实现方式 1,实现 Cloneable
这种方式很麻烦,如果 Teacher 里面还有个 private School school; 的对象引用,则还需要继续修改 Teacher 类和 School 类实现 Cloneable 接口。其明显的缺点是工作量大,代码繁多。
package org.xian;
import java.util.Objects;
public class Main {
static class Teacher implements Cloneable {
private String name;
@Override
public int hashCode() {
return Objects.hash(name);
}
}
static class Student implements Cloneable{
private String name;
private Teacher teacher;
@Override
protected Student clone() throws CloneNotSupportedException {
// 先浅拷贝当前的对象
Student student = (Student) super.clone();
// 将当前的 Teacher 对象再拷贝一份
Teacher teacher = (Teacher) this.getTeacher().clone();
// 设置 teacher
student.setTeacher(teacher);
return student;
}
}
public static void main(String[] args) throws CloneNotSupportedException {
Teacher teacher = new Teacher("张三");
Student student1 = new Student("李四",teacher);
Student student2 = (Student) student1.clone();
// 修改 student1 的 teacher
student1.getTeacher().setName("王五");
// 获取 student2 的teacher 名字,会输出 张三,说明 teacher 也拷贝了一份
System.out.println(student2.getTeacher().getName());
// 比较两者的地址,会输出 false,说明 teacher 重新拷贝一份
System.out.println(student1.getTeacher() == student2.getTeacher());
}
}
3.2、使用序列化的方式
需要实现系列化的接口,需要所有使用的类实现 Serializable 接口。例如 Teacher 新增一个 School school; 那么 School 也需要实现 Serializable 接口。
public class Main {
static class Teacher implements Serializable {
private String name;
}
static class Student implements Serializable{
private String name;
private Teacher teacher;
public Student deepClone() throws IOException, ClassNotFoundException {
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Student) ois.readObject();
}
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
Teacher teacher = new Teacher("张三");
Student student1 = new Student("李四",teacher);
Student student2 = student1.deepClone();
// 修改 student1 的 teacher
student1.getTeacher().setName("王五");
// 获取 student2 的teacher 名字,会输出 张三,说明 teacher 也拷贝了一份
System.out.println(student2.getTeacher().getName());
// 比较两者的地址,会输出 false,说明 teacher 重新拷贝一份
System.out.println(student1.getTeacher() == student2.getTeacher());
}
}
参考:https://blog.csdn.net/baiye_xing/article/details/71788741
来源:oschina
链接:https://my.oschina.net/RyenAng/blog/4485005