java int引用传递 java如何引用传递 - Java是“传递引用”还是“按值传递”?




15 Answers

Java始终是按值传递的 。 不幸的是,他们决定将对象的位置称为“引用”。 当我们传递一个对象的值时,我们将引用传递给它。 这对初学者来说很困惑。

它是这样的:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    // we pass the object to foo
    foo(aDog);
    // aDog variable is still pointing to the "Max" dog when foo(...) returns
    aDog.getName().equals("Max"); // true
    aDog.getName().equals("Fifi"); // false 
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // change d inside of foo() to point to a new Dog instance "Fifi"
    d = new Dog("Fifi");
    d.getName().equals("Fifi"); // true
}

在上面的示例中, aDog.getName()仍将返回"Max" 。 使用Dog "Fifi"在函数foo不更改main的值aDog ,因为对象引用按值传递。 如果它是通过引用传递的,那么mainaDog.getName()将在调用foo后返回"Fifi"

同样:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    foo(aDog);
    // when foo(...) returns, the name of the dog has been changed to "Fifi"
    aDog.getName().equals("Fifi"); // true
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // this changes the name of d to be "Fifi"
    d.setName("Fifi");
}

在上面的例子中, Fifi是调用foo(aDog)之后的狗的名字,因为对象的名字是在foo(...)food上执行的任何操作都是这样的,出于所有实际目的,它们是在aDog本身上执行的(除非d被更改为指向不同的Dog实例,例如d = new Dog("Boxer") )。

string是值传递还是引用传递 java引用

我一直认为Java是传递引用的

但是,我看过一些博客文章(例如, 这个博客 )声称它不是。

我不认为我理解他们所做的区别。

解释是什么?




Java总是按值而不是通过引用传递参数。

让我通过一个example解释一下:

public class Main{
     public static void main(String[] args){
          Foo f = new Foo("f");
          changeReference(f); // It won't change the reference!
          modifyReference(f); // It will modify the object that the reference variable "f" refers to!
     }
     public static void changeReference(Foo a){
          Foo b = new Foo("b");
          a = b;
     }
     public static void modifyReference(Foo c){
          c.setAttribute("c");
     }
}

我将逐步解释这个:

  1. 声明名为fFoo类型的引用,并将其分配给类型为Foo的新对象,其属性为"f"

    Foo f = new Foo("f");
    

  2. 从方法方面,声明了具有名称a的类型为Foo的引用,并且它最初被赋值为null

    public static void changeReference(Foo a)
    

  3. 当您调用方法changeReference ,引用a将被分配给作为参数传递的对象。

    changeReference(f);
    

  4. 声明名为b的类型为Foo的引用,并将其分配给类型为Foo的新对象,其属性为"b"

    Foo b = new Foo("b");
    

  5. a = b将引用a NOT f重新分配给其属性为"b"的对象。

  6. 当您调用modifyReference(Foo c)方法时,将创建引用c并将其分配给具有属性"f"的对象。

  7. c.setAttribute("c"); 将更改引用c指向它的对象的属性,并且它是引用f指向它的同一对象。

我希望你现在明白如何将对象作为参数传递在Java中:)




Java总是按值传递,没有例外。

那么如何让任何人都对此感到困惑,并相信Java是通过引用传递的,或者认为他们有一个Java作为传递参考的例子? 关键是Java在任何情况下都不会直接访问对象本身的值。 对对象的唯一访问是通过对该对象的引用 。 因为Java对象总是通过引用而不是直接访问,所以通常将字段和变量以及方法参数作为对象进行讨论 ,而当它们只是对对象的引用时这种混淆源于这种(严格来说,不正确的)命名法的变化。

所以,在调用方法时

  • 对于原始参数( intlong等),pass by value是原语的实际值 (例如,3)。
  • 对于对象,pass by value是对象引用的值。

因此,如果你有doSomething(foo)public void doSomething(Foo foo) { .. }那么两个Foos已经复制了指向相同对象的引用

当然,通过值传递对对象的引用看起来非常像(并且在实践中无法区分)通过引用传递对象。




我觉得争论“传递引用与传递价值”并不是非常有用。

如果你说“Java是任意的(参考/值)”,在任何一种情况下,你都没有提供完整的答案。 这里有一些额外的信息,有助于理解记忆中发生的事情。

在我们进入Java实现之前,堆栈/堆上的崩溃过程:值以一种非常有序的方式在堆栈中上下移动,就像在自助餐厅的堆栈一样。 堆中的内存(也称为动态内存)是杂乱无章的。 JVM只是在任何地方找到空间,并释放它,因为不再需要使用它的变量。

好的。 首先,本地原语进入堆栈。 所以这段代码:

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);
}

创建一个String,并在堆中分配空间,并将字符串的地址存储在堆栈中并赋予标识符hisName ,因为第二个String的地址与第一个String的地址相同,因此不会创建新的String并且没有分配新的堆空间,但是在堆栈上创建了新的标识符。 然后我们调用shout() :创建一个新的堆栈帧,并创建一个新的标识符, name并分配已存在的String的地址。

