深入 JAVA對象的復制與比較
1.對象的復制
String str1 = "This is a string!" //這里是 "對象引用" 的復制
String str2 = new String(str1); //這里是 "對象實(shí)例" 的復制
淺復制: 只復制復合對象本身.
深復制: 除了復制復合對象本身, 還復制了復合對象的引用的對象實(shí)例.
例如:
class Pupil{
public Pupil(String sno, String name, int age){
this.sno = new String(sno);
this.name = new String(name);
this.age = age;
}
public String getSno() {
return sno;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
private String sno;
private String name;
private int age;
}
public class CopyDemo {
public static Pupil[] shallowCopy(Pupil[] aClass) {
Pupil[] newClass = new Pupil[aClass.length];
//此時(shí)newClass 與aClass 指向同一塊內存
for(int i=0; i<aClass.length; i++)
newClass[i] = aClass[i];
return newClass;
}
public static Pupil[] deepCopy(Pupil[] aClass) {
Pupil[] newClass = new Pupil[aClass.length];
//此時(shí)newClass 與aClass 的相應sno , name 指向同一塊內存
for(int i=0; i<aClass.length; i++) {
String sno = aClass[i].getSno();
String name = aClass[i].getName();
int age = aClass[i].getAge();
newClass[i] = new Pupil(sno, name, age);
}
return newClass;
}
public static Pupil[] deeperCopy(Pupil[] aClass) {
Pupil[] newClass = new Pupil[aClass.length];
//完全的復制
for(int i=0; i<aClass.length; i++) {
String sno = new String(aClass[i].getSno());
String name = new String(aClass[i].getName());
int age = aClass[i].getAge();
newClass[i] = new Pupil(sno, name, age);
}
return newClass;
}
}
2.clone()的使用
* Object.clone()
* Cloneable 接口
* CloneNotSupportedException
a. 使用Object.clone 進(jìn)行復制
兩個(gè)必須條件:
1.一定要將重定義后的clone() 方法定義為公有方法(在Object 類(lèi)中, 它是受保護的成員, 不能直接使用)
2.該后代類(lèi)聲明實(shí)現接口 Cloneable 接口(當類(lèi)實(shí)現該接口, 其任何子類(lèi)也會(huì )繼承該接口), 該接口實(shí)際上沒(méi)有任何
內容, 只是一個(gè)標識, 標志實(shí)現該接口的類(lèi)提供clone() 方法.(這是接口的一種非典型用法)
public class Fraction implements Cloneable {
public Object clone() {
try{
return super.clone(); //call protected method
} catch (CloneNotSupportedException e) {
return null;
}
}
//other methods ...
}
b.重寫(xiě)Object.clone()
例如對 private char[] cb; character buffer 進(jìn)行復制
// add in class Cirbuf
public Object clone() {
try{
Cirbuf copy = (Cirbuf)super.clone();
copy.cb = (char[])cb.clone();
return copy;
}catch (CloneNotSupportedException e){
throw new InternalError(e.toString());
}
}
c.復制數組
數組是在方法調用重以引用的形式傳遞的對象. 下述情況下非常適合引用來(lái)傳遞數組:
*正在接收的方法不修改數組
*正在調用的方法不必關(guān)心是否修改數組
*正在調用的方法想要得到數組中的修改結果
否則, 就應該在方法調用中傳遞數組對象的副本. 只需調用 arrObj.clone() 方法即可完成數組arrObj 的復制操作. 隨后將該數組副本強制轉換為其正確類(lèi)型:
(type[])arrObj.clone();
System.arraycopy 方法提供一種用于在數組間復制多個(gè)元素的有效方式.
System.arraycopy(source, i, target, j, len)
3.對象實(shí)例的比較
例如:
Pupil p1 = new Pupil("99184001", "zhang3", 18);
Pupil p2 = new Pupil("99184001", "zhang3", 18);
a. "=="
if(p1 == p2)...
此次測試的是對象引用, 其結果肯定是false, 只要兩個(gè)對象引用不是互為別名就不會(huì )相等.
b. 淺比較 false
if(p1.getSno() == p2.getSno() && p1.getName() == p2.getName()
&& p1.getAge() == p2.getAge()) ...;
c. 深比較 true[/code]
if(p1.getSno().equals(p2.getSno()) && p1.getName().equals(p2.getName())
&& p1.getAge() == p2.getAge()) ...;[/code]
JAVA API 的跟類(lèi)Object 也提供了equals() 方法, 但它只是比較兩個(gè)對象引用, 而非比較兩個(gè)對象實(shí)例.
不管怎樣, 如果需要比較Pupil 類(lèi)的對象(例如要將它們放入對象容器), 應該為Pupil 類(lèi)重定義equals() 方法:
public boolean equals(Object otherobj) {
//檢查otherobj 是否為空
if(otherobj == null) return false;
//檢查otherobj 是否就是當前對象
if(otherobj == this) return true;
//檢查otherobj 是否具有正確的類(lèi)型, 即檢查是否可與當前對象比較
if(!(otherobj instanceof Pupil)) return false;
//將otherobj 轉換為Pupil 類(lèi)的對象引用
Pupil tmpObj = (Pupil)otherobj;
//關(guān)于學(xué)生是否相等的邏輯檢查
if(sno.equals(tmpObj.sno) && name.equals(tmpObj.name)
&& age == tmpObj.age) return true;
return false;
}
JAVA API 所提供的每個(gè)類(lèi)幾乎都提供了采用深比較策略的equals() 方法, 例如String 類(lèi)equals() 方法. 一般來(lái)說(shuō), 用戶(hù)自己定義的類(lèi)也應當提供合適的equals() 方法, 特別是當程序要將其對象放入JAVA API 所提供的對象容器類(lèi)的時(shí)候.
按照約定, 任何類(lèi)所提供的equals() 方法所實(shí)現的相等比較應該是等價(jià)關(guān)系, 即滿(mǎn)足自反性, 對稱(chēng)性和傳遞性. 另外一個(gè)類(lèi)重定義了equals() 方法, 也應該重定義相應hashCode() 方法, 否則將這個(gè)類(lèi)的對象放入映射對象容器時(shí)也會(huì )發(fā)生以外.
聯(lián)系客服