国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務器之家 - 編程語言 - JAVA教程 - 深入理解Java中的克隆

深入理解Java中的克隆

2020-06-04 11:36daisy JAVA教程

想必大家對克隆都有耳聞,世界上第一只克隆羊多莉就是利用細胞核移植技術將哺乳動物的成年體細胞培育出新個體,甚為神奇。其實在Java中也存在克隆的概念,即實現對象的復制。本文將嘗試介紹一些關于Java中的克隆和一些深入

前言

Java克隆(Clone)是Java語言的特性之一,但在實際中應用比較少見。但有時候用克隆會更方便更有效率。

對于克隆(Clone),Java有一些限制:

      1、被克隆的類必須自己實現Cloneable 接口,以指示 Object.clone() 方法可以合法地對該類實例進行按字段復制。Cloneable 接口實際上是個標識接口,沒有任何接口方法。

      2、實現Cloneable接口的類應該使用公共方法重寫 Object.clone(它是受保護的)。某個對象實現了此接口就克隆它是不可能的。即使 clone 方法是反射性調用的,也無法保證它將獲得成功。

      3、在Java.lang.Object類中克隆方法是這么定義的:

?
1
2
protected Object clone()
throws CloneNotSupportedException

創建并返回此對象的一個副本。表明是一個受保護的方法,同一個包中可見。

按照慣例,返回的對象應該通過調用 super.clone 獲得。

Java中的賦值

在Java中,賦值是很常用的,一個簡單的賦值如下

?
1
2
3
4
5
6
7
//原始類型
int a = 1;
int b = a;
 
//引用類型
String[] weekdays = new String[5];
String[] gongzuori = weekdays;//僅拷貝引用

在上述代碼中。

      1、如果是原始數據類型,賦值傳遞的為真實的值

      2、如果是引用數據類型,賦值傳遞的為對象的引用,而不是對象。

了解了數據類型和引用類型的這個區別,便于我們了解clone。

Clone

在Java中,clone是將已有對象在內存中復制出另一個與之相同的對象的過程。java中的克隆為逐域復制。

在Java中想要支持clone方法,需要首先實現Cloneable接口

Cloneable其實是有點奇怪的,它不同與我們常用到的接口,它內部不包含任何方法,它僅僅是一個標記接口。

其源碼如下

?
1
2
public interface Cloneable {
}

關于cloneable,需要注意的

      1、如果想要支持clone,就需要實現Cloneable 接口

      2、如果沒有實現Cloneable接口的調用clone方法,會拋出CloneNotSupportedException異常。

然后是重寫clone方法,并修改成public訪問級別

?
1
2
3
4
5
6
7
8
9
10
static class CloneableImp implements Cloneable {
 public int count;
 public Child child;
   
   
 @Override
 public Object clone() throws CloneNotSupportedException {
   return super.clone();
 }
}

調用clone方法復制對象

?
1
2
3
4
5
6
7
8
9
CloneableImp imp1 = new CloneableImp();
imp1.child = new Child("Andy");
try {
 Object obj = imp1.clone();
 CloneableImp imp2 = (CloneableImp)obj;
 System.out.println("main imp2.child.name=" + imp2.child.name);
} catch (CloneNotSupportedException e) {
 e.printStackTrace();
}

淺拷貝

上面的代碼實現的clone實際上是屬于淺拷貝(Shallow Copy)。

關于淺拷貝,你該了解的

     1、使用默認的clone方法

     2、對于原始數據域進行值拷貝

     3、對于引用類型僅拷貝引用

     4、執行快,效率高

     5、不能做到數據的100%分離。

     6、如果一個對象只包含原始數據域或者不可變對象域,推薦使用淺拷貝。

關于無法做到數據分離,我們可以使用這段代碼驗證

