考虑以下代码:
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”。
为什么呢?这不违反封装吗?如何按值返回?
答案 0 :(得分:10)
你需要进行防守计划。有几种选择可以考虑
public void addPerson(String personName) {
people.add(personName);
}
public List<String> getPeople {
return new ArrayList<String>(people);
}
就为什么而言,正如其他帖子所解释的那样。传递ArrayList
引用的值(唉,更改值不会更改原始引用)。但是,列表本身包含对其对象的可修改引用。
答案 1 :(得分:3)
Java总是按值传递:
因此,对于您的情况,它是传递引用对象的值。因此,对象参考。
答案 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)
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)