在學習Java的過程中,在《Java核心技術》(卷一)中看到這樣一句話“因為Object類中的clone()方法是protected,所以不能直接以anObject.clone()這樣的形式調用。當時看到的時候有些不解,之前學習的時候,對protected的認識是這樣的
protected 修飾的類和屬性,對於自己、本包和其子類可見
Object默認是所有類的超類,clone()方法是Object里的方法,那按照這個理解來說,clone()方法是可以被任意一個類調用的,但實際上,當我們試圖運行下面這段代碼的時候
public class test { public static void main(String... args){ test2 a = new test2(); a.clone(); } } class test2 { }
編輯器會直接提示
‘clone() has protected access in ‘java.lang.Object’’
出現這種情況的原因實際上是因為之前對protected的理解有些片面,protected的准確理解是
對於protected的成員或方法,要分子類和超類是否在同一個包中。與基類不在同一個包中的子類,只能訪問自身從基類繼承而來的受保護成員,而不能訪問基類實例本身的受保護成員。在相同包時,protected和public是一樣的
對於這個例子,也就是說,雖然test和test2都是Object的子類,但是因為clone()方法是protected的,而且test和Object並不在一個包里,因此test里的方法只能訪問自身從基類(這個例子中也就是Object)繼承而來的受保護成員(也就是clone()方法),而不能訪問基類實例本身的受保護成員(也就是試圖用a.clone()這樣的方式去調用clone()方法)
public class test { public static void main(String... args){ test a = new test(); a.clone(); } } class test2 { }
實際上,當我們通過上面這種方式去調用clone()方法時,注意此時a是test的實例而非test2的實例,編譯器會提示
Unhandled exception:java.lang.CloneNotSupportedException
雖然程序仍然不能正常運行,但這時阻撓程序運行的已經不是之前的權限問題了,這也說明了test是可以調用自身從Object中繼承的clone()方法,而至於這個異常的來歷,我們去查看一下Object.clone()方法的源碼,可以找到下面這段內容
/** * @return a clone of this instance. * @throws CloneNotSupportedException if the object's class does not * support the {@code Cloneable} interface. Subclasses * that override the {@code clone} method can also * throw this exception to indicate that an instance cannot * be cloned. * @see java.lang.Cloneable */ @HotSpotIntrinsicCandidate protected native Object clone() throws CloneNotSupportedException;
可以看到,Object.clone()方法是一個native方法,簡單地講,一個native方法就是一個java調用非java代碼的接口。而一般native方法的速度都要比你自己所寫的程序運行速度快很多,這也是為什么當我們想要對一個對象進行克隆操作時,推薦使用Object.clone()方法而非自己通過java代碼去實現這樣一個功能(雖然也可以達到想要的結果)。除了native關鍵字外,我們再來關注一下這個方法的一些注解
- @return:表示這個方法返回的一個實例的克隆,這也與我們的預想沒有太大出入。
- @throws:實際上,這就是我們剛才的程序不能正常運行的原因了,@throws后面的內容說明了如果一個類沒有實現Cloneable接口,或者一個子類試圖重寫clone方法都會拋出CloneNotSupportedException這個異常
在實際操作中,如果要使用clone()方法,一般會進行以下幾個操作:
1.實現Cloneable接口,這里多說一句,實際上Cloneable是一個標記接口,所謂標記接口,顧名思義,是一個用來對類進行標記的接口,也就是說這個接口實際上沒有任何內容,我們去查看Cloneable的源碼,也可以看到相關解釋
/** * A class implements the <code>Cloneable</code> interface to * indicate to the {@link java.lang.Object#clone()} method that it * is legal for that method to make a * field-for-field copy of instances of that class. */ public interface Cloneable { }
2.將clone()方法重寫為public,之前也解釋過這個內容,protected的權限不能滿足實際需要。
3.調用父類的clone()方法,這里再補充一個內容,實際上Object.clone()方法實現的是淺克隆,而不是深克隆,如果想實現深克隆,需要自己對clone方法進行合理的重寫。這里也簡單的解釋一下兩種克隆。
- 淺克隆復制出來的對象的所有變量都含有與原來的對象相同的值,而所有的對其他對象的引用仍然指向原來的對象。也就是說,克隆之后的對象和之前的對象仍存在一些關聯,克隆程度不高,因此也被稱為淺克隆。
- 而深克隆復制出來的所有變量都含有與原來的對象相同的值,那些引用其他對象的變量將指向復制出來的新對象,而不再是原有的那些被引用的對象。換言之,深復制把要復制的對象所引用的對象都復制了一遍。
這里也給一個實現上述方法的簡單例子:
public class Player implements Cloneable { private String name; private int age; private Coach coach; public Player(String name, int age, Coach coach) { this.name = name; this.age = age; this.coach = coach; } public String getName() { return name; } public int getAge() { return age; } public Coach getCoach() { return coach; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } public Player clone() { Player player = null; try { player = (Player) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return player; } public static void main(String... args) { Coach coach = new Coach("Popvinch"); Player duncan = new Player("Duncan", 42, coach); Player clone = duncan.clone(); clone.getCoach().setName("Kerr"); clone.setName("Curry"); clone.setAge(30); System.out.println("His name is " + duncan.getName() + ",His age is " + duncan.getAge() + ",His coach is " + duncan.getCoach().getName()); System.out.println("His name is " + clone.getName() + ",His age is " + clone.getAge() + ",His coach is " + clone.getCoach().getName()); System.out.println(duncan.getCoach() == clone.getCoach()); } }