JAVA中的Cloneable接口

前言

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/