Effective Java(什么是clone、什么是“深拷贝”和“浅拷贝”)
2017-09-20 14:42
387 查看
什么是clone?
按照书中的话来讲,能不重写clone就不要去重写,因为它带来的问题太多了。我们暂且不讨论这里面的陷阱有多少,只从对Java基础知识的掌握程度来说明什么是clone,以及什么是“深拷贝”和“浅拷贝”。
首先观察以下代码,并思考对象在内存中的分配以及引用的变化:
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
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 class Main {
public static void main(String[] args) throws Exception{
Student stu = new Student("kevin", 23);
Student stu2 = stu;
stu2.setAge(0);
System.out.println(stu.getAge());
}
}
这是一段很简单的代码,Student对象实例stu、stu2在内存中的分配及引用分别如下图所示:
所以代码中出现修改stu2实例的age字段时,stu中的age字段也被修改了,原因很简单因为它们的引用指向的都是同一个对象实例。
那如果我们想在实例化一个name=”kevin”,age=23的Student实例怎么办呢?当然可以再写一段Student stu2 = new Student(“kevin”, 23);如果再重新构造一个对象实例很复杂,能不能直接复制呢?显然,使Student实现Cloneable接口并重写clone方法即可,注意此时的重写clone方法在里面仅有一句代码即是即调用父类的clone方法,而不是自定义实现:
public class Student implements Cloneable{
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
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
protected Student clone()
throws CloneNotSupportedException {
return (Student)super.clone();
}
}
public class Main {
public static void main(String[] args) throws Exception{
Student stu = new Student("kevin", 23);
Student stu2 = stu.clone();
stu2.setAge(0);
System.out.println(stu.getAge());
}
}
调用clone方法产生的对象实例并不是之前的实例,而是在堆上重新实例化了一个各个参数类型值都相同的实例,所以此时修改stu2的age字段并不会影响到stu,看起来clone就是一个构造器的作用——创建实例。
什么是“深拷贝”和“浅拷贝”?
在上面的例子Student类中,我们新增一个引用型变量Test类:
public class Student implements Cloneable{
private String name;
private int age;
private Test test;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
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 String getTest() {
return test;
}
public void setTest(Test test) {
this.test= test;
}
@Override
protected Student clone()
throws CloneNotSupportedException {
return (Student)super.clone();
}
}
public class Main {
public static void main(String[] args) throws Exception{
Student stu = new Student("kevin", 23);
Student stu2 = stu.clone();
stu2.setAge(0);
System.out.println(stu.getAge());
}
}
实际上测试这段代码可知,clone出来的stu2确实和stu是两个对象实例,但它们的成员变量实际上确是指向的同一个引用(通过比较hashCode可知),这也就是所谓的“浅拷贝”。
对应的“深拷贝”则是所有的成员变量都会真正的做一份拷贝。怎么做到“深拷贝”,则是要求将类中的所有引用型变量都要clone。
**
* 深拷贝
* Created by zhaozhiyong on 2017/09/20.
*/
public class Student implements Cloneable{
private String name;
private int age;
private Test test;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
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 Test getTest() {
return test;
}
public void setTest(Test test) {
this.test = test;
}
@Override
protected Object clone()
throws CloneNotSupportedException {
Student stu = (Student)super.clone();
stu.test = test.clone(); //Test类也要继承Cloneable
return stu;
}
}
书中是不建议自定义重写clone方法的,如果非要重写书中总结为一句话:clone方法就是一个构造器,你必须确保它不会伤害到原始的对象,并确保正确地创建被克隆对象中的约束条件。
再说一个与本条目无关的点,查看Cloneable接口实际上可以发现里面什么方法都没有,clone方法却来自Object类,继承了Cloneable接口为什么就能重写clone方法了呢?原因在于clone方法在Object类中的修饰符是protected,而Cloneable接口和Object处于同一个包下,熟悉修饰符的都知道protected的权限限定在同一个包下或者其子类。Cloneable和Object同属于一个包,Cloneable自然能继承clone方法,继承了Cloneable接口的成为了它的子类同样也就继承了clone方法。
按照书中的话来讲,能不重写clone就不要去重写,因为它带来的问题太多了。我们暂且不讨论这里面的陷阱有多少,只从对Java基础知识的掌握程度来说明什么是clone,以及什么是“深拷贝”和“浅拷贝”。
首先观察以下代码,并思考对象在内存中的分配以及引用的变化:
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
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 class Main {
public static void main(String[] args) throws Exception{
Student stu = new Student("kevin", 23);
Student stu2 = stu;
stu2.setAge(0);
System.out.println(stu.getAge());
}
}
这是一段很简单的代码,Student对象实例stu、stu2在内存中的分配及引用分别如下图所示:
所以代码中出现修改stu2实例的age字段时,stu中的age字段也被修改了,原因很简单因为它们的引用指向的都是同一个对象实例。
那如果我们想在实例化一个name=”kevin”,age=23的Student实例怎么办呢?当然可以再写一段Student stu2 = new Student(“kevin”, 23);如果再重新构造一个对象实例很复杂,能不能直接复制呢?显然,使Student实现Cloneable接口并重写clone方法即可,注意此时的重写clone方法在里面仅有一句代码即是即调用父类的clone方法,而不是自定义实现:
public class Student implements Cloneable{
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
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
protected Student clone()
throws CloneNotSupportedException {
return (Student)super.clone();
}
}
public class Main {
public static void main(String[] args) throws Exception{
Student stu = new Student("kevin", 23);
Student stu2 = stu.clone();
stu2.setAge(0);
System.out.println(stu.getAge());
}
}
调用clone方法产生的对象实例并不是之前的实例,而是在堆上重新实例化了一个各个参数类型值都相同的实例,所以此时修改stu2的age字段并不会影响到stu,看起来clone就是一个构造器的作用——创建实例。
什么是“深拷贝”和“浅拷贝”?
在上面的例子Student类中,我们新增一个引用型变量Test类:
public class Student implements Cloneable{
private String name;
private int age;
private Test test;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
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 String getTest() {
return test;
}
public void setTest(Test test) {
this.test= test;
}
@Override
protected Student clone()
throws CloneNotSupportedException {
return (Student)super.clone();
}
}
public class Main {
public static void main(String[] args) throws Exception{
Student stu = new Student("kevin", 23);
Student stu2 = stu.clone();
stu2.setAge(0);
System.out.println(stu.getAge());
}
}
实际上测试这段代码可知,clone出来的stu2确实和stu是两个对象实例,但它们的成员变量实际上确是指向的同一个引用(通过比较hashCode可知),这也就是所谓的“浅拷贝”。
对应的“深拷贝”则是所有的成员变量都会真正的做一份拷贝。怎么做到“深拷贝”,则是要求将类中的所有引用型变量都要clone。
**
* 深拷贝
* Created by zhaozhiyong on 2017/09/20.
*/
public class Student implements Cloneable{
private String name;
private int age;
private Test test;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
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 Test getTest() {
return test;
}
public void setTest(Test test) {
this.test = test;
}
@Override
protected Object clone()
throws CloneNotSupportedException {
Student stu = (Student)super.clone();
stu.test = test.clone(); //Test类也要继承Cloneable
return stu;
}
}
书中是不建议自定义重写clone方法的,如果非要重写书中总结为一句话:clone方法就是一个构造器,你必须确保它不会伤害到原始的对象,并确保正确地创建被克隆对象中的约束条件。
再说一个与本条目无关的点,查看Cloneable接口实际上可以发现里面什么方法都没有,clone方法却来自Object类,继承了Cloneable接口为什么就能重写clone方法了呢?原因在于clone方法在Object类中的修饰符是protected,而Cloneable接口和Object处于同一个包下,熟悉修饰符的都知道protected的权限限定在同一个包下或者其子类。Cloneable和Object同属于一个包,Cloneable自然能继承clone方法,继承了Cloneable接口的成为了它的子类同样也就继承了clone方法。
相关文章推荐
- Effective Java(什么是clone、什么是“深拷贝”和“浅拷贝”)
- Effective Java(什么是clone、什么是“深拷贝”和“浅拷贝”)
- Effective Java(什么是clone、什么是“深拷贝”和“浅拷贝”)
- Effective Java(什么是clone、什么是“深拷贝”和“浅拷贝”)
- Effective Java(什么是clone、什么是“深拷贝”和“浅拷贝”)
- Effective Java(什么是clone、什么是“深拷贝”和“浅拷贝”)
- Effective Java(什么是clone、什么是“深拷贝”和“浅拷贝”)
- Effective Java(什么是clone、什么是“深拷贝”和“浅拷贝”)
- Effective Java(什么是clone、什么是“深拷贝”和“浅拷贝”)
- Effective Java(什么是clone、什么是“深拷贝”和“浅拷贝”)
- Effective Java(什么是clone、什么是“深拷贝”和“浅拷贝”)
- 浅拷贝和深拷贝(谈谈java中的clone)
- Java中的clone方法之浅拷贝与深拷贝
- java的clone到底说了点什么事情
- 【面试】--java 基础Cloneable 最彻底的clone是什么?
- C#学习——Clone浅学:浅拷贝
- c++ 什么情况使用深拷贝什么情况使用浅拷贝
- 什么是浅拷贝和深拷贝
- 浅拷贝和深拷贝:有什么区别
- fork拷贝父进程的什么东西