?
1
2
3
4
5
6
7
8
9
10
11
CloneableImp imp1 = new CloneableImp();
imp1.child = new Child("Andy");
try {
 Object obj = imp1.clone();
 CloneableImp imp2 = (CloneableImp)obj;
 imp2.child.name = "Bob";
     
 System.out.println("main imp1.child.name=" + imp1.child.name);
} catch (CloneNotSupportedException e) {
 e.printStackTrace();
}

上述代碼我們使用了imp1的clone方法克隆出imp2,然后修改 imp2.child.name 為 Bob,然后打印imp1.child.name 得到的結果是

?
1
main imp1.child.name=Bob

原因是淺拷貝并沒有做到數據的100%分離,imp1和imp2共享同一個Child對象,所以一個修改會影響到另一個。

深拷貝

深拷貝可以解決數據100%分離的問題。只需要對上面代碼進行一些修改即可。

1、Child實現Cloneable接口。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Child implements Cloneable{
 
 public String name;
 
 public Child(String name) {
   this.name = name;
 }
 
 @Override
 public String toString() {
   return "Child [name=" + name + "]";
 }
 
 @Override
 protected Object clone() throws CloneNotSupportedException {
   return super.clone();
 }
}

2.重寫clone方法,調用數據域的clone方法。

?
1
2
3
4
5
6
7
8
9
10
11
12
static class CloneableImp implements Cloneable {
 public int count;
 public Child child;
   
   
 @Override
 public Object clone() throws CloneNotSupportedException {
   CloneableImp obj = (CloneableImp)super.clone();
   obj.child = (Child) child.clone();
   return obj;
 }
}

當我們再次修改imp2.child.name就不會影響到imp1.child.name的值了,因為imp1和imp2各自擁有自己的child對象,因為做到了數據的100%隔離。

關于深拷貝的一些特點

      1、需要重寫clone方法,不僅僅只調用父類的方法,還需調用屬性的clone方法

      2、做到了原對象與克隆對象之間100%數據分離

      3、如果是對象存在引用類型的屬性,建議使用深拷貝

      4、深拷貝比淺拷貝要更加耗時,效率更低

為什么使用克隆

很重要并且常見的常見就是:某個API需要提供一個List集合,但是又不希望調用者的修改影響到自身的變化,因此需要克隆一份對象,以此達到數據隔離的目的。

應盡量避免clone

      1.通常情況下,實現接口是為了表明類可以為它的客戶做些什么,而Cloneable僅僅是一個標記接口,而且還改變了超類中的手保護的方法的行為,是接口的一種極端非典型的用法,不值得效仿。

      2.Clone方法約定及其脆弱 clone方法的Javadoc描述有點曖昧模糊,如下為 Java SE8的約定

          clone方法創建并返回該對象的一個拷貝。而拷貝的精確含義取決于該對象的類。一般的含義是,對于任何對象x,表達式

          x.clone() != x 為 true x.clone().getClass() == x.getClass() 也返回true,但非必須 x.clone().equals(x) 也返回true,但也不是必須的

上面的第二個和第三個表達式很容易就返回false。因而唯一能保證永久為true的就是表達式一,即兩個對象為獨立的對象。

       3.可變對象final域 在克隆方法中,如果我們需要對可變對象的final域也進行拷貝,由于final的限制,所以實際上是無法編譯通過的。因此為了實現克隆,我們需要考慮舍去該可變對象域的final關鍵字。

       4.線程安全 如果你決定用線程安全的類實現Cloneable接口,需要保證它的clone方法做好同步工作。默認的Object.clone方法是沒有做同步的。

總的來說,java中的clone方法實際上并不是完善的,建議盡量避免使用。如下是一些替代方案。

Copy constructors

使用復制構造器也可以實現對象的拷貝。

      1、復制構造器也是構造器的一種

      2、只接受一個參數,參數類型為當前的類

      3、目的是生成一個與參數相同的新對象

      4、復制構造器相比clone方法的優勢是簡單,易于實現。

