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

什么是解释?


当前回答

在我所知道的范围内,Java只知道值的呼叫,这意味着对于原始数据类型,您将使用复印件工作,对于对象,您将使用对象的参考复印件工作。

public static void swap(StringBuffer s1, StringBuffer s2) {
    StringBuffer temp = s1;
    s1 = s2;
    s2 = temp;
}


public static void main(String[] args) {
    StringBuffer s1 = new StringBuffer("Hello");
    StringBuffer s2 = new StringBuffer("World");
    swap(s1, s2);
    System.out.println(s1);
    System.out.println(s2);
}

这将人口Hello World而不是World Hello,因为在交换函数中,您使用复印件,这些复印件不会对主要的参考产生影响。

public static void appendWorld(StringBuffer s1) {
    s1.append(" World");
}

public static void main(String[] args) {
    StringBuffer s = new StringBuffer("Hello");
    appendWorld(s);
    System.out.println(s);
}

如果您将 StringBuffer 更改为 String,它只会产生 Hello 因为 String 是不可变的。

public static void appendWorld(String s){
    s = s+" World";
}

public static void main(String[] args) {
    String s = new String("Hello");
    appendWorld(s);
    System.out.println(s);
}

但是,你可以为 String 做一个插槽,这将使它能够与 Strings 一起使用:

class StringWrapper {
    public String value;

    public StringWrapper(String value) {
        this.value = value;
    }
}

public static void appendWorld(StringWrapper s){
    s.value = s.value +" World";
}

public static void main(String[] args) {
    StringWrapper s = new StringWrapper("Hello");
    appendWorld(s);
    System.out.println(s.value);
}

编辑:我认为这也是使用 StringBuffer 的理由,当涉及到“添加”两个线条时,因为你可以修改原始对象,你不能用像 String 这样的不可变的对象。

其他回答

我觉得关于“pass-by-reference vs pass-by-value”的争论并不太有用。

在我们到达Java实施之前,滑板/滑板上走路:价值观以顺序的方式滑板上走路,就像一家咖啡馆里的滑板一样。 滑板中的记忆(也称为动态记忆)是危险的,不组织的。

好吧! 首先,地方原始人走在地板上,所以这个代码:

int x = 3;
float y = 101.1f;
boolean amIAwesome = true;

此分類上一篇

当你宣布和即时一个对象时,实际对象会走在地板上,什么会走在地板上?对象的地址会走在地板上,C++程序员会称之为指标,但一些Java开发人员反对“指标”这个词。

像这样:

int problems = 99;
String name = "Jay-Z";

此分類上一篇

JButton[] marxBros = new JButton[3];
marxBros[0] = new JButton("Groucho");
marxBros[1] = new JButton("Zeppo");
marxBros[2] = new JButton("Harpo");

此分類上一篇

private static void shout(String name){
    System.out.println("There goes " + name + "!");
}

public static void main(String[] args){
    String hisName = "John J. Jingleheimerschmitz";
    String myName = hisName;
    shout(myName);
}

此分類上一篇

所以,值,参考?你说“土豆”。

创建新点对象创建新点参考,并启动该参考到点(参考到)上以前创建的点对象. 从这里,通过点对象生活,您将通过pnt1参考访问该对象. 所以我们可以说,在Java中,您通过其参考操纵对象。

此分類上一篇

public static void tricky(Point arg1, Point arg2) {
  arg1.x = 100;
  arg1.y = 100;
  Point temp = arg1;
  arg1 = arg2;
  arg2 = temp;
}
public static void main(String [] args) {
  Point pnt1 = new Point(0,0);
  Point pnt2 = new Point(0,0);
  System.out.println("X1: " + pnt1.x + " Y1: " +pnt1.y); 
  System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);
  System.out.println(" ");
  tricky(pnt1,pnt2);
  System.out.println("X1: " + pnt1.x + " Y1:" + pnt1.y); 
  System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);  
}

该计划的流动:

