值传递&引用传递 区分不了? 看这一篇就够了

在 Java 中,所有传递给方法的参数都是通过值传递的方式进行传递,尽管有时我们提到“引用传递, 然而在 Java 中并不存在真正的引用传递,只在讨论引用类型(如对象)时,我们常常提到“引用传递”。实际上,传递的仍然是值,但这个值是对象引用的副本. 在计算机科学中,值传递是将参数的实际值(或引用,对于对象)复制给被调用函数的一种方式。这意味着:

  • 对于基本数据类型(如 intcharboolean 等),方法内部使用的是参数的副本。对这个副本的任何更改不会影响原始变量。
  • 对于对象(引用类型),传递的是对象引用的副本。这意味着你在方法中获得的是指向同一对象的指针;对对象的属性的更改会影响原始对象,但对引用的重赋值不会影响外部的引用。

定义看起来有些枯燥? 那接下来使用代码进行解释:

目录

1. 值传递

定义

示例:

结果:

总结:

2. 引用传递

定义

示例

结果:

总结

3. 内存分析

4. 总结

1. 值传递

定义

在 Java 中,所有传递给方法的参数都是通过值传递的方式进行传递。这意味着:

  • 当你传递一个基本数据类型(如 intcharboolean 等)的值时,实际上是传递了该值的一份副本。
  • 当你传递一个对象(引用类型),你传递的是对象的引用(内存地址)的副本,而不是对象本身。

示例:

public class ValuePassingExample {
    public static void main(String[] args) {
        int a = 10;
        modifyValue(a); // 传递的是 a 的值的副本
        System.out.println("a after modifyValue: " + a); // a 仍然是 10
    }

    public static void modifyValue(int number) {
        number = number + 5; // 修改的是副本,不会影响原始 a
    }
}

结果:

a after modifyValue: 10

总结:

  • 基本类型的参数传递时,原变量的值不会受到方法内部的影响。

2. 引用传递

定义

  • 当你传递一个对象引用时,传递的是引用的副本。方法内部对这个引用的修改会影响原始对象,因为它们指向的是同一个内存地址。

示例

class Person {
    String name;

    Person(String name) {
        this.name = name;
    }
}

public class ReferencePassingExample {
    public static void main(String[] args) {
        Person person = new Person("Alice"); // 1. 在堆中创建一个 Person 对象
        changeName(person); // 2. 将 person 的引用(指向 "Alice" 的对象)传递给 changeName
        System.out.println(person.name); // 输出 "Bob"  3. person 仍然指向原始对象
    }

    public static void changeName(Person p) {
        p.name = "Bob"; // 4. 修改了 p(指向原始对象)的内容
        p = new Person("Charlie"); // 5. 重新赋值 p,分配新的堆内存
    }
}

结果:

Person's name after modifyPerson: Bob

1.步骤分析

1. 创建对象:Person person = new Person("Alice");

  • 这行代码在堆内存中分配了一块内存,创建了一个新的 Person 对象,其 name 属性初始化为 "Alice"person 变量存储的是这个对象的引用(地址)。

2. 传递引用:changeName(person);

  • 方法 changeName 被调用,将 person 的引用(指向 "Alice" 这个对象)传递给参数 p。此时,pperson 都指向同一个堆内存中的对象。

 3. 修改对象内容:p.name = "Bob";

  • 这行代码通过引用 p 修改了 Person 对象的 name 属性,使其现在变成 Bob。此时,person.name 的值也变为 "Bob",因为它们指向同一个对象。

4.  重新赋值引用:p = new Person("Charlie");

  • 这行代码做了以下两件事:
  1. 在堆内存中开辟了一块新空间,创建了一个新的 Person 对象,其 name 属性初始化为 "Charlie"
  2. p 的引用指向新的 Person 对象,而不再指向原来的对象(现在是 "Bob" 中的内容)。

2.影响

  • 原始引用未改变:尽管 p 已经指向了一个新的对象("Charlie"),person 仍然保持不变,仍然指向原来的对象("Bob")。
  • 原始对象的内容仍然有效:当输出 System.out.println(person.name); 时,将得到 "Bob"

总结

  • 对象引用的参数传递时,原始对象的属性会受到影响;但是如果在方法内部重新赋值该引用,会导致原引用不变。

3. 内存分析

栈内存(main 方法栈帧)       栈内存(modifyValue 方法栈帧)
+-----------+                 +-----------+
| a = 10    | ---->         | number = 10|
+-----------+                 +-----------+
                               | number = 15|  <- modifyValue 中的 number 是 a 的副本
                               +-----------+

说明:

  • numbermodifyValue 方法中是 a 的副本,对 number 的更改不会影响 a,因此在 main 方法中 a 的值仍然是 10
栈内存(main 方法栈帧)                                      堆内存
+---------------------+                                     +-----------+
| person (引用)      | ---->                              | Person    |
+---------------------+                                     | name = "Alice" |
                                                          +-----------+
                                                          (堆)|
                                                            |
栈内存(modifyPerson 方法栈帧)                         +-----------+
+-----------+                                            | Person    |
| p (引用)  | ---------------------------------------->| name = "Bob"  |  <- p.name 被修改
+-----------+                                            +-----------+
| p = new Person("Charlie");  // 这个引用是局部的,不影响 person|
+-----------+

说明:

  • 传递的是引用的副本。personmain 方法和 pmodifyPerson 方法均指向同一个 Person 对象。
  • 当我们通过 p.name = "Bob" 修改对象的属性时,main 方法中的 person 也受到了影响。
  • p 被重新赋值为一个新的 Person 实例时,它只影响 p 的引用,不会改变 main 中的 person 引用。

4. 总结

  1. 所有的参数都是以值的形式传递(即副本),无论是基本数据类型还是引用类型。每个参数的传递都是创建一个新副本。这意味着,方法内部无法通过参数的副本来改变外部变量的引用,即: 对基本类型参数的更改不会影响外部变量.
  2. 对象内容的可变性:在方法中,我们可以修改对象的属性。这种修改会影响到原始对象,因为 pperson 指向相同的对象。
  3. 引用的不可变性:尽管我们可以修改对象的内容,但如果在方法内部用新的对象引用赋值参数(即 p = new Person("Charlie");),这将不会影响原始引用 person。这个赋值只会改变 p 所指向的对象,而不会改变 person 的指向。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值