为什么他们称之为Java中的值?

时间:2014-03-25 09:43:58

标签: java

好的,所以我是一个相对较新的程序员。关于传递变量,我一遍又一遍地阅读。从我在这里读到的是我已经理解的内容:在Java中,当您将变量传递给方法(无论是原始对象还是对象)时,您总是按值传递。但是你真正做的是将引用的副本传递给内存中的对象或变量。

那么为什么他们称之为价值传递呢?在我看来,你是通过参考传递。另外,在C中传递指向函数的指针时,是不是要在内存中传递变量的地址?然而,它被称为价值传递。我在俯瞰什么?有人可以解释并澄清我为什么这样做吗?我知道如何传递变量等等,我已经阅读了几篇SO帖子,但我还不完全满意。考虑一个例子:

  static void setNameStatic(Dog d, String name) {

        d.name = name;

  }


  public static void main (String args[]) {

           Dog d = new Dog("dog1");

           System.out.println( d.getName() );

           setNameStatic( d, "dog2" );

           System.out.println( d.getName() );

  }

这不会输出:dog1和dog2。那么你不是去参考引用的地方和改变数据吗?这个价值究竟与这里有什么关系?任何有用的评论表示赞赏。是的,我知道它是通过价值传递,但为什么不称它为“通过发送参考副本传递”大声笑?

更新:感谢大家提供了有用的答案。我知道这是重复的,对此感到抱歉。我现在对答案感到满意。

10 个答案:

答案 0 :(得分:4)

您的记忆在以下时刻看起来像这样:

Dog d = new Dog("dog1");

Stack (with local vars)            Heap

+-----------+     points to
|  Dog d    | --------------->    real dog object
+-----------+
|  ...      |
+-----------+
|  ...      |
+-----------+

然后,当您传入对方法的引用时,它看起来像这样(Dog dog2是方法参数):

Stack (with local vars)            Heap
+-----------+
|  Dog d    | --------------->    real dog object
+-----------+                             ^
|  ...      |                             |
+-----------+                             |
|  Dog d2   |------------------------------
+-----------+

因此,引用按值传递。它指向同一个对象,但它是 new 引用!

意思是,如果你想尝试这样的事情:

public static void main(String ... args) {
    Dog d= new Dog("dog1");
    setNameStatic(d, "dog2");
    System.out.println(d); // Still prints dog1 !!!
}

static void setNameStatic(Dog d2, String name) {
    d2= new Dog(name);
}

因为新的(复制的)引用现在指向堆上的新对象而原始对象没有更改。

答案 1 :(得分:2)

因为reference类型的任何变量的是其在内存中的地址。

因为primitive类型的任何变量的是其值本身。

因此,您始终传递变量。

答案 2 :(得分:1)

在java中,您将按值传递参数给方法,但如果参数是对象,参数本身就是ref类型。

作为C之类的编程语言的对比,允许通过引用传递,如果您打印引用,则会获得与打印引用时所获得的内容不同的内容。

要确保对象是引用类型并将它们与基元进行比较,请尝试以下方法:

public class TestClass {
    public static void main(String[] args) {
        String s = new String("a");
        String t = new String("a");
        System.out.println(s==t);
        char a = 'a';
        char b = 'a';
        System.out.println(a==b);
    }
}

结果是:
假为真

答案 3 :(得分:1)

在Java中将对象作为参数传递时,实际传递引用,引用是堆栈中使用的值。但是,调用者不会观察到您在方法内对此引用所做的任何分配,因为您只更改了引用的副本。原始值作为参数也是如此。

在其他语言(如C ++)中,您可以选择通过引用传递,允许您从方法中更改引用本身。例如,在Java中,您无法实现接受两个参数并交换它们的交换方法。在C ++中,它是可能的。

答案 4 :(得分:1)

每个变量都按值传递。在灵长类动物的例子中,我们有一些天真的情况 - 将值传递给函数。

