天天看點

Java中關于傳回引用可變對象常見問題剖析

前言

在工程中,我們建立了一個類,經常包含一些可變類的對象執行個體,當我們需要提取這些資料的時候,需要格外注意,否則會出一些意料之外的問題

正文

名詞解釋

可變對象:類中有方法可以改變其對象執行個體域(資料域)的類

舉例

假設我們有一個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引用了同一個對象,如圖

Java中關于傳回引用可變對象常見問題剖析

改正

如果需要傳回一個可變對象的引用,我們應該首先對其進行克隆(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++中的指針),是以在使用過程中一定要慎重慎重再慎重!