欧美一级特黄大片做受成人-亚洲成人一区二区电影-激情熟女一区二区三区-日韩专区欧美专区国产专区

一文帶你讀懂java中的對象拷貝

本篇文章為大家展示了一文帶你讀懂java中的對象拷貝,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細(xì)介紹希望你能有所收獲。

讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來自于我們對這個行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領(lǐng)域值得信任、有價值的長期合作伙伴,公司提供的服務(wù)項目有:主機(jī)域名、雅安服務(wù)器托管、營銷軟件、網(wǎng)站建設(shè)、天涯網(wǎng)站維護(hù)、網(wǎng)站推廣。

java對象拷貝詳解及實例

Java賦值是復(fù)制對象引用,如果我們想要得到一個對象的副本,使用賦值操作是無法達(dá)到目的的:

@Test
public void testassign(){
 Person p1=new Person();
 p1.setAge(31);
 p1.setName("Peter");

 Person p2=p1;
 System.out.println(p1==p2);//true
}

如果創(chuàng)建一個對象的新的副本,也就是說他們的初始狀態(tài)完全一樣,但以后可以改變各自的狀態(tài),而互不影響,就需要用到j(luò)ava中對象的復(fù)制,如原生的clone()方法。

如何進(jìn)行對象克隆

Object對象有個clone()方法,實現(xiàn)了對象中各個屬性的復(fù)制,但它的可見范圍是protected的,所以實體類使用克隆的前提是:

① 實現(xiàn)Cloneable接口,這是一個標(biāo)記接口,自身沒有方法。

② 覆蓋clone()方法,可見性提升為public。

@Data
public class Person implements Cloneable {
  private String name;
  private Integer age;
  private Address address;
  @Override
  protected Object clone() throws CloneNotSupportedException {
    return super.clone();
  }
}

@Test
public void testShallowCopy() throws Exception{
 Person p1=new Person();
 p1.setAge(31);
 p1.setName("Peter");

 Person p2=(Person) p1.clone();
 System.out.println(p1==p2);//false
 p2.setName("Jacky");
 System.out.println("p1="+p1);//p1=Person [name=Peter, age=31]
 System.out.println("p2="+p2);//p2=Person [name=Jacky, age=31]
}

該測試用例只有兩個基本類型的成員,測試達(dá)到目的了。

事情貌似沒有這么簡單,為Person增加一個Address類的成員:

@Data
public class Address {
  private String type;
  private String value;
}

再來測試,問題來了。

@Test
public void testShallowCopy() throws Exception{
 Address address=new Address();
 address.setType("Home");
 address.setValue("北京");

 Person p1=new Person();
 p1.setAge(31);
 p1.setName("Peter");
 p1.setAddress(address);

 Person p2=(Person) p1.clone();
 System.out.println(p1==p2);//false

 p2.getAddress().setType("Office");
 System.out.println("p1="+p1);
 System.out.println("p2="+p2);
}

查看輸出:

false
p1=Person(name=Peter, age=31, address=Address(type=Office, value=北京))
p2=Person(name=Peter, age=31, address=Address(type=Office, value=北京))

遇到了點(diǎn)麻煩,只修改了p2的地址類型,兩個地址類型都變成了Office。

淺拷貝和深拷貝

前面實例中是淺拷貝和深拷貝的典型用例。

淺拷貝:被復(fù)制對象的所有值屬性都含有與原來對象的相同,而所有的對象引用屬性仍然指向原來的對象。

深拷貝:在淺拷貝的基礎(chǔ)上,所有引用其他對象的變量也進(jìn)行了clone,并指向被復(fù)制過的新對象。

也就是說,一個默認(rèn)的clone()方法實現(xiàn)機(jī)制,仍然是賦值。

如果一個被復(fù)制的屬性都是基本類型,那么只需要實現(xiàn)當(dāng)前類的cloneable機(jī)制就可以了,此為淺拷貝。

如果被復(fù)制對象的屬性包含其他實體類對象引用,那么這些實體類對象都需要實現(xiàn)cloneable接口并覆蓋clone()方法。

@Data
public class Address implements Cloneable {
  private String type;
  private String value;

  @Override
  protected Object clone() throws CloneNotSupportedException {
    return super.clone();
  }
}

這樣還不夠,Person的clone()需要顯式地clone其引用成員。

@Data
public class Person implements Cloneable {
  private String name;
  private Integer age;
  private Address address;
  @Override
  protected Object clone() throws CloneNotSupportedException {
    Object obj=super.clone();
    Address a=((Person)obj).getAddress();
    ((Person)obj).setAddress((Address) a.clone());
    return obj;
  }
}

重新跑前面的測試用例:

