最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入了解Java對象的克隆

 更新時間:2020年08月19日 11:58:11   作者:弗蘭克的貓  
這篇文章主要介紹了Java對象的克隆的相關資料,幫助大家更好的理解和學習Java,感興趣的朋友可以了解下

  今天要介紹一個概念,對象的克隆。本篇有一定難度,請先做好心理準備。看不懂的話可以多看兩遍,還是不懂的話,可以在下方留言,我會看情況進行修改和補充。

  克隆,自然就是將對象重新復制一份,那為什么要用克隆呢?什么時候需要使用呢?先來看一個小栗子:

  簡單起見,我們這里用的是Goods類的簡單版本。

public class Goods {
  private String title;
  private double price;
  
  public Goods(String aTitle, double aPrice){
    title = aTitle;
    price = aPrice;
  }

  public void setPrice(double price) {
    this.price = price;
  }

  public void setTitle(String title) {
    this.title = title;
  }
  //用于打印輸出商品信息
  public void print(){
    System.out.println("Title:"+title+" Price:"+price);
  }
}

  然后我們來使用這個類。

public class GoodsTest {
  public static void main(String[] args){
    Goods goodsA = new Goods("GoodsA",20);
    Goods goodsB = goodsA;
    System.out.println("Before Change:");
    goodsA.print();
    goodsB.print();

    goodsB.setTitle("GoodsB");
    goodsB.setPrice(50);
    System.out.println("After Change:");
    goodsA.print();
    goodsB.print();
  }
}

  我們創(chuàng)建了一個Goods對象賦值給變量goodsA,然后又創(chuàng)建了一個Goods變量,并把goodsA賦值給它,先調用Goods的print方法輸出這兩個變量中的信息,然后調用Goods類中的setTitle和setPrice方法來修改goodsB中的對象內容,再輸出兩個變量中的信息,下面是輸出:

Before Change:
Title:GoodsA Price:20.0
Title:GoodsA Price:20.0
After Change:
Title:GoodsB Price:50.0
Title:GoodsB Price:50.0

  這里我們發(fā)現了靈異事,我們明明修改的是goodsB的內容,可是goodsA的內容也同樣發(fā)生了改變,這究竟是為什么呢?別心急,且聽我慢慢道來。

  在Java語言中,數據類型分為值類型(基本數據類型)和引用類型,值類型包括int、double、byte、boolean、char等簡單數據類型,引用類型包括類、接口、數組等復雜類型。使用等號賦值都是進行值傳遞的,如將一個整數型變量賦值給另一個整數型變量,那么后者將存儲前者的值,也就是變量中的整數值,對于基本類型如int,double,char等是沒有問題的,但是對于對象,則又是另一回事了,這里的goodsA和goodsB都是Goods類對象的變量,但是它們并沒有存儲Goods類對象的內容,而是存儲了它的地址,也就相當于C++中的指針,如果對于指針不了解,那我就再舉個栗子好了。我們之前舉過一個栗子,把計算機比作是倉庫管理員,內存比作是倉庫,你要使用什么類型的變量,就需要先登記,然后管理員才會把東西給你,但如果是給你分配一座房子呢?這時候不是把房子搬起來放到登記簿粒,而是登記下房子的地址,這里的地址就是我們的類對象變量里記錄的內容,所以,當我們把一個類對象變量賦值給另一個類對象變量,如goodsB = goodsA時,實際上只是把A指向的對象地址賦值給了B,這樣B也同樣指向這個地址,所以這時候,goodsA和goodsB操作的是同一個對象。

  所以,如果只是簡單的賦值的話,之后對于goodsA和goodsB的操作都將影響同一個對象,這顯然不是我們的本意。也許你還會問,直接再new一個對象不就好了,確實如此,但有時候,如果我們需要保存一個goodsA的副本,那就不僅僅要new一個對象,還需要進行一系列賦值操作才能將我們的新對象設置成跟goodsA對象一樣,而且Goods類越復雜,這個操作將會越繁瑣,另外使用clone方法還進行本地優(yōu)化,效率上也會快很多,總而言之,就是簡單粗暴。

  那如何使用克隆呢?這里我們就要介紹我們牛逼哄哄的Object類了,所有的類都是Object類的子類,雖然我們并沒有顯式聲明繼承關系,但所有類都難逃它的魔掌,它有兩個protected方法,其中一個就是clone方法。

  下面我來展示一波正確的騷操作:

