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

Java按值(或按引用)返回引用,java,Java,考虑以下代码: package Prova; import java.util.ArrayList; public class Prova { private ArrayList<String> people; public Prova() { people=new ArrayList<String> (); } public ArrayList<String> getPeople (){

考虑以下代码:

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());
    }
}
Prova包;
导入java.util.ArrayList;
公共类普罗瓦
{
私人ArrayList人;
公共Prova(){
people=newarraylist();
}
公共数组列表getPeople(){
还人,;
}
公共静态void main(字符串[]args){
Prova p=新Prova();
p、 go();
}
公开作废go(){
ArrayList temp=getPeople();
临时添加(“千斤顶”);
System.out.print(getPeople());
}
}
上面印着“杰克”


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

您需要进行防御性编程。有几个备选方案可供考虑

  • 不要对外公开列表,而是公开应用于列表的方法,例如
  • 返回不可变对象或对象的副本。例如:
public-List-getPeople{
返回新的ArrayList(人);
}

至于原因,其他帖子已经解释过了。传递到
ArrayList
的引用的值(更改值不会更改原始引用)。但是,列表本身包含对其对象的可修改引用

Java总是按值传递:

  • 对于基元类型,它直接传递值
  • 对于对象,它传递对象引用的值
  • 所以对于您的情况,它传递的是引用对象的值。因此对象引用。

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

    public List<String> getPeople() {
        return Collections.unmodifiableList(people);
    }
    
    public List getPeople(){
    返回集合。不可修改列表(人);
    }
    

    我建议大家看看Joshua Bloch的优秀著作《高效Java(第二版)》,“第39项:在需要时制作防御性拷贝”。不变性很迷人,但与制作防御性拷贝一样,它可能会变得昂贵。标准java集合并非设计为不变的数据结构。因此,如果你能像约翰·斯约伯格建议的那样去做,而不公开名单,那就太好了

    但您也应该考虑为什么需要执行如此高级别的封装。您是否将类公开为公共API?如果没有,如果您很好地了解和控制类的客户机,那么太多的封装可能是不切实际的。请记住,封装/信息隐藏与安全性关系不大,而更多的是向客户机提供简洁明确的API

    Java本质上是“按值传递”,与通常的意思有一个小但显著的区别,即它实际上是“按参考值传递”。 因此,当您处理Java类型(JVM的类公民,非基本类型)时,下面这样的声明基本上意味着您将获得类型为
    MyClass
    的引用
    referenceToMyObject
    的副本,指向JVM堆内存中相同的特定
    MyClass
    对象实例

    public class SomeClass {
    
        private MyClass referenceToMyClassInstance = new MyClass("instanceId-1");
    
        public MyClass getMyClassInstance() {
              return referenceToMyClassInstance;
        }
    
    }
    
    因此,在您的示例中,您基本上获得了指向同一
    ArrayList
    实例的引用副本,调用
    getPeople()
    的任何人现在都可以随意更改实际实例,可能会破坏应封装的状态

    因此,您应该返回ArrayList的一个副本,或者用不可修改的Decorator
    Collections.unmodifiableList(人员)包装它
    

    Java在处理对象时总是按引用传递。代码所做的只是验证这一事实。那么你到底期待什么回来?@Churk!如果它是通过引用传递的,
    getPeople()=…
    可以将成员更改为指向另一个ArrayList。对于论点传递,类似的反例是存在的,并且在我链接的问题中有十几个(更不用说几十个重复的问题了)。我只想重申@delnan的观点。在Java中,引用(基本上)是将指针的值传递给objectCollections。不可修改列表(人员)一点也不昂贵——它只是一个简单的包装器。是的,但它是原始集合的视图。因此,修改原始视图将反映在视图中。你应该确定那是你想要的。我的意思是,这与制作防御性拷贝非常不同。
    public List<String> getPeople() {
        return Collections.unmodifiableList(people);
    }
    
    public class SomeClass {
    
        private MyClass referenceToMyClassInstance = new MyClass("instanceId-1");
    
        public MyClass getMyClassInstance() {
              return referenceToMyClassInstance;
        }
    
    }