在Objects的例子中,我们有一个稍微复杂但类似的范例 - 对象的引用按值传递

请注意细微差别:

//This is essentially a no-op. The values are only local to the function
void swap(int a, int b){
    int temp = a;
    a = b;
    b = temp;
}

//This is different. The references to a and b are passed by value so the values of a.x and b.x are actually swapped on return.
void swap(Point a, Point b){
    int temp = a.x;
    a.x = b.x;
    b.x = temp;
}

答案 5 :(得分:1)

按值传递意味着将值传输到方法,即创建相同值的另一个副本:

int i = 5; // one copy of 5
foo(i);

private void foo(int n) {
   // here n is 5, however it is "another" 5
}

传递价值的证据是,您无法在i方法中更改foo()

int i = 5; // one copy of 5
foo(i);
// i is still 5 here

private void foo(int n) {
   n = 6;
   // n is 6, however it is actual inside the method only.
}

对象也是如此,但在这种情况下,引用是按值传递的。

答案 6 :(得分:1)

  

另外,在C中传递指向函数的指针时,是不是要在内存中传递变量的地址?然而,它被称为传递价值。

不,不是。当您将指针传递给C中的函数的值时,它被称为“按引用传递”。该功能可直接访问&修改原始值,因为它有它的地址。传递值是指您传递对象的而不是其位置或地址。请看以下示例。

int addOne(int x)
{
    return x + 1;
}

void destructiveAddOne(int *x)
{
  *x += 1;
}

int main(void)
{
    int x = 5;
    int y = 5;

    // We pass the _value_ of x, ie, 5 to addOne, and store the result in z.
    // The value of x will remain unchanged, ie, 5.
    int z = addOne(x);

    // We pass a REFERENCE to y, ie, it's address to destructiveAddOne()
    // destructiveAddOne then modifies the value of y to be y + 1. The value of y
    // has now CHANGED to 6. This is call by reference.
    destructiveAddOne(&y);

    return 0;
}

答案 7 :(得分:1)

  1. 变量包含告诉JVM如何到达内存中引用的对象的位(Heap)。

  2. 将参数传递给方法时,您不传递引用变量,而是传递引用变量中的位副本。像这样:3bad086a。 3bad086a代表了一种获取传递对象的方法。

  3. 所以你只是传递3bad086a它是参考值。
  4. 您传递的是引用的值而不是引用本身(而不是对象)。
  5. 此值实际上是COPIED并提供给方法。
  6. 之前有过: Is Java "pass-by-reference" or "pass-by-value"?

答案 8 :(得分:1)

让你的头脑发挥是一件棘手的事。我认为这是真实的数字,虽然是抽象的。假设您的第一只狗dog1在记忆位置创建了#100,000; 100,000" (我知道,但请忍受我)。您将参考值的100,000传递给方法。您的方法更改生活在100,000的对象的名称。

如果你将dog1对象传递给d = new Dog(" dogXXX")的方法,那么它将在(例如)200,000处使用一个全新的对象,将你的dog1留在内存地址100,000不变。

对于内存地址滥用行为道歉,专家们会随地吐痰...

答案 9 :(得分:1)

Java中的引用通过引用的值传递。 传递引用作为方法参数时,将创建副本并对此副本进行操作。 为了说明它,请看一下这个示例代码:

final Person person = new Person();
person.setName("A");
changeName(person, "B");
System.out.println(person.getName());

现在我们已经changeName实现了这个:

private static void changeName(Person person, String name) {
    person.setName(name);
}

然后结果是B.它是因为引用的副本仍然指向同一个对象,并且有可能修改它。

另一方面,如果您将changeName方法更改为:

private static void changeName(Person person, String name) {
    person = new Person();
    person.setName(name);
}

然后结果是A.它是因为参考的副本指向新对象,但原始参考仍然指向原始对象,并且它的值不被修改。 如果Java通过引用传递对象引用,那么结果将是B,因为它将对原始对象进行操作。