//要使用克隆方法需要實現Cloneable接口
public class Goods implements Cloneable{
  private String title;
  private double price;

  public Goods(String aTitle, double aPrice){
    title = aTitle;
    price = aPrice;
  }

  public void setPrice(double price) {
    this.price = price;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public void print(){
    System.out.println("Title:"+title+" Price:"+price);
  }

  //這里重載了接口的clone方法
  @Override
  protected Object clone(){
    Goods g = null;    //這里是異常處理的語句塊,可以先不用了解,只要知道是這樣使用就好,之后的文章中會有詳細的介紹
    try{
      g = (Goods)super.clone();
    }catch (CloneNotSupportedException e){
      System.out.println(e.toString());
    }
    return g;
  }
}

  其實修改的地方只有兩個,一個是定義類的時候實現了Cloneable接口,關于接口的知識在之后會有詳細說明,這里只要簡單理解為是一種規(guī)范就行了,然后我們重載了clone方法,并在里面調用了父類也就是(Object)的clone方法??梢钥吹轿覀儾]有new一個新的對象,而是使用父類的clone方法進行克隆,關于try catch的知識這里不做過多介紹,之后會有文章做詳細說明,這里只需要理解為try語句塊里是一個可能發(fā)生錯誤的代碼,catch會捕獲這種錯誤并進行處理。

  接下來我們再使用這個類的克隆方法:

public class GoodsTest {
  public static void main(String[] args){
    Goods goodsA = new Goods("GoodsA",20);
    Goods goodsB = (Goods)goodsA.clone();
    System.out.println("Before Change:");
    goodsA.print();
    goodsB.print();

    goodsB.setTitle("GoodsB");
    goodsB.setPrice(50);
    System.out.println("After Change:");
    goodsA.print();
    goodsB.print();
  }
}

  我們僅僅是把賦值改成了調用goodsA的clone方法并進行類型轉換。輸出如下:

Before Change:
Title:GoodsA Price:20.0
Title:GoodsA Price:20.0
After Change:
Title:GoodsA Price:20.0
Title:GoodsB Price:50.0

  看,這樣不就達到我們目的了嗎?是不是很簡單?

  但是別高興的太早,關于克隆,還有一點內容需要介紹。

  克隆分為淺克隆和深克隆。我們上面使用的只是淺克隆,那兩者有什么區(qū)別呢?這里再舉一個栗子,使用的是簡化版的Cart類:

public class Cart implements Cloneable{
  //實例域
  Goods goodsList = new Goods("",0);//簡單起見,這里只放了一個商品
  double budget = 0.0;//預算

  //構造函數
  public Cart(double aBudget){
    budget = aBudget;
  }

  //獲取預算
  public double getBudget() {
    return budget;
  }

  //修改預算
  public void setBudget(double aBudget) {
    budget = aBudget;
  }

  //這里只是簡單的將商品進行了賦值
  public void addGoods(Goods goods){
    goodsList = (Goods) goods.clone();
  }

  //這是為了演示加上的代碼,僅僅將商品標題修改成新標題
  public void changeGoodsTitle(String title){
    goodsList.setTitle(title);
  }

  //打印商品信息
  public void print(){
    System.out.print("Cart內的預算信息:"+budget+" 商品信息:");
    goodsList.print();
  }

  //重載clone方法
  @Override
  protected Object clone(){
    Cart c = null;
    try{
      c = (Cart)super.clone();
    }catch (CloneNotSupportedException e ){
      e.printStackTrace();
    }
    return c;
  }
}

  這里將goodsList由數組改成了單個對象變量,僅僅用于演示方便,還增加了一個changeGoodsTitle方法,用于將商品的標題修改成另一個標題,接下來修改一下GoodsTest類:

