激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術(shù)|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務(wù)器之家 - 編程語言 - JAVA教程 - Java編程實現(xiàn)對象克隆(復(fù)制)代碼詳解

Java編程實現(xiàn)對象克隆(復(fù)制)代碼詳解

2021-02-23 11:23萌小Q JAVA教程

這篇文章主要介紹了Java編程實現(xiàn)對象克隆(復(fù)制)代碼詳解,涉及了克隆的原因,如何實現(xiàn)克隆,克隆的一般步驟,深克隆與淺克隆的介紹等相關(guān)內(nèi)容,具有一定借鑒價值,需要的朋友可以參考下。

克隆,想必大家都有耳聞,世界上第一只克隆羊多莉就是利用細胞核移植技術(shù)將哺乳動物的成年體細胞培育出新個體,甚為神奇。其實在java中也存在克隆的概念,即實現(xiàn)對象的復(fù)制。

本文將嘗試介紹一些關(guān)于java中的克隆和一些深入的問題,希望可以幫助大家更好地了解克隆。

假如說你想復(fù)制一個簡單變量。很簡單:

?
1
2
int apples = 5;
int pears = apples;

不僅僅是int類型,其它七種原始數(shù)據(jù)類型(boolean,char,byte,short,float,double.long)同樣適用于該類情況。

但是如果你復(fù)制的是一個對象,情況就有些復(fù)雜了。

假設(shè)說我是一個beginner,我會這樣寫:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class student {
  private int number;
 
  public int getnumber() {
    return number;
  }
 
  public void setnumber(int number) {
    this.number = number;
  }
   
}
public class test {
   
  public static void main(string args[]) {
    student stu1 = new student();
    stu1.setnumber(12345);
    student stu2 = stu1;
     
    system.out.println("學(xué)生1:" + stu1.getnumber());
    system.out.println("學(xué)生2:" + stu2.getnumber());
  }
}

結(jié)果:

學(xué)生1:12345 
學(xué)生2:12345

這里我們自定義了一個學(xué)生類,該類只有一個number字段。

我們新建了一個學(xué)生實例,然后將該值賦值給stu2實例。(student stu2 = stu1;)

再看看打印結(jié)果,作為一個新手,拍了拍胸腹,對象復(fù)制不過如此,

難道真的是這樣嗎?

我們試著改變stu2實例的number字段,再打印結(jié)果看看:

?
1
2
3
4
stu2.setnumber(54321);
 
system.out.println("學(xué)生1:" + stu1.getnumber());
system.out.println("學(xué)生2:" + stu2.getnumber());

結(jié)果:

學(xué)生1:54321 
學(xué)生2:54321  

這就怪了,為什么改變學(xué)生2的學(xué)號,學(xué)生1的學(xué)號也發(fā)生了變化呢?

原因出在(stu2 = stu1) 這一句。該語句的作用是將stu1的引用賦值給stu2,

這樣,stu1和stu2指向內(nèi)存堆中同一個對象。如圖:

Java編程實現(xiàn)對象克隆(復(fù)制)代碼詳解

那么,怎樣才能達到復(fù)制一個對象呢?

是否記得萬類之王object。它有11個方法,有兩個protected的方法,其中一個為clone方法。

在java中所有的類都是缺省的繼承自java語言包中的object類的,查看它的源碼,你可以把你的jdk目錄下的src.zip復(fù)制到其他地方然后解壓,里面就是所有的源碼。發(fā)現(xiàn)里面有一個訪問限定符為protected的方法clone():

?
1
2
3
4
5
6
7
8
/*
creates and returns a copy of this object. the precise meaning of "copy" may depend on the class of the object.
the general intent is that, for any object x, the expression:
1) x.clone() != x will be true
2) x.clone().getclass() == x.getclass() will be true, but these are not absolute requirements.
3) x.clone().equals(x) will be true, this is not an absolute requirement.
*/
protected native object clone() throws clonenotsupportedexception;