Point pnt1 = new Point(0,0);
Point pnt2 = new Point(0,0);

System.out.println("X1: " + pnt1.x + " Y1: " +pnt1.y); 
System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);
System.out.println(" ");

预计产量将是:

X1: 0     Y1: 0
X2: 0     Y2: 0

在此线上“pass-by-value”进入游戏中。

tricky(pnt1,pnt2);           public void tricky(Point arg1, Point arg2);

 arg1.x = 100;
 arg1.y = 100;

此分類上一篇

下一篇: 迷人的方法

Point temp = arg1;
arg1 = arg2;
arg2 = temp;

在这里,您首先创建一个新的 temp 点参考,将指向同一个位置,如 arg1 参考。 然后您将移动 arg1 参考,以指向同一个位置,如 arg2 参考。

从这里,迷人的方法的范围已经消失了,你不再有任何访问参考: arg1, arg2, temp. 但重要注意的是,当它们“在生活中”时,你所做的一切都会永久地影响它们所指向的对象。

X1: 0         Y1: 0
X2: 0         Y2: 0
X1: 100       Y1: 100
X2: 0         Y2: 0

这是一个非常简单的方式来理解这一点. 让我们通过C++参考。

#include <iostream>
using namespace std;

class Foo {
    private:
        int x;
    public:
        Foo(int val) {x = val;}
        void foo()
        {
            cout<<x<<endl;
        }
};

void bar(Foo& ref)
{
    ref.foo();
    ref = *(new Foo(99));
    ref.foo();
}

int main()
{
   Foo f = Foo(1);
   f.foo();
   bar(f);
   f.foo();

   return 0;
}

什么是结果?

1
1
99
99

因此,在 bar() 将新值分配到输入的“参考”后,它实际上改变了从主要本身输入的值,解释了从主要打印99 的最后一个 f.foo() 呼叫。

现在,让我们看看Java说了什么。

public class Ref {

    private static class Foo {
        private int x;

        private Foo(int x) {
            this.x = x;
        }

        private void foo() {
            System.out.println(x);
        }
    }

    private static void bar(Foo f) {
        f.foo();
        f = new Foo(99);
        f.foo();
    }

    public static void main(String[] args) {
        Foo f = new Foo(1);
        System.out.println(f.x);
        bar(f);
        System.out.println(f.x);
    }

}

它说:

1
1
99
1

因此,Foo的主要引用被转移到酒吧,仍然没有改变!

这个例子清楚地表明,Java不是与C++相同的,当我们说“通过参考”。基本上,Java将“参考”作为“值”转移到功能,这意味着Java通过值。

Pass By Reference 收到的函数值是通话者所使用的对象的参考。 通话者所指的对象的任何函数都将被通话者看到,并将从那时起与这些变化进行操作。

正如这些定义所表明的那样,参考通过值是毫无意义的,如果我们要接受这个定义,那么这些术语就变得毫无意义,所有语言都只是通过值。

因此,有了这个理解,我们可以看看Java,并看到它实际上有两种。所有Java原始类型总是通过值,因为你收到一个复印件的呼叫器的对象,并且不能修改他们的复印件。

正如许多人之前提到的那样,Java总是有价值的。

下面是另一个例子,这将帮助你理解差异(经典交换例子):

public class Test {
  public static void main(String[] args) {
    Integer a = new Integer(2);
    Integer b = new Integer(3);
    System.out.println("Before: a = " + a + ", b = " + b);
    swap(a,b);
    System.out.println("After: a = " + a + ", b = " + b);
  }

  public static swap(Integer iA, Integer iB) {
    Integer tmp = iA;
    iA = iB;
    iB = tmp;
  }
}

印刷:

前: a = 2, b = 3 后: a = 2, b = 3

这是因为 iA 和 iB 是新的本地参考变量,具有相同值的过去参考(他们的点到 a 和 b 相应)。因此,试图改变 iA 或 iB 的参考将仅在本地范围内变化,而不是在该方法之外。