前言
在工程中,我們建立了一個類,經常包含一些可變類的對象執行個體,當我們需要提取這些資料的時候,需要格外注意,否則會出一些意料之外的問題
正文
名詞解釋
可變對象:類中有方法可以改變其對象執行個體域(資料域)的類
舉例
假設我們有一個example類,其中為Date類建立了一個實體birthday,在外部需要知道某個example類執行個體的birthday值時,會使用到getBirthday()方法,通常我們的getBirthday()方法會寫成如下的樣子:
class Example{
private Date birthday;
...
public Date getBirthday{
return birthday;
}
...
}
這樣的寫法,會破壞example類的封裝性,因為當外部調用了getBirthday()這個方法後,生成了另一個對于birthday所引用對象的引用,在新的引用中調用Date類中的方法,同樣會改變目前執行個體中birthday的值,例如如下代碼:
Example e = new Example(....);
Date d = e.getBirthday();
double tenYearsInMilliSeconds = * * * * * ;
d.setTime(d.getTime() - (long) tenYearsInMilliSeconds);
這樣的代碼,會使得再次調用e.getBirthday()後,得到的值是修改後的值。
原因
上述例子中,d和e.birthday引用了同一個對象,如圖
改正
如果需要傳回一個可變對象的引用,我們應該首先對其進行克隆(clone),這樣會建立一個目前對象的副本,而不會對目前對象造成影響,代碼改正如下
class Example{
private Date birthday;
...
public Date getBirthday{
return birthday.clone();
}
...
}
2017.6.10更新
看過Date(java.util.Date)的源碼的各位應該知道,Date裡的clone方法傳回的是一個Object對象,這會導緻上面的 getBirthday 函數報錯(類型不比對,不能從Object類型轉為Date),是以,這裡需要将代碼更正如下
class Example{
private Date birthday;
...
public Date getBirthday{
return (Date) birthday.clone();
}
...
}
經過這樣的轉換,也不會破壞封裝性(因為傳回來的是 birthday 的一個複制品)
後記
在編寫Java代碼的時候,一定要記得,Java中每聲明一個類的執行個體名稱,事實上隻是聲明了一個引用名稱,目前執行個體建立是在調用new + 構造器後才建立的(類似于C/C++中的指針),是以在使用過程中一定要慎重慎重再慎重!