仔細一看,它還是一個native方法,大家都知道native方法是非java語言實現(xiàn)的代碼,供java程序調(diào)用的,因為java程序是運行在jvm虛擬機上面的,要想訪問到比較底層的與操作系統(tǒng)相關(guān)的就沒辦法了,只能由靠近操作系統(tǒng)的語言來實現(xiàn)。

第一次聲明保證克隆對象將有單獨的內(nèi)存地址分配。
第二次聲明表明,原始和克隆的對象應(yīng)該具有相同的類類型,但它不是強制性的。
第三聲明表明,原始和克隆的對象應(yīng)該是平等的equals()方法使用,但它不是強制性的。

因為每個類直接或間接的父類都是object,因此它們都含有clone()方法,但是因為該方法是protected,所以都不能在類外進行訪問。

要想對一個對象進行復(fù)制,就需要對clone方法覆蓋。

為什么要克隆?

  大家先思考一個問題,為什么需要克隆對象?直接new一個對象不行嗎?

  答案是:克隆的對象可能包含一些已經(jīng)修改過的屬性,而new出來的對象的屬性都還是初始化時候的值,所以當(dāng)需要一個新的對象來保存當(dāng)前對象的“狀態(tài)”就靠clone方法了。那么我把這個對象的臨時屬性一個一個的賦值給我新new的對象不也行嘛?可以是可以,但是一來麻煩不說,二來,大家通過上面的源碼都發(fā)現(xiàn)了clone是一個native方法,就是快啊,在底層實現(xiàn)的。

  提個醒,我們常見的object a=new object();object b;b=a;這種形式的代碼復(fù)制的是引用,即對象在內(nèi)存中的地址,a和b對象仍然指向了同一個對象。

  而通過clone方法賦值的對象跟原來的對象時同時獨立存在的。

如何實現(xiàn)克隆

先介紹一下兩種不同的克隆方法,淺克隆(shallowclone)和深克隆(deepclone)。

在java語言中,數(shù)據(jù)類型分為值類型(基本數(shù)據(jù)類型)和引用類型,值類型包括int、double、byte、boolean、char等簡單數(shù)據(jù)類型,引用類型包括類、接口、數(shù)組等復(fù)雜類型。淺克隆和深克隆的主要區(qū)別在于是否支持引用類型的成員變量的復(fù)制,下面將對兩者進行詳細介紹。

一般步驟是(淺克隆):

1. 被復(fù)制的類需要實現(xiàn)clonenable接口(不實現(xiàn)的話在調(diào)用clone方法會拋出clonenotsupportedexception異常), 該接口為標(biāo)記接口(不含任何方法)

2. 覆蓋clone()方法,訪問修飾符設(shè)為public。方法中調(diào)用super.clone()方法得到需要的復(fù)制對象。(native為本地方法)

下面對上面那個方法進行改造:

?
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
class student implements cloneable{
    private int number;
    public int getnumber() {
        return number;
    }
    public void setnumber(int number) {
        this.number = number;
    }
    @override
      public object clone() {
        student stu = null;
        try{
            stu = (student)super.clone();
        }
        catch(clonenotsupportedexception e) {
            e.printstacktrace();
        }
        return stu;
    }
}
public class test {
    public static void main(string args[]) {
        student stu1 = new student();
        stu1.setnumber(12345);
        student stu2 = (student)stu1.clone();
        system.out.println("學(xué)生1:" + stu1.getnumber());
        system.out.println("學(xué)生2:" + stu2.getnumber());
        stu2.setnumber(54321);
        system.out.println("學(xué)生1:" + stu1.getnumber());
        system.out.println("學(xué)生2:" + stu2.getnumber());
    }
}

結(jié)果:

學(xué)生1:12345 
學(xué)生2:12345 
學(xué)生1:12345 
學(xué)生2:54321

如果你還不相信這兩個對象不是同一個對象,那么你可以看看這一句:

