我一直以为Java使用pass-by-reference. 但是,我读了一篇博客文章,声称Java使用pass-by-value. 我不认为我明白作者所做的区别。

什么是解释?


当前回答

Java 通过值的参数,在 Java 中没有参考的选项。

但是,在收集带层,它使用参考内部不暴露于用户。

它是必不可少的,因为它节省了很多的记忆,并提高了速度。

其他回答

我看到所有的答案都是相同的:通过值。 然而,最近的布莱恩·戈茨关于Valhalla项目的更新实际上会以不同的方式回答:

事实上,这是一个常见的“gotcha”问题,关于Java对象是否通过值或参考,答案是“也不”:对象参考通过值。

您可以在这里阅读更多: 瓦尔哈拉州 部分 2: 语言模型

编辑:Brian Goetz是Java语言建筑师,领导项目如Project Valhalla和Project Amber。

Edit-2020-12-08: 瓦尔哈拉的最新状态

如果你想把它放在一个单一的句子,以便轻松理解和记住,最简单的答案:

Java 总是通过新参考的值

(因此,您可以修改原始对象,但无法访问原始参考)

我试图简化上面的例子,只保持问题的本质. 让我把这个作为一个容易记住和正确应用的故事。 故事如下: 你有一个宠物狗,吉米,尾巴长12英寸。

下次你旅行,你带狗,无意中,到一个邪恶的<unk>子,他也是长尾的仇恨者,所以他把它切到一个可怜的2英寸,但他这样做你的亲爱的吉米,而不是一个克隆。

public class Doggie {

    public static void main(String...args) {
        System.out.println("At the owner's home:");
        Dog d = new Dog(12);
        d.wag();
        goodVet(d);
        System.out.println("With the owner again:)");
        d.wag();
        badVet(d);
        System.out.println("With the owner again(:");
        d.wag();
    }

    public static void goodVet (Dog dog) {
        System.out.println("At the good vet:");
        dog.wag();
        dog = new Dog(12); // create a clone
        dog.cutTail(6);    // cut the clone's tail
        dog.wag();
    }

    public static void badVet (Dog dog) {
        System.out.println("At the bad vet:");
        dog.wag();
        dog.cutTail(2);   // cut the original dog's tail
        dog.wag();
    }    
}

class Dog {

    int tailLength;

    public Dog(int originalLength) {
        this.tailLength = originalLength;
    }

    public void cutTail (int newLength) {
        this.tailLength = newLength;
    }

    public void wag()  {
        System.out.println("Wagging my " +tailLength +" inch tail");
    }
}

Output:
At the owner's home:
Wagging my 12 inch tail
At the good vet:
Wagging my 12 inch tail
Wagging my 6 inch tail
With the owner again:)
Wagging my 12 inch tail
At the bad vet:
Wagging my 12 inch tail
Wagging my 2 inch tail
With the owner again(:
Wagging my 2 inch tail

在这里,每一个单一的答案都倾向于从其他语言中提到通过指标,并显示在Java中不可能做什么,因为任何原因,没有人试图从其他语言中展示如何实施通过对象的价值。

这个代码表明如何做这样的事情:

public class Test
{
    private static void needValue(SomeObject so) throws CloneNotSupportedException
    {
        SomeObject internalObject = so.clone();
        so=null;
        
        // now we can edit internalObject safely.
        internalObject.set(999);
    }
    public static void main(String[] args)
    {
        SomeObject o = new SomeObject(5);
        System.out.println(o);
        try
        {
            needValue(o);
        }
        catch(CloneNotSupportedException e)
        {
            System.out.println("Apparently we cannot clone this");
        }
        System.out.println(o);
    }
}

public class SomeObject implements Cloneable
{
    private int val;
    public SomeObject(int val)
    {
        this.val = val;
    }
    public void set(int val)
    {
        this.val = val;
    }
    public SomeObject clone()
    {
        return new SomeObject(val);
    }
    public String toString()
    {
        return Integer.toString(val);
    }
}

在这里,我们有一个函数需要值,它正在立即创建一个对象的克隆,需要在对象本身的类中实施,而该类必须被标记为克隆。

可能很好,Java没有通过参考语法,但称之为“通过价值”的语言沿着有愿望的思维线。

数据通过参数在函数之间共享,现在有两种方式通过参数:

通过参数 : 呼叫器和呼叫器使用相同的变量为参数. 通过值 : 呼叫器和呼叫器有两个独立的变量与相同的值。

Java 使用 Pass by Value

在传输原始数据时,它复制原始数据类型的值;在传输对象时,它复制对象的地址,并转移到转换方法变量。

Java 在存储变量中遵循以下规则:

原始和对象参考等本地变量在 Stack 记忆中创建,对象在 Heap 记忆中创建。

使用原始数据类型的例子:

public class PassByValuePrimitive {
    public static void main(String[] args) {
        int i=5;
        System.out.println(i);  //prints 5
        change(i);
        System.out.println(i);  //prints 5
    }
    
    
    private static void change(int i) {
        System.out.println(i);  //prints 5
        i=10;
        System.out.println(i); //prints 10
        
    }
}

使用对象的例子:

public class PassByValueObject {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("prem");
        list.add("raj");
        new PassByValueObject().change(list);
        System.out.println(list); // prints [prem, raj, ram]
        
    }
    
    
    private  void change(List list) {
        System.out.println(list.get(0)); // prem
        list.add("ram");
        list=null;
        System.out.println(list.add("bheem")); //gets NullPointerException
    }
}