false
p1=Person(name=Peter, age=31, address=Address(type=Home, value=北京))
p2=Person(name=Peter, age=31, address=Address(type=Office, value=北京))

clone方式深拷貝小結(jié)

① 如果有一個非原生成員,如自定義對象的成員,那么就需要:

  1. 該成員實現(xiàn)Cloneable接口并覆蓋clone()方法,不要忘記提升為public可見。
     
  2. 同時,修改被復(fù)制類的clone()方法,增加成員的克隆邏輯。

② 如果被復(fù)制對象不是直接繼承Object,中間還有其它繼承層次,每一層super類都需要實現(xiàn)Cloneable接口并覆蓋clone()方法。

與對象成員不同,繼承關(guān)系中的clone不需要被復(fù)制類的clone()做多余的工作。

一句話來說,如果實現(xiàn)完整的深拷貝,需要被復(fù)制對象的繼承鏈、引用鏈上的每一個對象都實現(xiàn)克隆機(jī)制。

前面的實例還可以接受,如果有N個對象成員,有M層繼承關(guān)系,就會很麻煩。

利用序列化實現(xiàn)深拷貝

clone機(jī)制不是強(qiáng)類型的限制,比如實現(xiàn)了Cloneable并沒有強(qiáng)制繼承鏈上的對象也實現(xiàn);也沒有強(qiáng)制要求覆蓋clone()方法。因此編碼過程中比較容易忽略其中一個環(huán)節(jié),對于復(fù)雜的項目排查就是困難了。

要尋找可靠的,簡單的方法,序列化就是一種途徑。

1.被復(fù)制對象的繼承鏈、引用鏈上的每一個對象都實現(xiàn)java.io.Serializable接口。這個比較簡單,不需要實現(xiàn)任何方法,serialVersionID的要求不強(qiáng)制,對深拷貝來說沒毛病。

2.實現(xiàn)自己的deepClone方法,將this寫入流,再讀出來。俗稱:冷凍-解凍。

@Data
public class Person implements Serializable {
  private String name;
  private Integer age;
  private Address address;
  public Person deepClone() {
    Person p2=null;
    Person p1=this;
    PipedOutputStream out=new PipedOutputStream();
    PipedInputStream in=new PipedInputStream();
    try {
      in.connect(out);
    } catch (IOException e) {
      e.printStackTrace();
    }

    try(ObjectOutputStream bo=new ObjectOutputStream(out);
        ObjectInputStream bi=new ObjectInputStream(in);) {
      bo.writeObject(p1);
      p2=(Person) bi.readObject();

    } catch (Exception e) {
      e.printStackTrace();
    }
    return p2;
  }
}

原型工廠類

為了便于測試,也節(jié)省篇幅,封裝一個工廠類。

公平起見,避免某些工具庫使用緩存機(jī)制,使用原型方式工廠。

public class PersonFactory{
  public static Person newPrototypeInstance(){
    Address address = new Address();
    address.setType("Home");
    address.setValue("北京");

    Person p1 = new Person();
    p1.setAddress(address);
    p1.setAge(31);
    p1.setName("Peter");
    return p1;
  }
}

利用Dozer拷貝對象

Dozer是一個Bean處理類庫。

maven依賴

<dependency>
 <groupId>net.sf.dozer</groupId>
 <artifactId>dozer</artifactId>
 <version>5.5.1</version>
</dependency>

測試用例:

@Data
public class Person {
  private String name;
  private Integer age;
  private Address address;

  @Test
  public void testDozer() {
  Person p1=PersonFactory.newPrototypeInstance();
    Mapper mapper = new DozerBeanMapper();
    Person p2 = mapper.map(p1, Person.class);
    p2.getAddress().setType("Office");
    System.out.println("p1=" + p1);
    System.out.println("p2=" + p2);
  }
}

@Data
public class Address {
  private String type;
  private String value;
}

輸出:

p1=Person(name=Peter, age=31, address=Address(type=Home, value=北京))
p2=Person(name=Peter, age=31, address=Address(type=Office, value=北京))

注意:在萬次測試中dozer有一個很嚴(yán)重的問題,如果DozerBeanMapper對象在for循環(huán)中創(chuàng)建,效率(dozer:7358)降低近10倍。由于DozerBeanMapper是線程安全的,所以不應(yīng)該每次都創(chuàng)建新的實例??梢宰詭У膯卫SDozerBeanMapperSingletonWrapper來創(chuàng)建mapper,或集成到spring中。

還有更暴力的,創(chuàng)建一個People類:

@Data
public class People {
  private String name;
  private String age;//這里已經(jīng)不是Integer了
  private Address address;