?
1
system.out.println(stu1 == stu2); // false

上面的復(fù)制被稱為淺克隆。

還有一種稍微復(fù)雜的深度復(fù)制:

我們在學(xué)生類里再加一個address類。

?
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
class address {
    private string add;
    public string getadd() {
        return add;
    }
    public void setadd(string add) {
        this.add = add;
    }
}
class student implements cloneable{
    private int number;
    private address addr;
    public address getaddr() {
        return addr;
    }
    public void setaddr(address addr) {
        this.addr = addr;
    }
    public int getnumber() {
        return number;
    }
    public void setnumber(int number) {
        this.number = number;
    }
    @override
      public object clone() {
        student stu = null;
        try{
            stu = (student)super.clone();
        }
        catch(clonenotsupportedexception e) {
            e.printstacktrace();
        }
        return stu;
    }
}
public class test {
    public static void main(string args[]) {
        address addr = new address();
        addr.setadd("杭州市");
        student stu1 = new student();
        stu1.setnumber(123);
        stu1.setaddr(addr);
        student stu2 = (student)stu1.clone();
        system.out.println("學(xué)生1:" + stu1.getnumber() + ",地址:" + stu1.getaddr().getadd());
        system.out.println("學(xué)生2:" + stu2.getnumber() + ",地址:" + stu2.getaddr().getadd());
    }
}

結(jié)果:

學(xué)生1:123,地址:杭州市 
學(xué)生2:123,地址:杭州市 

乍一看沒什么問題,真的是這樣嗎?

我們在main方法中試著改變addr實例的地址。

?
1
2
3
4
addr.setadd("西湖區(qū)");
 
system.out.println("學(xué)生1:" + stu1.getnumber() + ",地址:" + stu1.getaddr().getadd());
system.out.println("學(xué)生2:" + stu2.getnumber() + ",地址:" + stu2.getaddr().getadd());

結(jié)果:

?
1
2
3
4
學(xué)生1:123,地址:杭州市
學(xué)生2:123,地址:杭州市
學(xué)生1:123,地址:西湖區(qū)
學(xué)生2:123,地址:西湖區(qū)

這就奇怪了,怎么兩個學(xué)生的地址都改變了?

原因是淺復(fù)制只是復(fù)制了addr變量的引用,并沒有真正的開辟另一塊空間,將值復(fù)制后再將引用返回給新對象。

所以,為了達到真正的復(fù)制對象,而不是純粹引用復(fù)制。我們需要將address類可復(fù)制化,并且修改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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package abc;
 
class address implements cloneable {
  private string add;
 
  public string getadd() {
    return add;
  }
 
  public void setadd(string add) {
    this.add = add;
  }
   
  @override
  public object clone() {
    address addr = null;
    try{
      addr = (address)super.clone();
    }catch(clonenotsupportedexception e) {
      e.printstacktrace();
    }
    return addr;
  }
}
 
class student implements cloneable{
  private int number;
 
  private address addr;
   
  public address getaddr() {
    return addr;
  }
 
  public void setaddr(address addr) {
    this.addr = addr;
  }
 
  public int getnumber() {
    return number;
  }
 
  public void setnumber(int number) {
    this.number = number;
  }
   
  @override
  public object clone() {
    student stu = null;
    try{
      stu = (student)super.clone();  //淺復(fù)制
    }catch(clonenotsupportedexception e) {
      e.printstacktrace();
    }
    stu.addr = (address)addr.clone();  //深度復(fù)制
    return stu;
  }
}
public class test {
   