public class GoodsTest {
  public static void main(String[] args){
    Goods goodsA = new Goods("GoodsA",20);//新建一個商品對象
    Cart cartA = new Cart(5000);//新建一個購物車對象
    cartA.addGoods(goodsA);//添加商品
    Cart cartB = (Cart) cartA.clone();//使用淺克隆
     //輸出修改前信息
    System.out.println("Before Change:");
    cartA.print();
    cartB.print();
     //修改購物車A中的商品標題
    cartA.changeGoodsTitle("NewTitle");     //重新輸出修改后的信息
    System.out.println("After Change:");
    cartA.print();
    cartB.print();
  }
}

  輸出信息:

Before Change:
Cart內的預算信息:5000.0 商品信息:Title:GoodsA Price:20.0
Cart內的預算信息:5000.0 商品信息:Title:GoodsA Price:20.0
After Change:
Cart內的預算信息:5000.0 商品信息:Title:NewTitle Price:20.0
Cart內的預算信息:5000.0 商品信息:Title:NewTitle Price:20.0

  我們發(fā)現,雖然我們調用的是cartA中的方法修改購物車A中的商品信息,但購物車B中的信息同樣被修改了,這是因為使用淺克隆模式的時候,成員變量如果是對象等復雜類型時,僅僅使用的是值拷貝,就跟我們之前介紹的那樣,所以cartB雖然是cartA的一個拷貝,但是它們的成員變量goodsList卻共用一個對象,這樣就藕斷絲連了,顯然不是我們想要的效果,這時候就需要使用深拷貝了,只需要將Cart類的clone方法修改一下即可:

  @Override
  protected Object clone(){
    Cart c = null;
    try{
      c = (Cart)super.clone();
      c.goodsList = (Goods) goodsList.clone();//僅僅添加了這段代碼,將商品對象也進行了克隆
    }catch (CloneNotSupportedException e ){
      e.printStackTrace();
    }
    return c;
  }

 現在再來運行一下:

Before Change:
Cart內的預算信息:5000.0 商品信息:Title:GoodsA Price:20.0
Cart內的預算信息:5000.0 商品信息:Title:GoodsA Price:20.0
After Change:
Cart內的預算信息:5000.0 商品信息:Title:NewTitle Price:20.0
Cart內的預算信息:5000.0 商品信息:Title:GoodsA Price:20.0

  這樣就得到了我們想要的結果了。

  這樣,對象的拷貝就講完了。

  嗎?

  哈哈哈哈,不要崩潰,并沒有,還有一種更復雜的情況,那就是當你的成員變量里也包含引用類型的時候,比如Cart類中有一個CartB類的成員變量,CartB類中同樣存在引用類型的成員變量,這時候,就存在多層克隆的問題了。這里再介紹一個騷操作,只需要了解即可,那就是序列化對象。操作如下:

import java.io.*;

public class Cart implements Serializable{
  //實例域
  Goods goodsList = new Goods("",0);//簡單起見,這里只放了一個商品
  double budget = 0.0;//預算

  //構造函數
  public Cart(double aBudget){
    budget = aBudget;
  }

  //獲取預算
  public double getBudget() {
    return budget;
  }

  //修改預算
  public void setBudget(double aBudget) {
    budget = aBudget;
  }

  //這里只是簡單的將商品進行了賦值
  public void addGoods(Goods goods){
    goodsList = (Goods) goods.clone();
  }

  //這是為了演示加上的代碼,僅僅將商品標題修改成新標題
  public void changeGoodsTitle(String title){
    goodsList.setTitle(title);
  }

  //打印商品信息
  public void print(){
    System.out.print("Cart內的預算信息:"+budget+" 商品信息:");
    goodsList.print();
  }
  //這里是主要是騷操作
  public Object deepClone() throws IOException, OptionalDataException,ClassNotFoundException {
    // 將對象寫到流里
    ByteArrayOutputStream bo = new ByteArrayOutputStream();
    ObjectOutputStream oo = new ObjectOutputStream(bo);
    oo.writeObject(this);
    // 從流里讀出來
    ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
    ObjectInputStream oi = new ObjectInputStream(bi);
    return (oi.readObject());
  }
}

  關于這種方法我就不多做介紹了,大家只需要知道有這樣一種方法就行了,以后如果遇到了需要使用這種情況,就知道該怎樣處理了。

  這里總結一下,對象的克隆就是把一個對象的當前狀態(tài)重新拷貝一份到另一個新對象中,兩個對象變量指向不同的對象,淺克隆僅僅調用super.clone()方法,對成員變量也只是簡單的值拷貝,所以當成員變量中有數組,對象等復雜類型的時候,就會存在藕斷絲連的混亂關系,深拷貝不僅僅調用super.clone()方法進行對象拷貝,將對象中的復雜類型同樣進行了拷貝,這樣兩個對象就再無瓜葛,井水不犯河水了。

  至此,對象的克隆就真正的結束了,歡迎大家繼續(xù)關注!如有不懂的問題可以留言。也歡迎各位大佬來批評指正。喜歡我的教程的話記得動動小手點下推薦,也歡迎關注我的博客。