  @Test
  public void testDozer() {
  Person p1=PersonFactory.newPrototypeInstance();
    Mapper mapper = new DozerBeanMapper();
    People p2 = mapper.map(p1, People.class);
    p2.getAddress().setType("Office");
    System.out.println("p1=" + p1);
    System.out.println("p2=" + p2);
  }
}

只要屬性名相同,干~

繼續(xù)蹂躪:

@Data
public class People {
  private String name;
  private String age;
  private Map<String,String> address;//&#65533;&#65533;

  @Test
  public void testDozer() {
  Person p1=PersonFactory.newPrototypeInstance();
    Mapper mapper = new DozerBeanMapper();
    People p2 = mapper.map(p1, People.class);
    p2.getAddress().put("type", "Office");
    System.out.println("p1=" + p1);
    System.out.println("p2=" + p2);
  }
}

利用Commons-BeanUtils復(fù)制對象

maven依賴

<dependency>
 <groupId>commons-beanutils</groupId>
 <artifactId>commons-beanutils</artifactId>
 <version>1.9.3</version>
</dependency>

測試用例:

@Data
public class Person {
  private String name;
  private String age;
  private Address address;

  @Test
  public void testCommonsBeanUtils(){
  Person p1=PersonFactory.newPrototypeInstance();
    try {
      Person p2=(Person) BeanUtils.cloneBean(p1);
      System.out.println("p1=" + p1);
      p2.getAddress().setType("Office");
      System.out.println("p2=" + p2);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

利用cglib復(fù)制對象

maven依賴:

<dependency>
 <groupId>cglib</groupId>
 <artifactId>cglib</artifactId>
 <version>3.2.4</version>
</dependency>

測試用例:

@Test
public void testCglib(){
 Person p1=PersonFactory.newPrototypeInstance();
 BeanCopier beanCopier=BeanCopier.create(Person.class, Person.class, false);
 Person p2=new Person();
 beanCopier.copy(p1, p2,null);
 p2.getAddress().setType("Office");
 System.out.println("p1=" + p1);
 System.out.println("p2=" + p2);
}

結(jié)果大跌眼鏡,cglib這么牛x,居然是淺拷貝。不過cglib提供了擴(kuò)展能力:

@Test
public void testCglib(){
 Person p1=PersonFactory.newPrototypeInstance();
 BeanCopier beanCopier=BeanCopier.create(Person.class, Person.class, true);
 Person p2=new Person();
 beanCopier.copy(p1, p2, new Converter(){
  @Override
  public Object convert(Object value, Class target, Object context) {
   if(target.isSynthetic()){
    BeanCopier.create(target, target, true).copy(value, value, this);
   }
   return value;
  }
 });
 p2.getAddress().setType("Office");
 System.out.println("p1=" + p1);
 System.out.println("p2=" + p2);
}

Orika復(fù)制對象

orika的作用不僅僅在于處理bean拷貝,更擅長各種類型之間的轉(zhuǎn)換。

maven依賴:

<dependency>
 <groupId>ma.glasnost.orika</groupId>
 <artifactId>orika-core</artifactId>
 <version>1.5.0</version>
</dependency>
</dependencies>

測試用例:

@Test
public void testOrika() {
 MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();

 mapperFactory.classMap(Person.class, Person.class)
 .byDefault()
 .register();
 ConverterFactory converterFactory = mapperFactory.getConverterFactory();
 MapperFacade mapper = mapperFactory.getMapperFacade();

 Person p1=PersonFactory.newPrototypeInstance();
 Person p2 = mapper.map(p1, Person.class);
 System.out.println("p1=" + p1);
 p2.getAddress().setType("Office");
 System.out.println("p2=" + p2);
}

Spring BeanUtils復(fù)制對象

給Spring個面子,貌似它不支持深拷貝。

Person p1=PersonFactory.newPrototypeInstance();
Person p2 = new Person();
Person p2 = (Person) BeanUtils.cloneBean(p1);
//BeanUtils.copyProperties(p2, p1);//這個更沒戲

深拷貝性能對比

@Test
public void testBatchDozer(){
 Long start=System.currentTimeMillis();
 Mapper mapper = new DozerBeanMapper();
 for(int i=0;i<10000;i++){
  Person p1=PersonFactory.newPrototypeInstance();
  Person p2 = mapper.map(p1, Person.class);
 }
 System.out.println("dozer:"+(System.currentTimeMillis()-start));
 //dozer:721
}
@Test
public void testBatchBeanUtils(){
 Long start=System.currentTimeMillis();
 for(int i=0;i<10000;i++){
  Person p1=PersonFactory.newPrototypeInstance();
  try {
   Person p2=(Person) BeanUtils.cloneBean(p1);
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
 System.out.println("commons-beanutils:"+(System.currentTimeMillis()-start));
 //commons-beanutils:229
}
@Test
public void testBatchCglib(){
 Long start=System.currentTimeMillis();
 for(int i=0;i<10000;i++){
  Person p1=PersonFactory.newPrototypeInstance();
  BeanCopier beanCopier=BeanCopier.create(Person.class, Person.class, true);
  Person p2=new Person();
  beanCopier.copy(p1, p2, new Converter(){
   @Override
   public Object convert(Object value, Class target, Object context) {
    if(target.isSynthetic()){
     BeanCopier.create(target, target, true).copy(value, value, this);
    }
    return value;
   }
  });
 }
 System.out.println("cglib:"+(System.currentTimeMillis()-start));
 //cglib:133
}
@Test
public void testBatchSerial(){
 Long start=System.currentTimeMillis();
 for(int i=0;i<10000;i++){
  Person p1=PersonFactory.newPrototypeInstance();
  Person p2=p1.deepClone();
 }
 System.out.println("serializable:"+(System.currentTimeMillis()-start));
 //serializable:687
}
@Test
public void testBatchOrika() {
 MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();

 mapperFactory.classMap(Person.class, Person.class)
 .field("name", "name")
 .byDefault()
 .register();
 ConverterFactory converterFactory = mapperFactory.getConverterFactory();
 MapperFacade mapper = mapperFactory.getMapperFacade();

 Long start=System.currentTimeMillis();
 for(int i=0;i<10000;i++){
  Person p1=PersonFactory.newPrototypeInstance();
  Person p2 = mapper.map(p1, Person.class);
 }
 System.out.println("orika:"+(System.currentTimeMillis()-start));
 //orika:83
}

@Test
public void testBatchClone(){
 Long start=System.currentTimeMillis();
 for(int i=0;i<10000;i++){
  Person p1=PersonFactory.newPrototypeInstance();
  try {
   Person p2=(Person) p1.clone();
  } catch (CloneNotSupportedException e) {
   e.printStackTrace();
  }
 }
 System.out.println("clone:"+(System.currentTimeMillis()-start));
 //clone:8
}

(10k)性能比較:

//dozer:721
//commons-beanutils:229
//cglib:133
//serializable:687
//orika:83
//clone:8

深拷貝總結(jié)

原生的clone效率無疑是最高的,用腳趾頭都能想到。

偶爾用一次,用哪個都問題都不大。

一般性能要求稍高的應(yīng)用場景,cglib和orika完全可以接受。

另外一個考慮的因素,如果項目已經(jīng)引入了某個依賴,就用那個依賴來做吧,沒必要再引入一個第三方依賴。

上述內(nèi)容就是一文帶你讀懂java中的對象拷貝,你們學(xué)到知識或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識儲備,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

本文名稱:一文帶你讀懂java中的對象拷貝
文章鏈接:http://aaarwkj.com/article34/pcdspe.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供建站公司、服務(wù)器托管、云服務(wù)器、定制網(wǎng)站、網(wǎng)站維護(hù)網(wǎng)站內(nèi)鏈

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站網(wǎng)頁設(shè)計
亚洲欧美日韩综合一区| 国产一区二区三区性色| 亚洲欧美极品一区色婷婷| 精品国产一区二区三区性色av| 激情五月综合开心五月| 日本不卡一二三区在线观看| 亚洲日本成人一区二区| 大屁股白浆一区二区三区| 国产精品情侣av自拍| 哈昂~不要啊在线观看| 丰满人妻少妇精品一区二区三区 | 女同久久精品国产精品天堂99| 偷拍丝袜美腿在线观看| 欧美+亚洲+精品+三区| 国产亚洲精品精品国产亚洲| 日本一区二区在线观看视频| 久久色综合色悠悠色综合色| 久久偷拍一区二区三区| 十八禁网站免费在线播放| 日本一区二区三区播放| 色91精品在线观看剧情| av国语对白在线观看| 久久精品国产亚洲av高清大结局| 九色视频在线观看91| 国产欧美日韩精品一区| 国产伦一区二区三区三州| 97在线视频观看视频在线| 色综合一区二区日本韩国亚洲| 婷婷精品国产欧美精品亚洲| 黄色日韩欧美在线观看| 国产中文字幕一区久久| 久久亚洲综合精品人妻| 亚洲一区二区视频在线观看免费 | 黄片大全在线免费视频观看| 青青草av一区二区三区| 亚洲婷婷综合精品五月天| 国产在线自拍一区二区| 欧美成人免费做真爱高潮| 国产成人亚洲欧美激情| 91在线播放欧美国产视频| 欧美精品一区二区三区色|