Java显然是一种价值传递,但需要一些澄清

时间:2015-11-26 07:17:00

标签: java pass-by-reference pass-by-value

已经阅读了关于此主题的大量堆栈溢出答案,并且我已阅读了许多博客。我可以肯定地得出结论,Java是按值传递的。

但为了说服人们,我需要一些通过参考的语言证明。

所以有人可以提供一个通过引用传递的语言的实例,我们可以通过引用来解释java没有通过引用传递。

4 个答案:

答案 0 :(得分:4)

  

所以有人可以给出一个通过引用传递的语言的实例,我们可以通过引用来解释java没有通过引用传递

Pass-by-ref与pass-by-value的最佳示例是swap。

void swap(String a, String b) {
    String c = b;
    b = a;
    a = c;
}

void main() {
    String a = "A";
    String b = "B";
    swap(a, b);
    System.out.println(a); // A
    System.out.println(b); // B
}

在这种情况下,当变量main.a指向与swap.a相同的对象时,您有两个对字符串的引用" A"。

vs C#(IDE One)支持by-ref。

void Swap(ref string a, ref string b) {
    string c = b;
    b = a;
    a = c;
}
void main() {
    string a = "A";
    string b = "B";
    Swap(ref a, ref b);
    Console.WriteLine(a); // B
    Console.WriteLine(b); // A
}

在这种情况下,变量main.a和swap.a是相同的引用,因此对swap.a的更改也发生在main.a。

那么这与

有何不同
void swap(StringBuilder a, StringBuilder b) {
    String a1 = a.toString();
    String b1 = b.toString();
    a.setLength(0);
    a.append(b1);
    b.setLength(0);
    b.append(a1);
}

void main(){
    StringBuilder a = new StringBuilder("A");
    StringBuilder b = new StringBuilder("B");
    swap(a, b);
    System.out.println(a); // B
    System.out.println(b); // A
}

在这种情况下,指向的对象会发生变化。例如:

public static void main(String... agv){
    StringBuilder a = new StringBuilder("A");
    StringBuilder b = new StringBuilder("B");
    StringBuilder alsoA = a;
    swap(a, b);
    System.out.println(a); // B
    System.out.println(b); // A
    System.out.println(alsoA); //B
}

vs C#(IDEOne

void Main() {
    string a = "a";
    string b = "b";
    string alsoA = a;
    Swap(ref a, ref b);
    Console.WriteLine(a); // B
    Console.WriteLine(b); // A
    Console.WriteLine(alsoA); // A
}

如果您仍然不确定,Java Ranch会有good article

答案 1 :(得分:3)

我不知道任何语言都是一直传递的,但C#有ref修饰符,允许特定参数使用传递引用。这使您可以很容易地看到差异。例如:

using System;

public class Program
{
    static void Main()
    {
        string x = "original x";
        string y = "original y";
        Modify(x, ref y);
        Console.WriteLine(x);
        Console.WriteLine(y);
    }

    static void Modify(string px, ref string py)
    {
        px = "modified x";
        py = "modified y";
    }
}

输出

original x
modified y

这是因为px的分配不会影响调用代码中的xx已传递给该方法。但是py 的分配确实更改了y,因为y是通过引用传递的; pyy基本上是同一存储位置的别名。

所有这些仅在您自己修改参数时才有意义。将上述程序与以下程序进行比较:

using System;
using System.Text;

public class Program
{
    static void Main()
    {
        StringBuilder x = new StringBuilder("original x");
        Modify(x);
        Console.WriteLine(x);
    }

    static void Modify(StringBuilder px)
    {
        px.Length = 0;
        px.Append("modified x");
    }
}

现在Modify方法根本不会更改px的值 - 而是更改px的值引用的对象。不需要按引用传递 - x的值通过值传递给方法,并将其复制到px。这就像在一张纸上复制你的家庭住址并将其交给某人。它不是复制房子本身 - 它只是模仿到达房子的方式。如果你给纸张的人去了,并把前门涂成红色,那么当你回家时,你会看到这种变化。

这就是理解variables, objects and references之间差异的重要原因。

答案 2 :(得分:0)

C ++有不同的传递参数的方法。一种方法是将指针类型指定为参数。我认为这与Java的作用很接近。指针按值传递。您可以修改指针引用的对象的状态,但不能更改调用函数引用的对象实例。

我包含了一个带有两个函数的C ++示例。一个函数按值调用,一个函数按引用语义调用。具有引用语义调用的函数可以更改调用函数引用的对象实例。

#include <iostream>

using namespace std;

class ClassA
{

public:
    int a = 0;

};

void funcByValue(ClassA *param)
{
    param->a = 1;
    param = new ClassA();
}

void funcByReference(ClassA* &param)
{
    param->a = 1;
    param = new ClassA();
}


int main()
{
    ClassA *objectA = new ClassA();

    cout << objectA->a << "\n";     //1
    cout << objectA << "\n";        //0000002B1D355AF0

    //This call changes objectA's state
    funcByValue(objectA);
    cout << objectA->a << "\n";     //1
    cout << objectA << "\n";        //0000002B1D355AF0

    //This call changes the object objectA is referencing
    funcByReference(objectA);
    cout << objectA->a << "\n";     //0
    cout << objectA << "\n";        //0000002B1D345610
}

答案 3 :(得分:0)