那么,价值,参考? 你说“土豆”。




Java按值传递对象的引用。




我无法相信没人提到Barbara Liskov。当她在1974年设计CLU时,她遇到了同样的术语问题,并且通过共享(也称为通过对象共享调用按对象调用)发明了这个特定情况的“通过值调用,其值为参考“。




在java中,所有内容都是引用,所以当你有类似的东西时:Point pnt1 = new Point(0,0);Java确实如下:

  1. 创建新的Point对象
  2. 创建新的Point引用并初始化该引用以指向先前创建的Point对象(参考)
  3. 从这里开始,通过Point对象生命,您将通过pnt1引用访问该对象。所以我们可以说在Java中你通过它的引用操作对象。

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);

使用两个不同的引用关联创建两个不同的Point对象。

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

在这条线上'传递价值'进入游戏......

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

参考文献pnt1pnt2按值传递给棘手的方法,这意味着现在你参考pnt1,并pnt2有自己的copies命名arg1arg2。所以pnt1,并arg1 到同一个对象。(与pnt2和相同arg2

tricky方法中:

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

接下来的tricky方法

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

在这里,您首先创建新的tempPoint引用,它将指向相同的位置,如arg1引用。然后您将参考arg1像同一个地方arg2的参考。最后arg2指向同一个地方temp

从这里的范围tricky方法走了,你没有获得任何更多的引用:arg1arg2temp但重要的是,当你在生活中使用这些引用时所做的一切都会永久地影响它们所指向的对象。

所以在执行方法之后tricky,当你返回时main,你有这种情况:

所以现在,完全执行程序将是:

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



Java总是按值传递,而不是通过引用传递

首先,我们需要了解通过值传递的内容以及通过引用传递的内容。

按值传递意味着您在内存中复制传入的实际参数值。这是实际参数内容的副本

通过引用传递(也称为按地址传递)意味着存储实际参数的地址的副本

有时Java可以给出通过引用传递的错觉。让我们看看它是如何工作的,使用下面的例子:

public class PassByValue {
    public static void main(String[] args) {
        Test t = new Test();
        t.name = "initialvalue";
        new PassByValue().changeValue(t);
        System.out.println(t.name);
    }

    public void changeValue(Test f) {
        f.name = "changevalue";
    }
}

class Test {
    String name;
}

该程序的输出是:

changevalue

让我们一步一步地理解:

Test t = new Test();

众所周知,它将在堆中创建一个对象并将引用值返回给t。例如,假设t的值是0x100234(我们不知道实际的JVM内部值,这只是一个例子)。

new PassByValue().changeValue(t);

将参考t传递给函数时,它不会直接传递对象测试的实际参考值,但会创建t的副本,然后将其传递给函数。由于它是通过值传递的,因此它传递变量的副本而不是它的实际引用。由于我们说t的值是0x100234,t和f都将具有相同的值,因此它们将指向同一个对象。

如果使用引用f更改函数中的任何内容,它将修改对象的现有内容。这就是我们获得输出的原因changevalue,该输出在函数中更新。

为了更清楚地理解这一点,请考虑以下示例:

public class PassByValue {
    public static void main(String[] args) {
        Test t = new Test();
        t.name = "initialvalue";
        new PassByValue().changeRefence(t);
        System.out.println(t.name);
    }

    public void changeRefence(Test f) {
        f = null;
    }
}

class Test {
    String name;
}

这会抛出NullPointerException吗?不,因为它只传递了引用的副本。在通过引用传递的情况下,它可能抛出一个NullPointerException,如下所示:

希望这会有所帮助。




不,它不是通过引用传递。

根据Java语言规范,Java是按值传递的:

当调用方法或构造函数(第15.12节)时,实际参数表达式的值在执行方法体或构造函数体之前初始化新创建的参数变量,每个声明的类型。出现在DeclaratorId中的标识符可以用作方法或构造函数体中的简单名称来引用形式参数




Java是按值调用的。

这个怎么运作

  • 您总是传递参考值的位副本!

  • 如果它是原始数据类型,则这些位包含原始数据类型本身的值,这就是为什么如果我们更改方法内的header的值,那么它不会反映外部的更改。

  • 如果它是一个像Foo foo = new Foo()这样的对象数据类型,那么在这种情况下,对象地址的副本会像文件快捷方式一样传递,假设我们在C:\ desktop中有一个文本文件abc.txt,并假设我们设置了快捷方式相同的文件,并把它放在C:\ desktop \ abc-shortcut中,所以当你从C:\ desktop \ abc.txt访问文件并写入''并关闭文件时再次从快捷方式打开文件然后你write '是程序员学习的最大的在线社区'然后总文件更改将是'是程序员学习的最大的在线社区'这意味着从打开文件的位置开始无关紧要,每次我们访问同一个文件时,我们都可以假设Foo为文件,并假设foo存储在123hd7h(原始地址如C:\ desktop \ abc.txt)地址和234jdid(复制地址如C:\ desktop \ abc-shortcut,其中实际上包含了文件的原始地址)。所以为了更好地理解制作快捷方式文件和感觉...




我想我会提供这个答案,以便从规格中添加更多细节。

首先,通过引用传递与传递值之间的区别是什么?

通过引用传递意味着被调用函数的参数将与调用者的传递参数相同(不是值,而是标识 - 变量本身)。

按值传递意味着被调用函数的参数将是调用者传递的参数的副本。

或者来自维基百科,关于传递参考的主题

在逐个引用的评估(也称为pass-by-reference)中,函数接收对用作参数的变量的隐式引用,而不是其值的副本。这通常意味着函数可以修改(即赋值)用作参数的变量 - 其调用者将看到的内容。

对主题传递的价值

在call-by-value中,计算参数表达式,并将结果值绑定到函数[...]中的相应变量。如果函数或过程能够为其参数赋值,则仅指定其本地副本[...]。

其次,我们需要知道Java在其方法调用中使用了什么。在Java语言规范状态

当调用方法或构造函数(第15.12节)时,实际参数表达式的值在执行方法体或构造函数体之前初始化新创建的参数变量,每个声明的类型。

因此它将参数的值赋予(或绑定)到相应的参数变量。

论证的价值是什么?

让我们考虑引用类型,在Java虚拟机规范状态

有三种引用类型:类类型,数组类型和接口类型。它们的值分别是对动态创建的类实例,数组或类实例或实现接口的数组的引用。

Java语言规范还规定

引用值(通常只是引用)是指向这些对象的指针,以及一个特殊的空引用,它指的是没有对象。

参数(某种引用类型)的值是指向对象的指针。请注意,变量,具有引用类型返回类型的方法的调用以及实例创建表达式(new ...)都将解析为引用类型值。

所以

public void method (String param) {}
...
String var = new String("ref");
method(var);
method(var.toString());
method(new String("ref"));

all将String实例引用的值绑定到方法新创建的参数中param。这正是pass-by-value的定义所描述的。因此,Java是按值传递的

您可以按照引用来调用方法或访问引用对象的字段这一事实与对话完全无关。传递参考的定义是

这通常意味着函数可以修改(即赋值)用作参数的变量 - 其调用者将看到的内容。

在Java中,修改变量意味着重新分配它。在Java中,如果您在方法中重新分配了变量,那么调用者就不会注意到它。修改变量引用的对象完全是一个不同的概念。

here还在Java虚拟机规范中定义了原始值。该类型的值是相应的积分或浮点值,适当编码(8,16,32,64等位)。




正如许多人之前提到的那样,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的引用只会在本地范围内更改,而不会在此方法之外。




在Java中,只传递引用并按值传递:

Java参数都是按值传递的(当方法使用时会复制引用):

对于基本类型,Java行为很简单:将值复制到基本类型的另一个实例中。

在Objects的情况下,这是相同的:对象变量是仅包含使用“new”关键字创建的Object 地址的指针(桶),并且像原始类型一样被复制。

行为可能与原始类型不同:因为复制的对象变量包含相同的地址(对于同一个对象)对象的内容/成员可能仍然在方法中被修改并且稍后访问外部,给出了(包含)对象的错觉本身是通过引用传递的。

“字符串”对象似乎是城市传说中“对象通过引用传递” 的完美反例

实际上,在一个永远不可能的方法中,更新作为参数传递的String的值:

String对象,由声明为final的数组保存,不能修改。只有Object的地址可能被另一个使用“new”替换。使用“new”更新变量,不会让Object从外部访问,因为变量最初是按值传递并复制的。




我已经为here任何编程语言创建了一个致力于这类问题的线程。here

还提到了Java。以下是简短摘要:

  • Java按值传递参数
  • “by value”是java将参数传递给方法的唯一方法
  • 使用作为参数给出的对象的方法将改变对象,因为引用指向原始对象。(如果该方法本身改变某些值)



简而言之,Java对象具有一些非常特殊的属性。

一般来说,Java有原始类型(intboolchardouble,等等)直接由值来传递。然后Java有对象(从中派生出来的所有东西java.lang.Object)。实际上,对象总是通过引用来处理(引用是一个你无法触摸的指针)。这意味着实际上,对象是通过引用传递的,因为引用通常不是很有趣。但它确实意味着您无法更改指向的对象,因为引用本身是按值传递的。

这听起来有点奇怪和令人困惑吗?让我们考虑C如何通过引用传递并按值传递。在C中,默认约定是按值传递。void foo(int x)按值传递int。void foo(int *x)是一个不需要的函数int a,而是一个指向int的指针:foo(&a)。可以使用它与&运算符传递变量地址。

把它带到C ++,我们有参考。引用基本上(在此上下文中)隐藏等式的指针部分的语法糖:void foo(int &x)被调用foo(a),其中编译器本身知道它是引用,并且a应该传递非引用的地址。在Java中,所有引用对象的变量实际上都是引用类型,实际上强制通过引用调用大多数意图和目的,而没有由例如C ++提供的细粒度控制(和复杂性)。




Related