  public static void main(string args[]) {
     
    address addr = new address();
    addr.setadd("杭州市");
    student stu1 = new student();
    stu1.setnumber(123);
    stu1.setaddr(addr);
     
    student stu2 = (student)stu1.clone();
     
    system.out.println("學(xué)生1:" + stu1.getnumber() + ",地址:" + stu1.getaddr().getadd());
    system.out.println("學(xué)生2:" + stu2.getnumber() + ",地址:" + stu2.getaddr().getadd());
     
    addr.setadd("西湖區(qū)");
     
    system.out.println("學(xué)生1:" + stu1.getnumber() + ",地址:" + stu1.getaddr().getadd());
    system.out.println("學(xué)生2:" + stu2.getnumber() + ",地址:" + stu2.getaddr().getadd());
  }
}

結(jié)果:

學(xué)生1:123,地址:杭州市
學(xué)生2:123,地址:杭州市
學(xué)生1:123,地址:西湖區(qū)
學(xué)生2:123,地址:杭州市

這樣結(jié)果就符合我們的想法了。

最后我們可以看看api里其中一個實現(xiàn)了clone方法的類:

java.util.date:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
 * return a copy of this object.
 */
public object clone() {
  date d = null;
  try {
    d = (date)super.clone();
    if (cdate != null) {
      d.cdate = (basecalendar.date) cdate.clone();
    }
  } catch (clonenotsupportedexception e) {} // won't happen
  return d;
}

該類其實也屬于深度復(fù)制。

淺克隆和深克隆

1、淺克隆

在淺克隆中,如果原型對象的成員變量是值類型,將復(fù)制一份給克隆對象;如果原型對象的成員變量是引用類型,則將引用對象的地址復(fù)制一份給克隆對象,也就是說原型對象和克隆對象的成員變量指向相同的內(nèi)存地址。

簡單來說,在淺克隆中,當(dāng)對象被復(fù)制時只復(fù)制它本身和其中包含的值類型的成員變量,而引用類型的成員對象并沒有復(fù)制。

Java編程實現(xiàn)對象克隆(復(fù)制)代碼詳解

在java語言中,通過覆蓋object類的clone()方法可以實現(xiàn)淺克隆。

2、深克隆

在深克隆中,無論原型對象的成員變量是值類型還是引用類型,都將復(fù)制一份給克隆對象,深克隆將原型對象的所有引用對象也復(fù)制一份給克隆對象。

簡單來說,在深克隆中,除了對象本身被復(fù)制外,對象所包含的所有成員變量也將復(fù)制。

Java編程實現(xiàn)對象克隆(復(fù)制)代碼詳解

在java語言中,如果需要實現(xiàn)深克隆,可以通過覆蓋object類的clone()方法實現(xiàn),也可以通過序列化(serialization)等方式來實現(xiàn)。

(如果引用類型里面還包含很多引用類型,或者內(nèi)層引用類型的類里面又包含引用類型,使用clone方法就會很麻煩。這時我們可以用序列化的方式來實現(xiàn)對象的深克隆。)

序列化就是將對象寫到流的過程,寫到流中的對象是原有對象的一個拷貝,而原對象仍然存在于內(nèi)存中。通過序列化實現(xiàn)的拷貝不僅可以復(fù)制對象本身,而且可以復(fù)制其引用的成員對象,因此通過序列化將對象寫到一個流中,再從流里將其讀出來,可以實現(xiàn)深克隆。需要注意的是能夠?qū)崿F(xiàn)序列化的對象其類必須實現(xiàn)serializable接口,否則無法實現(xiàn)序列化操作。

擴展

java語言提供的cloneable接口和serializable接口的代碼非常簡單,它們都是空接口,這種空接口也稱為標(biāo)識接口,標(biāo)識接口中沒有任何方法的定義,其作用是告訴jre這些接口的實現(xiàn)類是否具有某個功能,如是否支持克隆、是否支持序列化等。

解決多層克隆問題

