2012-03-31 60 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)