在Java中是按值传递还是按引用传递?

7

我读了很多文章都说Java是按值传递的。但是我仍然不理解按值传递和引用传递之间的区别。我写了一个示例程序并执行它,结果如下...

public class PassByValue {

   private int a;
   private int b;

   public PassByValue(int a, int b) {
    this.a = a;
    this.b = b;
   }

   public static int mul(int a, int b) {

       int z = a * b;
       System.out.println("The Value of Z inside mul: " + z);
       return z;

   }

   public static void main(String args[]) {

    int z = 100;

        int x = 40;
        int y = 20;

    mul(x,y);

    PassByValue pass = new PassByValue(x,y);
    mul(pass.a,pass.b);

        System.out.println("The Value of Z" + z);

   }

}

执行

800
800 and 
100

有人可以为我解释一下以下问题吗...

  1. 什么是传值(Pass By Value)的意思...

回答:这只是将变量中存储的数字或值传递给函数。我对吗还是错。

  1. 你如何说Java是传值(Pass By Value)的?
  2. 为什么Java是传值(Pass By Value),而不是传引用(Pass By Reference)?
  3. 上述程序是否展示了传值和传引用的示例...但是它仍然只通过传值来完成...我写了那个程序。

4
我们需要问那个问题多少次?看看任何一个“相关”的链接,很有可能阅读其中两个已经被回答而没有被关闭为重复问题的链接可以澄清事情。 - user395760
https://dev59.com/EXVD5IYBdhLWcg3wQJOT#7034719 - kommradHomer
可能是Java是按引用传递还是按值传递?的重复问题。 - Raedwald
4个回答

18
混淆可能是由于变量本身不能包含对象所致。变量只能包含对对象的引用。(换句话说,对象根本没有被传递,不是按引用传递,也不是按值传递。)一旦你意识到这一点,就很清楚在Java中没有任何按引用传递的情况。指向对象的变量存储一个引用,并且这个引用是按值传递的。

1.什么是按值传递...

答:它只是将存储在变量中的数字或值传递给函数。我是对还是错。

没错。传递的是变量中包含的值,而不是变量本身。

1.你如何表达Java是按值传递的?

Java是按值传递的,因为原始类型是按值传递的,引用也是按值传递的。(对象永远不会被传递。)例如,在Java中无法实现交换方法。也就是说,你不能这样做:

String str1 = "hello";
String str2 = "world";
swap(str1, str2);
// str1 can't refer to anything but "hello"
// str2 can't refer to anything but "world"

2. 为什么Java是按值传递而不是按引用传递?

正如上面所解释的那样,即使是引用也是按值传递的。


13

你的回答是正确的,但缺乏细节。如果你执行:

Dog d = new Dog()

d 是一个指向 Dog 实例的引用,也就是说,当你将 d 传递给一个方法时,传递进去的只是该引用的副本(即该引用的值,而不是引用本身)。因此,你有两个引用指向同一个 Dog 实例,在调用范围内和被调用范围内各一个。假设在 walkDog 方法中有一行代码:

d = new Dog();

那么方法中的 d 引用仅指向新创建的 Dog 实例。最初调用方法的引用仍然指向原始的 Dog 实例。如果 Java 支持按引用传递,那么在方法中将使用相同的引用,而不是引用的副本,因此更改引用的值将影响调用范围和被调用范围中的引用。

编辑 -- 基于你的评论,我想澄清一件事情。由于原始范围和方法范围中的引用都指向同一个对象,所以你仍然可以在两个范围内更改该对象上的内容。因此,如果在 walkDog 方法中执行:

d.drinkWater();

并且 drinkWater 改变了 Dog 对象上的某个变量,那么由于两个引用都指向同一个对象,该对象已被更改。

这只是每个范围内的引用实际上是什么的区别。但它确实经常出现。


hvgotcodes:谢谢...解释得非常好...如果我错了,请纠正我,我没有将原始变量或引用发送到我的函数中...而是发送了一个副本...因此该值仅在该范围内更改,而不是在任何地方都更改... - theJava
因此,复制引用和原始引用指向同一地址位置... 但是指向 DOG 的不同实例。 - theJava
2
@theJava,不,有两个指向同一只狗的引用。更改其中一个引用不会更改另一个引用,但使用任何一个引用来更改对象都会更改同一个对象。 - hvgotcodes
对于C语言开发者而言:这等同于在C语言中传递指针,然后在进入函数时立即复制该指针以在函数内部使用。除非您在函数内更改指针的值,否则指针所指向的数据是相同的。 - mikebabcock
@hvgotcodes,为什么将字符串传递到方法中时它们不会改变呢?因为它们是对象,而不是原始数据类型。 - OKGimmeMoney

3

您可以将按值传递视为仅传递变量中包含的值而不是变量本身。因此,该值被复制到另一个临时变量中。相比之下,您可以将按引用传递视为传递实际变量,不会进行任何临时副本,因此对变量的任何更改都将“保存”。虽然还有更多要说,但这可能更容易理解。


1
我认为,与其在Java中寻找支持按引用或值传递的方法,不如清楚地了解在实现中如何使用Java类的实例。这一切都取决于实例的类型——可变/不可变,这将决定我们将事物传递给函数的方式!这取决于您来探索这种差异!
让我澄清一下为什么没有必要追溯传递什么的论点。考虑以下第一段代码:
    void foobar(int *a){
        printf("%d", *a);
    }

    int main(){
        int a = 5;
        foobar(&a);
        return 0;
    }

在这段C代码中,你传递的是变量'a'的地址。这恰好是按引用传递! :)
让我们考虑另一个例子... 第二段代码:
    void foobar(int* a){
        printf("%d", *a);
    }

    int main(){
        int a = 5;
        int *p = &a;
        foobar(p);
        return 0;
    }

在这个 C 代码中,我传递的是什么……?变量 'p' 的值,无论它是指针还是其他什么都无所谓 :P

那么你如何称呼这种传递方式呢?传值还是传引用?这就由你决定了!但我们需要关注的是我们如何实现它... :)

因此,在 Java 中……我们要通过传递的内容来说——它支持 "传值" 或 "传某个实例的引用",而不是 "传引用"

***我能清楚地得出的唯一结论是:在 Java 中,对于原始数据类型而言,我们不能使用传引用(我的意思是指原始数据类型),因为没有指针可以在没有实际变量的情况下编辑字节的内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接