引言:
在object基類中,有一個方法叫clone,產生一個前期對象的克隆,克隆對象是原對象的拷貝,由于引用類型的存在,有深克隆和淺克隆之分,若克隆對象中存在引用類型的屬性,深克隆會將此屬性完全拷貝一份,而淺克隆僅僅是拷貝一份此屬性的引用。首先看一下容易犯的幾個小問題
clone方法是object類的,并不是cloneable接口的,cloneable只是一個標記接口,標記接口是用用戶標記實現該接口的類具有某種該接口標記的功能,常見的標記接口有三個:serializable、cloneable、randomaccess,沒有實現cloneable接口,那么調用clone方法就會爆出clonenotsupportedexception異常。
object類中的clone方法是protected修飾的,這就表明我們在子類中不重寫此方法,就在子類外無法訪問,因為這個protected權限是僅僅能在object所在的包和子類能訪問的,這也驗證了子類重寫父類方法權限修飾符可以變大但不能變小的說法。
1
|
protected native object clone() throws clonenotsupportedexception; |
重寫clone方法,內部僅僅是調用了父類的clone方法,其實是為了擴大訪問權限,當然你可以把protected改為public,以后再繼承就不用重寫了。當然只是淺克隆的clone函數,深克隆就需要修改了。
1
2
3
4
|
@override protected object clone() throws clonenotsupportedexception { return super .clone(); } |
屬性是string的情況,string也是一個類,那string引用類型嗎?string的表現有的像基本類型,歸根到底就是因為string不可改變,克隆之后倆個引用指向同一個string,但當修改其中的一個,改的不是string的值,卻是新生成一個字符串,讓被修改的引用指向新的字符串。外表看起來就像基本類型一樣。
淺克隆:
淺克隆就是引用類型的屬性無法完全復制,類user中包含成績屬性mark,mark是由chinese和math等等組成的,淺克隆失敗的例子
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
|
class mark{ private int chinese; private int math; public mark( int chinese, int math) { this .chinese = chinese; this .math = math; } public void setchinese( int chinese) { this .chinese = chinese; } public void setmath( int math) { this .math = math; } @override public string tostring() { return "mark{" + "chinese=" + chinese + ", math=" + math + '}' ; } } public class user implements cloneable{ private string name; private int age; private mark mark; public user(string name, int age,mark mark) { this .name = name; this .age = age; this .mark = mark; } @override public string tostring() { return "user{" + "name='" + name + '\ '' + ", age=" + age + ", mark=" + mark + '}' ; } @override protected object clone() throws clonenotsupportedexception { return super .clone(); } public static void main(string[] args) throws clonenotsupportedexception { mark mark = new mark( 100 , 99 ); user user = new user( "user" , 22 ,mark); user userclone = (user) user.clone(); system.out.println( "原user:" +user); system.out.println( "克隆的user:" +userclone); //修改引用類型的mark屬性 user.mark.setmath( 60 ); system.out.println( "修改后的原user:" +user); system.out.println( "修改后的克隆user:" +userclone); } } |
輸出結果為:
原user:user{name='user', age=22, mark=mark{chinese=100, math=99}}
克隆的user:user{name='user', age=22, mark=mark{chinese=100, math=99}}
修改后的原user:user{name='user', age=22, mark=mark{chinese=100, math=60}}
修改后的克隆user:user{name='user', age=22, mark=mark{chinese=100, math=60}}
很清楚的看到user的mark更改后,被克隆的user也修改了。而要想不被影響,就需要深克隆了。
深克隆:
方式一:clone函數的嵌套調用
既然引用類型無法被完全克隆,那將引用類型也實現cloneable接口重寫clone方法,在user類中的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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
class mark implements cloneable{ private int chinese; private int math; public mark( int chinese, int math) { this .chinese = chinese; this .math = math; } public void setchinese( int chinese) { this .chinese = chinese; } public void setmath( int math) { this .math = math; } @override protected object clone() throws clonenotsupportedexception { return super .clone(); } @override public string tostring() { return "mark{" + "chinese=" + chinese + ", math=" + math + '}' ; } } public class user implements cloneable{ private string name; private int age; private mark mark; public user(string name, int age,mark mark) { this .name = name; this .age = age; this .mark = mark; } @override public string tostring() { return "user{" + "name='" + name + '\ '' + ", age=" + age + ", mark=" + mark + '}' ; } @override protected object clone() throws clonenotsupportedexception { user user = (user) super .clone(); user.mark = (mark) this .mark.clone(); return user; } public static void main(string[] args) throws clonenotsupportedexception { mark mark = new mark( 100 , 99 ); user user = new user( "user" , 22 ,mark); user userclone = (user) user.clone(); system.out.println( "原user:" +user); system.out.println( "克隆的user:" +userclone); //修改引用類型的mark屬性 user.mark.setmath( 60 ); system.out.println( "修改后的原user:" +user); system.out.println( "修改后的克隆user:" +userclone); } } |
輸出結果為:
原user:user{name='user', age=22, mark=mark{chinese=100, math=99}}
克隆的user:user{name='user', age=22, mark=mark{chinese=100, math=99}}
修改后的原user:user{name='user', age=22, mark=mark{chinese=100, math=60}}
修改后的克隆user:user{name='user', age=22, mark=mark{chinese=100, math=99}}
方式二:序列化
上一種方法已經足夠滿足我們的需要,但是如果類之間的關系很多,或者是有的屬性是數組呢,數組可無法實現cloneable接口(我們可以在clone方法中手動復制數組),但是每次都得手寫clone方法,很麻煩,而序列化方式只需要給每個類都實現一個serializable接口,也是標記接口,最后同序列化和反序列化操作達到克隆的目的(包括數組的復制)。序列化和反序列化的知識請參照下一篇
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
|
import java.io.*; class mark implements serializable { private int chinese; private int math; public mark( int chinese, int math) { this .chinese = chinese; this .math = math; } public void setchinese( int chinese) { this .chinese = chinese; } public void setmath( int math) { this .math = math; } @override public string tostring() { return "mark{" + "chinese=" + chinese + ", math=" + math + '}' ; } } public class user implements serializable{ private string name; private int age; private mark mark; public user(string name, int age,mark mark) { this .name = name; this .age = age; this .mark = mark; } @override public string tostring() { return "user{" + "name='" + name + '\ '' + ", age=" + age + ", mark=" + mark + '}' ; } public static void main(string[] args) throws ioexception, classnotfoundexception { mark mark = new mark( 100 , 99 ); user user = new user( "user" , 22 ,mark); bytearrayoutputstream bo = new bytearrayoutputstream(); objectoutputstream oo = new objectoutputstream(bo); oo.writeobject(user); //序列化 bytearrayinputstream bi = new bytearrayinputstream(bo.tobytearray()); objectinputstream oi = new objectinputstream(bi); user userclone = (user) oi.readobject(); //反序列化 system.out.println( "原user:" +user); system.out.println( "克隆的user:" +userclone); user.mark.setmath( 59 ); system.out.println( "修改后的原user:" +user); system.out.println( "修改后的克隆user:" +userclone); } } |
輸出結果:
原user:user{name='user', age=22, mark=mark{chinese=100, math=99}}
克隆的user:user{name='user', age=22, mark=mark{chinese=100, math=99}}
修改后的原user:user{name='user', age=22, mark=mark{chinese=100, math=60}}
修改后的克隆user:user{name='user', age=22, mark=mark{chinese=100, math=99}}
帶數組屬性的克隆
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
|
import java.io.*; import java.util.arrays; public class user implements serializable{ private string name; private int age; private int [] arr; public user(string name, int age, int [] arr) { this .name = name; this .age = age; this .arr = arr; } @override public string tostring() { return "user{" + "name='" + name + '\ '' + ", age=" + age + ", arr=" + arrays.tostring(arr) + '}' ; } public static void main(string[] args) throws ioexception, classnotfoundexception { int [] arr = { 1 , 2 , 3 , 4 , 5 , 6 }; user user = new user( "user" , 22 ,arr); bytearrayoutputstream bo = new bytearrayoutputstream(); objectoutputstream oo = new objectoutputstream(bo); oo.writeobject(user); //序列化 bytearrayinputstream bi = new bytearrayinputstream(bo.tobytearray()); objectinputstream oi = new objectinputstream(bi); user userclone = (user) oi.readobject(); //反序列化 system.out.println( "原user:" +user); system.out.println( "克隆的user:" +userclone); user.arr[ 1 ] = 9 ; system.out.println( "修改后的原user:" +user); system.out.println( "修改后的克隆user:" +userclone); } } |