如果引用類型里面還包含很多引用類型,或者內(nèi)層引用類型的類里面又包含引用類型,使用clone方法就會很麻煩。這時我們可以用序列化的方式來實現(xiàn)對象的深克隆。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class outer implements serializable{
 private static final long serialversionuid = 369285298572941l; //最好是顯式聲明id
 public inner inner;
 //discription:[深度復(fù)制方法,需要對象及對象所有的對象屬性都實現(xiàn)序列化] 
 public outer myclone() {
   outer outer = null;
   try { // 將該對象序列化成流,因為寫在流里的是對象的一個拷貝,而原對象仍然存在于jvm里面。所以利用這個特性可以實現(xiàn)對象的深拷貝
     bytearrayoutputstream baos = new bytearrayoutputstream();
     objectoutputstream oos = new objectoutputstream(baos);
     oos.writeobject(this);
      // 將流序列化成對象
     bytearrayinputstream bais = new bytearrayinputstream(baos.tobytearray());
     objectinputstream ois = new objectinputstream(bais);
     outer = (outer) ois.readobject();
   } catch (ioexception e) {
     e.printstacktrace();
   } catch (classnotfoundexception e) {
     e.printstacktrace();
   }
   return outer;
 }
}

inner也必須實現(xiàn)serializable,否則無法序列化:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
public class inner implements serializable{
 private static final long serialversionuid = 872390113109l; //最好是顯式聲明id
 public string name = "";
 
 public inner(string name) {
   this.name = name;
 }
 
 @override
 public string tostring() {
   return "inner的name值為:" + name;
 }
}

這樣也能使兩個對象在內(nèi)存空間內(nèi)完全獨立存在,互不影響對方的值。

總結(jié)

實現(xiàn)對象克隆有兩種方式:

  1). 實現(xiàn)cloneable接口并重寫object類中的clone()方法;

  2). 實現(xiàn)serializable接口,通過對象的序列化和反序列化實現(xiàn)克隆,可以實現(xiàn)真正的深度克隆。

注意:基于序列化和反序列化實現(xiàn)的克隆不僅僅是深度克隆,更重要的是通過泛型限定,可以檢查出要克隆的對象是否支持序列化,這項檢查是編譯器完成的,不是在運行時拋出異常,這種是方案明顯優(yōu)于使用object類的clone方法克隆對象。讓問題在編譯的時候暴露出來總是優(yōu)于把問題留到運行時。

以上就是本文關(guān)于java編程實現(xiàn)對象克隆(復(fù)制)代碼詳解的全部內(nèi)容,希望對大家有所幫助。感興趣的來朋友可以繼續(xù)參閱本站其他相關(guān)專題,如有不足之處,歡迎留言指出。

原文鏈接:https://www.cnblogs.com/Qian123/p/5710533.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 精品一区二区久久久 | 免费看欧美一级特黄a毛片 九色com | 久久爽精品区穿丝袜 | 天使萌一区二区三区免费观看 | 国产 视频 一区二区 | 国产精品999在线 | 亚洲精品午夜在线 | 国产精品久久久久久影院8一贰佰 | 日本欧美一区二区三区视频麻豆 | 中国的免费的视频 | 免费一级毛片在线播放不收费 | 午夜视频在线免费观看 | 狠狠操天天操 | 成人在线免费视频观看 | 亚洲第一激情网 | 亚洲九色 | 一级尻逼视频 | 国产精品免费看 | 欧美高清视频一区 | 天天操天天干天天操 | 成人免费淫片视频软件 | 久久久久久久黄色片 | 亚洲第一精品在线 | 久久aⅴ国产欧美74aaa | 成人做爰高潮片免费视频韩国 | 日本欧美一区二区三区在线观看 | 免费一级电影 | 国产在线精品一区二区三区不卡 | 久久老司机 | 欧美a级一区二区 | 亚洲精品欧美在线 | 国产成人精品免费视频大全办公室 | 激情网站免费观看 | 91精选视频在线观看 | 免费在线观看中文字幕 | 中文字幕国产日韩 | 深夜福利久久久 | 欧美性生活视频免费 | 福利免费观看 | 久久午夜免费视频 | 成年免费视频黄网站在线观看 |