Java為我們提供了各種各樣功能的接口,Clonable接口就是其中之一。
它通常配合Object類的 clone方法使用。這個方法可以為我們創建一個對象的拷貝,即復制一個對象。在進入本文的主要內容之前,先來對訪問限定符 protected進行一個解剖。
1.再談 protected
我們知道,在一個類中被 protected修飾的字段和方法,它們的訪問權限如下:
- 本類內部:本類內部的所有成員都能夠訪問被
protected
修飾的元素。- 同一個包內:同一包中的其他類,不管是子類還是非子類,都可以訪問該元素。
- 不同包的子類:若子類與父類不在同一個包中,子類可以通過繼承或者創建子類對象的方式來訪問父類的?
protected
成員。
這里重點討論第三點,當一個類中有被 protected修飾的字段和方法,并且它的子類與它在不同包時,那么子類只能在自身內部去調用父類的 protected成員,不能在外部調用。
舉個例子如下:
//父類 Animal位于Demo1包
package Demo1;public class Animal {private String name;private int age;public Animal(String name, int age) {this.name = name;this.age = age;}protected void eat() {System.out.println(this.name + "正在吃飯....");}
}//子類 Dog位于Demo2包package Demo2;import Demo1.Animal;public class Dog extends Animal {public Dog(String name, int age) {super(name, age);}public void eat1() {super.eat();}
}//測試類 Testimport Demo2.Dog;public class Test {public static void main(String[] args) {Dog dog = new Dog("大黃",1);//dog.eat();發生錯誤:java: eat() 在 Demo1.Animal 中是 protected 訪問控制dog.eat1();//正常運行,運行結果:大黃正在吃飯....}
}
我們可以發現,確實如此,eat()方法是父類Animal中被protected修飾的方法,不能在子類Dog類外部直接調用,像這樣:dog.eat(),只能在子類內部調用,只有在子類內部的方法中去調用父類Animal,像這樣:eat1()中的super.eat();這樣Dog類外部使用 dog.eat1(),就可以間接調用父類Animal的eat()方法。
當然,我們也可以對在子類中對父類的protected方法進行重寫,這樣在子類外部就能直接調用了。這里就不再舉例了。
?2. 如何進行克隆
要使用Object類的clone方法,我們通常需要做以下幾件事:
- 在要克隆的類中重寫Object類的 clone方法
- 實現 Cloneable接口
- 事先處理異常情況
現在有一個Person類,我們打算克隆它的一個對象。
1.在要克隆的類中重寫Object類的 clone方法
package Demo3;public class Person {public String name;public Person(String name) {this.name = name;}//重寫 clone方法@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
注:可以通過快捷鍵“Alt + Insert”快速重寫 clone方法。?
?2.實現 Cloneable接口
package Demo3;public class Person implements Cloneable{private String name;public Person(String name) {this.name = name;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
注:當我們進入 Cloneable接口時,會發現它是一個空接口,它的作用是 宣布實現了這個接口的類可以克隆。
?3.處理異常情況,我們創建一個測試類,在測試類中去克隆一個Person對象。
package Demo3;public class Test {public static void main(String[] args) throws CloneNotSupportedException{Person person1 = new Person("小明");Person person2 = (Person) person1.clone();}
}
//這里,需要做兩件事
//1.把重寫的 clone方法中的:throws CloneNotSupportedException 這個部分復制到 mian方法后面。
//2.因為重寫的 clone方法的返回值為 Object,因此這里需要強轉。
讓我們測試一下:
package Demo3;public class Test {public static void main(String[] args) throws CloneNotSupportedException{Person person1 = new Person("小明");Person person2 = (Person) person1.clone();System.out.println("person1的名字:" + person1.name);System.out.println("person2的名字:" + person2.name);}
}//運行結果
person1的名字:小明
person2的名字:小明
顯然,確實做到克隆一份person1。
那么它的過程是怎么樣的呢?我們通過圖畫的方式來說明:
?
?3. 淺拷貝
知道了如何進行克隆,接著我們來討論一下什么是淺拷貝,顧名思義可以理解為:淺度的克隆。
舉個例子:我們在創建一個新的類 Money,在 Person類中創建一個 money對象,并將它作為一個字段。
//Money類package Demo3;public class Money {public int m = 10;
}//Person類package Demo3;public class Person implements Cloneable{public String name;public Money money = new Money();public Person(String name) {this.name = name;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
現在,我們再去克隆,接著修改person2對象當中的money對象的m,看看結果如何。
package Demo3;public class Test {public static void main(String[] args) throws CloneNotSupportedException{Person person1 = new Person("小明");Person person2 = (Person) person1.clone();System.out.println("person1的m:" + person1.money.m);System.out.println("修改前的person2的m:" + person2.money.m);person2.money.m = 100;System.out.println("==================");System.out.println("person1的m:" + person1.money.m);System.out.println("修改后的person2的m:" + person2.money.m);}
}//運行結果
person1的m:10
修改前的person2的m:10
==================
person1的m:100
修改后的person2的m:100
我們可以發現,person1與person2公用一個 money.m,因為person1和person2的money引用指向同一個對象。對于它的過程,我們依舊可以用畫圖的方式表示:
4. 深拷貝?
?在淺拷貝的例子里,我們會發現 person1和 person2公用一個 money.m,原因是它們的 money引用中儲存的是同一個money對象的地址。現在我們不想它們公用一個 money.m,那么我們該怎么做呢?這里我們就需要實現深拷貝。
想要實現深拷貝,要做兩件事:
- 要進行拷貝的類都要重寫 clone方法和實現 Cloneable接口,例如作為 Person類字段的 Money類也要重寫clone方法。
- 修改主要的類的重寫的clone方法,例如 上述例子中的 Person類,它是主要的類,因為Money類是它的字段。
?在Money類中重寫 clone方法和實現 Cloneable接口。
package Demo3;public class Money implements Cloneable{public int m = 10;//重寫 clone方法@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
修改 Person類中的重寫的 clone方法。
package Demo3;public class Person implements Cloneable{public String name;public Money money = new Money();public Person(String name) {this.name = name;}@Overrideprotected Object clone() throws CloneNotSupportedException {Person temp = (Person) super.clone(); temp.money = (Money) this.money.clone();return temp;}
}//說明:這里先創建一個 Person類型的變量 temp,用來接收克隆的person對象,
//接著讓temp.money去接收克隆的money對象,最后返回 temp變量。
現在,我們再去克隆,接著修改person2對象當中的money對象的m,看看結果如何。
package Demo3;public class Test {public static void main(String[] args) throws CloneNotSupportedException{Person person1 = new Person("小明");Person person2 = (Person) person1.clone();System.out.println("person1的m:" + person1.money.m);System.out.println("修改前的person2的m:" + person2.money.m);person2.money.m = 100;System.out.println("==================");System.out.println("person1的m:" + person1.money.m);System.out.println("修改后的person2的m:" + person2.money.m);}
}//運行結果
person1的m:10
修改前的person2的m:10
==================
person1的m:10
修改后的person2的m:100
顯然,此時修改 person2的 money.m也不會影響person1,因為person2的 money對象是克隆person1的,不再是與person1公用了。對于它的過程,我們用圖畫表示如下:
到此,本文完。本文若有不對的地方,還請各位看官指出,多謝!!!