2012-03-31 44 views
2

考慮以下代碼:Java返回引用

package Prova; 

import java.util.ArrayList; 

public class Prova 
{ 
    private ArrayList<String> people; 
    public Prova() { 
     people=new ArrayList<String>(); 
    } 

    public ArrayList<String> getPeople(){ 
     return people; 
    } 

    public static void main(String[] args) { 
     Prova p=new Prova(); 
     p.go(); 
    } 

    public void go(){ 
     ArrayList<String> temp=getPeople(); 
     temp.add("jack"); 
     System.out.print(getPeople()); 
    } 
} 

它打印 「傑克」。

爲什麼?這不違反封裝嗎?如何通過價值回報它?

+0

的Java包總是被引用傳遞,當涉及到的對象。你的代碼只是驗證這個事實。那麼你究竟期待什麼回報? – Churk 2012-03-31 12:36:45

+2

@Churk [爪哇不傳遞通過引用](http://stackoverflow.com/q/40480/395760)!如果它是通過按引用,'getPeople()= ...'將是可能的,並且改變部件以指向另一個ArrayList的。對於參數傳遞,類似的反例存在,在我掛的問題(更不用說幾十個重複的)的十幾個中給出。 – delnan 2012-03-31 12:39:26

+0

我只想重申@delnan的觀點。在Java中,引用(根本)是*指針的值傳遞給* – 2012-07-17 03:02:36

回答

9

您需要防守編程。有幾個方案考慮

  • 不要對外公開名單,公開方法應用到列表,而不是,例如,
public void addPerson(String personName) { 
    people.add(personName); 
} 
  • 返回不可變對象或對象的副本。例如,
public List<String> getPeople { 
    return new ArrayList<String>(people); 
} 

至於爲什麼推移,它是作爲已被其他職位說明。通過ArrayList的參考值(唉,更改該值不會更改原始參考)。但是,列表本身包含對其對象的可修改引用。

3

爪哇總是由值傳遞:

  1. 對於原始類型,它直接傳遞值。
  2. 對於它傳遞對象引用的值。

因此對於您的情況,它是傳遞參考對象的值。因此對象引用。

1

getPeople()方法違反了封裝,因爲它返回對其私有列表的引用,而不是返回不可變視圖或副本。你可以很容易地實現此方法解決這個問題:

public List<String> getPeople() { 
    return Collections.unmodifiableList(people); 
} 

我建議看看約書亞Bloch的優秀著作「有效的Java(第2版)」,「第39項:在需要時進行防禦副本」。

1

不變性是迷人的,但與製作防禦性副本一樣,它可能會變得昂貴。標準的Java集合並不是被設計成不可變的數據結構。所以如果你能像JohanSjöberg所建議的那樣去做,並且根本不公開這個列表,那就太好了。

但你也應該考慮爲什麼你需要執行如此高水平的封裝。您是否將您的課程公開爲公共API?如果不是,如果你很好地瞭解和控制你的班級的客戶,太多的封裝可能是不切實際的。請記住,封裝/信息隱藏不是關於安全性,而是更多地向客戶端呈現簡潔和明確的API。

+0

Collections.unmodifiableList(人)是也不貴的對象 - 它只是一個簡單的包裝。 – 2012-04-01 06:38:47

+0

是的,但它是原始集合的視圖。因此,修改原件將反映在視圖中。你應該確定這就是你想要的。我的意思是與製作防守型副本非常不同。 – nansen 2012-04-01 10:27:25

0

Java本質上是「按價值傳遞」,與通常意義上的含義有微小但顯着的區別,即它實際上是「通過參考價值」。 所以,當你處理Java類型(JVM的二等公民,非原始類型),報關像下面基本上意味着你會得到MyClass類型的參考referenceToMyObject的副本,指向同一個特定MyClass對象JVM堆內存中的實例。

public class SomeClass { 

    private MyClass referenceToMyClassInstance = new MyClass("instanceId-1"); 

    public MyClass getMyClassInstance() { 
      return referenceToMyClassInstance; 
    } 

} 

所以,在你的榜樣,你基本上得到參考指點拷貝到同ArrayList實例,誰叫getPeople()現在可以改變實際的實例反正他/她喜歡的,有可能會損毀什麼應該被封裝狀態。

所以,你應該返回ArrayList中的副本或不可修改的裝飾Collections.unmodifiableList(people)