程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java Object Cloning-Java對象克隆

Java Object Cloning-Java對象克隆

編輯:關於JAVA

1. OverIEw

在實際編程中,我們經常會遇到這樣一個情景:有一個對象A,存在屬性方法,現在需要一個和A完全相同的新對象B,並且B的任何改動都不會影響到A中的值。那麼,最常用的辦法就是對A進行克隆。

2. How to Clone

在Java.lang.Object中有一個clone方法,該方法的簽名如下:

Java代碼

  1. protected native Object clone() throws CloneNotSupportedException;

該方法返回一個Object實例的拷貝,該實例拷貝具有:

1)拷貝對象是一個新對象而不是一個原對象引用。

2)拷貝的對象中包含的是原有對象信息,而不是對象初始化的信息(也就是說,拷貝過程沒有調用構造函數)。

從第1條我們可以看出,拷貝的對象與原對象存在這樣一種關系:

Java代碼

  1. 1. x.clone() != x will be true
  2. 2. x.clone().getClass() == x.getClass() will be true, but these are not absolute requirements
  3. 3. x.clone().equals(x) will be true, but these are not absolute requirements

要實現克隆,必須滿足一下三點:

1)類必須實現Cloneable接口

2)類必須重載Object類中的clone()方法

3)重載的clone()方法中,必須顯示調用super.clone()。

我們看一下Cloneable接口的定義:

Java代碼

  1. public interface Cloneable { }

Cloneable接口是不包含任何方法的,僅僅表示一個標志(類似Serializable接口),而且這個標志也是針對Object類中clone()方法的,如果clone類沒有實現Cloneable接口,並調用了Object的clone()方法(也就是調用了super.Clone()方法),那麼Object的clone()方法就會拋出CloneNotSupportedException異常。

按照要求,我們寫一個克隆類的小例子:

Java代碼

  1. public class DummyClone implements Cloneable{
  2. public Object clone(){
  3. DummyClone dc = null;
  4. try {
  5. dc = (DummyClone)super.clone();
  6. } catch (CloneNotSupportedException e) {
  7. e.printStackTrace();
  8. }
  9. return dc;
  10. }
  11. }

3. Shadow Clone

首先,我們來看一個例子:ClassA沒有實現Cloneable接口。ClassB存在ClassA的實例,實現了Cloneable接口,並重載了clone方法。ClassC實例化一個ClassB對象,然後克隆一個對象。

Java代碼

  1. class ClassA {
  2. public int a;
  3. public void doubleA(){
  4. a = a * 2;
  5. }
  6. public ClassA(int aa){
  7. this.a = aa;
  8. }
  9. public String toString(){
  10. return Integer.toString(a);
  11. }
  12. }
  13. class ClassB implements Cloneable{
  14. public int b;
  15. public ClassA ca = new ClassA(11);
  16. public Object clone(){
  17. ClassB cb = null;
  18. try {
  19. cb = (ClassB)super.clone();
  20. } catch (CloneNotSupportedException e) {
  21. e.printStackTrace();
  22. }
  23. return cb;
  24. }
  25. }
  26. public class ClassC {
  27. public static void main(String[] args) {
  28. ClassB cb = new ClassB();
  29. cb.b = 222;
  30. System.out.println("before clone : cb.b = " + cb.b);
  31. System.out.println("before clone : cb.ca = " + cb.ca);
  32. //
  33. ClassB cb2 = (ClassB)cb.clone();
  34. cb2.b = 3333;
  35. cb2.ca.doubleA();
  36. System.out.println("========================");
  37. System.out.println("after clone : cb.b = " + cb.b);
  38. System.out.println("after clone : cb.ca = " + cb.ca);
  39. System.out.println("========================");
  40. System.out.println("after clone : cb2.b = " + cb2.b);
  41. System.out.println("after clone : cb2.ca = " + cb2.ca);
  42. }
  43. }

輸出結果如下:

Java代碼

  1. before clone : cb.b = 222
  2. before clone : cb.ca = 11
  3. ========================
  4. after clone : cb.b = 222
  5. after clone : cb.ca = 22
  6. ========================
  7. after clone : cb2.b = 3333
  8. after clone : cb2.ca = 22

從結果我們看到,int類型的b被完全的克隆了,而ClassA類型的ca卻沒有被克隆。因為克隆後cb2對b的賦值不會影響原有cb中b的值,而調用cb2.ca.doubleA()方法後,對cb2.ca的改變同時改變了cb.ca,表明cb2.ca與cb.ca僅僅指向同一個對象的不同引用。從中可以看出,調用Object類中clone()方法產生的效果是:先在內存中開辟一塊和原始對象一樣的空間,然後原樣拷貝原始對象中的內容。對基本數據類型,這樣的操作是沒有問題的,但對非基本類型變量clone後的變量和原始對象中相應的變量指向的是同一個對象。

這就是影子克隆。影子克隆,並沒有完整的完成克隆,有時候這並不是我們想要的結果。我們有時需要,就像列子中調用cb2.ca.doubleA()方法時,不會對原對象產生改變,這時候我們就需要deep clone;

4. Deep Clone

要實現深度克隆,在上面的例子基礎上,我們只需要這樣修改即可:

Java代碼

  1. class ClassA implements Cloneable{
  2. public int a;
  3. public void doubleA(){
  4. a = a * 2;
  5. }
  6. public ClassA(int aa){
  7. this.a = aa;
  8. }
  9. public String toString(){
  10. return Integer.toString(a);
  11. }
  12. public Object clone(){
  13. ClassA ca = null;
  14. try {
  15. ca = (ClassA)super.clone();
  16. } catch (CloneNotSupportedException e) {
  17. e.printStackTrace();
  18. }
  19. return ca;
  20. }
  21. }
  22. class ClassB implements Cloneable{
  23. public int b;
  24. public ClassA ca = new ClassA(11);
  25. public Object clone(){
  26. ClassB cb = null;
  27. try {
  28. cb = (ClassB)super.clone();
  29. } catch (CloneNotSupportedException e) {
  30. e.printStackTrace();
  31. }
  32. cb.ca = (ClassA)ca.clone();
  33. return cb;
  34. }
  35. }
  36. public class ClassC {
  37. public static void main(String[] args) {
  38. ClassB cb = new ClassB();
  39. cb.b = 222;
  40. System.out.println("before clone : cb.b = " + cb.b);
  41. System.out.println("before clone : cb.ca = " + cb.ca);
  42. //
  43. ClassB cb2 = (ClassB)cb.clone();
  44. cb2.b = 3333;
  45. cb2.ca.doubleA();
  46. System.out.println("========================");
  47. System.out.println("after clone : cb.b = " + cb.b);
  48. System.out.println("after clone : cb.ca = " + cb.ca);
  49. System.out.println("========================");
  50. System.out.println("after clone : cb2.b = " + cb2.b);
  51. System.out.println("after clone : cb2.ca = " + cb2.ca);
  52. }
  53. }

輸出結果:

Java代碼

  1. before clone : cb.b = 222
  2. before clone : cb.ca = 11
  3. ========================
  4. after clone : cb.b = 222
  5. after clone : cb.ca = 11
  6. ========================
  7. after clone : cb2.b = 3333
  8. after clone : cb2.ca = 22

基本的數據類型可以自動的實現深度的克隆,然而並不是所有的類都可以進行深度的克隆,就比如String類,其類定義為final,且沒有重載clone方法,我們就無法完成對String類的深度克隆。關於不可變對象的克隆,我們通常沒有必要去克隆。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved