前言
Java中的 Cloneable
并没有具体的方法,那为什么我们需要实现Cloneable
这个空接口才能使用clone
方法呢?
这里有两方面的原因:
- 首先,可能有一个上溯造型句柄指向一个基础类型,而且不知道它是否真的能克隆那个对象。在这种情况下,可以用
instanceof
关键字来判断句柄是否确实实现同一个能克隆的对象连接。
- 第二个原因是考虑到我们可能不愿所有的对象都能克隆。所以
Object.clone()
会验证一个类是否真的是实现了Cloneable
接口。若没有实现,则抛出CloneNotSupportedException
异常。
成功的克隆
首先对象中的clone()
方法必须要是public
的,其次要实现Cloneable
接口,我们最好用一个try-catch
块把super.clone()
代码封装起来,试图捕获一个应当永不出现的异常。
super.clone()
是Object.clone()
操作,之所以能调用,因为Object
中的clone()
方法是protected
的,所以衍生类都可以访问(Object
是顶级父类)。
Object.clone()
操作会检查原来对象有多大,再为新的对象腾出足够多的内容,将所有二进制位从原来的对象,复制到新的对象爱嗯。这叫“按位复制”。
简单的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| public class CloneDemo {
@Test public void t() { Cat cat = new Cat(); Object catClone = cat.clone(); if (Objects.nonNull(catClone)) { System.out.println("catClone:" + catClone.toString()); ((Cat) catClone).name = "cat"; System.out.println("cat:" + cat.toString()); System.out.println("catClone:" + catClone.toString());
} } }
class Cat implements Cloneable { String name = "keep";
static { System.out.println("load Cat"); }
@Override protected Object clone() { try { return super.clone(); } catch (Exception e) { System.err.println(e.getMessage()); } return null; }
@Override public String toString() { return "Cat{" + "name='" + name + '\'' + '}'; } }
|
对象中包含其它对象,其它对象也会被Clone
吗?
结论,Object.clone()
不会自动克隆所有句柄指向的目标,是“浅层复制”。
对象中的对象没有实现Cloneable
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| public class CloneDemo { @Test public void t() { Cat cat = new Cat(); cat.dog = new Dog(); Object catClone = cat.clone(); if (Objects.nonNull(catClone)) { System.out.println("catClone:" + catClone.toString()); ((Cat) catClone).name = "cat"; ((Cat) catClone).dog.name = "xiao dog"; System.out.println("改变Clone后的对象值=>"); System.out.println("cat:" + cat.toString()); System.out.println("catClone:" + catClone.toString()); } } }
class Cat implements Cloneable { String name = "keep"; Dog dog;
static { System.out.println("load Cat"); }
@Override protected Object clone() { try { return super.clone(); } catch (Exception e) { System.err.println(e.getMessage()); } return null; }
@Override public String toString() { return "Cat{" + "name='" + name + '\'' + ", dog=" + dog + '}'; } }
class Dog { String name = "dog";
static { System.out.println("load Dog"); }
@Override public String toString() { return "Dog{" + "name='" + name + '\'' + '}'; } }
|
输出结果
1 2 3 4 5 6
| load Cat load Dog catClone:Cat{name='keep', dog=Dog{name='dog'}} 改变Clone后的对象值=> cat:Cat{name='keep', dog=Dog{name='xiao dog'}} catClone:Cat{name='cat', dog=Dog{name='xiao dog'}}
|
这里我们发现,Dog
对象还是引用,并没有被Clone
成功。
对象中的对象实现了Cloneable
接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| public class CloneDemo { @Test public void t() { Cat cat = new Cat(); cat.dog = new Dog(); Object catClone = cat.clone(); if (Objects.nonNull(catClone)) { System.out.println("catClone:" + catClone.toString()); ((Cat) catClone).name = "cat"; ((Cat) catClone).dog.name = "xiao dog"; System.out.println("改变Clone后的对象值=>"); System.out.println("cat:" + cat.toString()); System.out.println("catClone:" + catClone.toString());
} } }
class Cat implements Cloneable { String name = "keep"; Dog dog;
static { System.out.println("load Cat"); }
@Override protected Object clone() { try { return super.clone(); } catch (Exception e) { System.err.println(e.getMessage()); } return null; }
@Override public String toString() { return "Cat{" + "name='" + name + '\'' + ", dog=" + dog + '}'; } }
class Dog implements Cloneable { String name = "dog";
static { System.out.println("load Dog"); }
@Override protected Object clone() { System.out.println(" Dog clone "); try { return super.clone(); } catch (Exception e) { System.err.println(e.getMessage()); } return null; }
@Override public String toString() { return "Dog{" + "name='" + name + '\'' + '}'; } }
|
输出结果
1 2 3 4 5 6
| load Cat load Dog catClone:Cat{name='keep', dog=Dog{name='dog'}} 改变Clone后的对象值=> cat:Cat{name='keep', dog=Dog{name='xiao dog'}} catClone:Cat{name='cat', dog=Dog{name='xiao dog'}}
|
在这里我们发现,并没有任何区别,实际上上面已经说道“按位复制”,那么Dog
对象在Cat
对象中只是引用,不存在Dog
内存,所以就不存在复制Dog
内存。
本文地址: https://github.com/maxzhao-it/blog/post/4368/