一段使用了復制構造器的代碼示例

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Car {
 Wheel wheel;
 String manufacturer;
 
 public Car(Wheel wheel, String manufacturer) {
   this.wheel = wheel;
   this.manufacturer = manufacturer;
 }
 
 //copy constructor
 public Car(Car car) {
   this(car.wheel, car.manufacturer);
 }
 
 public static class Wheel {
   String brand;
 }
}

注意,上面的代碼實現為淺拷貝,如果想要實現深拷貝,參考如下代碼

?
1
2
3
4
5
6
7
8
//copy constructor
public Car(Car car) {
 Wheel wheel = new Wheel();
 wheel.brand = car.wheel.brand;
   
 this.wheel = wheel;
 this.manufacturer = car.manufacturer;
}

為了更加便捷,我們還可以為上述類增加一個靜態的方法

?
1
2
3
public static Car newInstance(Car car) {
 return new Car(car);
}

使用Serializable實現深拷貝

其實,使用序列化也可以實現對象的深拷貝。簡略代碼如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class DeepCopyExample implements Serializable{
 private static final long serialVersionUID = 6098694917984051357L;
 public Child child;
 
 public DeepCopyExample copy() {
   DeepCopyExample copy = null;
   try {
     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     ObjectOutputStream oos = new ObjectOutputStream(baos);
     oos.writeObject(this);
 
     ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
     ObjectInputStream ois = new ObjectInputStream(bais);
     copy = (DeepCopyExample) ois.readObject();
   } catch (IOException e) {
     e.printStackTrace();
   } catch (ClassNotFoundException e) {
     e.printStackTrace();
   }
   return copy;
 }
}

其中,Child必須實現Serializable接口

?
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Child implements Serializable{
 private static final long serialVersionUID = 6832122780722711261L;
 public String name = "";
 
 public Child(String name) {
   this.name = name;
 }
 
 @Override
 public String toString() {
   return "Child [name=" + name + "]";
 }
}

使用示例兼測試代碼

?
1
2
3
4
5
6
7
8
9
DeepCopyExample example = new DeepCopyExample();
example.child = new Child("Example");
   
DeepCopyExample copy = example.copy();
if (copy != null) {
 copy.child.name = "Copied";
 System.out.println("example.child=" + example.child + ";copy.child=" + copy.child);
}
//輸出結果:example.child=Child [name=Example];copy.child=Child [name=Copied]

由輸出結果來看,copy對象的child值修改不影響example對象的child值,即使用序列化可以實現對象的深拷貝。

總結

以上就是Java中克隆的全部內容,希望本文對大家學習Java能有所幫助。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 本道综合精品 | 日韩一二三区视频 | 欧美一级高清在线 | 亚洲欧洲日韩 | 久久av网站 | 国产美女视频自拍 | 免费一级视频在线观看 | 欧美精品v国产精品v日韩精品 | 国产精品久久久久永久免费观看 | 中文字幕亚洲一区 | 黄色片网站在线免费观看 | 国产麻豆乱码精品一区二区三区 | 免费视频一区二区 | 成人h免费观看视频 | 国产在线视频一区二区 | 欧美日韩三区 | 成人资源在线观看 | 欧美一区亚洲二区 | 日韩一二区| av集中淫 | 国产 高清 在线 | 国产精品com | 日韩免费一区 | 九九热这里 | 日韩成人在线影院 | 国产精品欧美一区二区 | 国产一二在线 | 成人精品在线观看 | 欧美日韩在线观看视频 | 欧美国产精品一区二区三区 | 精品久久亚洲 | 国产精品久久久久久久久久免费 | 日韩一区二区精品 | 999这里只有是极品 最新中文字幕在线 | 国产欧美日本 | 亚洲精品一区二区三区99 | 玖玖国产精品视频 | 毛片在线视频 | 国产视频在线播放 | 中文字幕日韩一区 | 日本a视频 |