以上就是深入了解Java對象的克隆的詳細內容,更多關于Java 克隆的資料請關注腳本之家其它相關文章!

相關文章

  • 深入了解Java中Cookie和Session的區(qū)別

    深入了解Java中Cookie和Session的區(qū)別

    會話跟蹤是Web程序中常用的技術,用來跟蹤用戶的整個會話,常用的會話跟蹤技術是Cookie與Session,本文就詳細的介紹一下Java中Cookie和Session的區(qū)別,感興趣的可以了解一下
    2023-06-06
  • java對象強轉成object的方法實現

    java對象強轉成object的方法實現

    在 Java 編程中,有時候我們需要將一個具體的對象強制轉換成 Object 類型,本文主要介紹了java對象強轉成object的方法實現,具有一定的參考價值,感興趣的可以了解一下
    2024-03-03
  • 關于MyBaties的基本配置標簽總結

    關于MyBaties的基本配置標簽總結

    今天給大家?guī)淼氖顷P于MyBaties基礎的相關知識,文章圍繞著MyBaties的基本配置標簽展開,文中有非常詳細的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • Java中使用opencv的問題

    Java中使用opencv的問題

    這篇文章主要介紹了Java中使用opencv的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • 淺談Java編程中的synthetic關鍵字

    淺談Java編程中的synthetic關鍵字

    這篇文章主要介紹了淺談Java編程中的synthetic關鍵字的相關內容,包括其簡單的介紹和實例,需要的朋友可以了解下。
    2017-09-09
  • 解決IDEA報錯java無效的目標發(fā)行版:22

    解決IDEA報錯java無效的目標發(fā)行版:22

    在使用IDEA編譯項目時,可能會遇到JDK版本不一致的錯誤,這篇文章主要介紹了解決IDEA報錯java無效的目標發(fā)行版:22的相關資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2024-10-10
  • Mybatis中的@Select、foreach用法

    Mybatis中的@Select、foreach用法

    這篇文章主要介紹了Mybatis中的@Select、foreach用法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • Java Lambda表達式和函數式接口實例分析

    Java Lambda表達式和函數式接口實例分析

    這篇文章主要介紹了Java Lambda表達式和函數式接口,結合實例形式分析了Java8 Lambda表達式和函數式接口相關原理、用法及操作注意事項,需要的朋友可以參考下
    2019-09-09
  • java封裝全局異常處理深入詳解

    java封裝全局異常處理深入詳解

    這篇文章主要為大家介紹了java封裝全局異常處理的深入詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-09-09
  • jedis連接池對commons-pool的封裝示例詳解

    jedis連接池對commons-pool的封裝示例詳解

    這篇文章主要為大家介紹了jedis連接池對commons-pool的封裝示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-09-09

最新評論

志丹县| 赣榆县| 新巴尔虎右旗| 海淀区| 象山县| 罗源县| 陵川县| 民勤县| 惠来县| 沽源县| 屏东县| 苍梧县| 三河市| 广丰县| 景东| 政和县| 拉萨市| 合肥市| 思南县| 嫩江县| 中牟县| 泾阳县| 绥阳县| 西吉县| 苍梧县| 永济市| 乐昌市| 青州市| 新巴尔虎左旗| 阿拉善右旗| 冷水江市| 博乐市| 东兰县| 福鼎市| 米脂县| 仙桃市| 阳城县| 中西区| 乐陵市| 成安县| 贡嘎县|