Java按值(或通过引用)返回引用

时间:2012-03-31 12:30:19

标签: 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());
    }
}

打印“jack”。

为什么呢?这不违反封装吗?如何按值返回?

5 个答案:

答案 0 :(得分:10)

你需要进行防守计划。有几种选择可以考虑

  • 不要在外部公开列表,而是公开要应用于列表的方法,例如
public void addPerson(String personName) {
    people.add(personName);
}
  • 返回不可变对象或对象的副本。例如,
public List<String> getPeople {
     return new ArrayList<String>(people);
}

为什么而言,正如其他帖子所解释的那样。传递ArrayList引用的值(唉,更改值不会更改原始引用)。但是,列表本身包含对其对象的可修改引用。

答案 1 :(得分:3)

Java总是按值传递:

  1. 对于基本类型,它直接传递值。
  2. 对于对象,它传递对象引用的值。
  3. 因此,对于您的情况,它是传递引用对象的值。因此,对象参考。

答案 2 :(得分:1)

getPeople()方法违反了封装,因为它返回对其私有列表的引用,而不是返回不可变的视图或副本。您可以通过以下方式轻松解决此问题:

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

我建议你看一下Joshua Bloch的优秀着作“Effective Java(第2版)”,“第39项:在需要时制作防御性副本”。

答案 3 :(得分:1)

不变性很有魅力,但与制作防御性副本一样,它可能变得昂贵。标准的Java集合并不是设计为不可变的数据结构。所以如果你能像JohanSjöberg那样建议并且根本不公开这个列表那就太好了。

但您还应该考虑为什么需要强制执行如此高级别的封装。您是否将您的课程公开为公共API?如果没有,如果你很好地了解和控制你的班级的客户,太多的封装可能是不切实际的。请记住,封装/信息隐藏不是关于安全性,而是关于向客户端呈现简洁明确的API。

答案 4 :(得分:0)

Java基本上是“按价值传递”,与通常意味着的差别很小但显着不同,即它实际上是“通过参考值传递”。 因此,当您处理Java类型(JVM的类公民,非基本类型)时,如下所述的声明基本上意味着您将获得类型referenceToMyObject的引用MyClass的副本,指向JVM堆内存中的同一特定MyClass Object实例。

public class SomeClass {

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

    public MyClass getMyClassInstance() {
          return referenceToMyClassInstance;
    }

}

因此,在您的示例中,您基本上会获得指向同一ArrayList实例的引用副本,并且任何调用getPeople()的人现在都能够更改他/她喜欢的实际实例,可能会破坏什么应该是封装状态。

所以你应该返回一个ArrayList的副本或用不可修改的Decorator Collections.unmodifiableList(people)

包装它