用最简单的方式解释 Java 是值传递还是引用传递

相关文章:

本文目标:去掉高大上的专有名词,用最简单的方式解释“我这个 Java 方法到底传递的是啥”。

背景和其他(可跳过)

先用两句“废话”介绍一下背景,本段可直接跳过,昨天一做 C/C++ 好友想学习一下 Java,环境配置好了,IDEA 也装好了,“HelloWorld”也撸出来了。但是在看到《Java 核心技术 卷 Ⅰ》时候对“Java 是值传递还是引用传递”提出了疑问。

首先这本书相关的描述过于复杂和啰嗦,反而对本来就带着疑问去翻阅的人引入了很多新的概念,比如“按值调用”、“按引用调用”、“对象引用是按值传递的”,当然,这里并不是说这本书不好,作为一本专业书籍,理应用词严谨,但是如果是带着“ Java 是值传递还是引用传递”这个疑问去查看这本书,反而不一定能很快地得出结论,理解作者的本意。

所以本文的目标并不想去过多的比较描述什么是“值传递”,什么是“引用传递”,它们有什么区别等等,因为这些概念其实没那么大意义(Java 一大嘈点就是学院派风格严重),反而让人难以理解更重要的内容,而更重要的是在平时撸码的过程中能够理解“我这个方法到底传的是啥”

结论

先说结论:Java 传递的是值,如果是基本数据类型,这个值就是字面量的值,如果是引用数据类型,这个值就是地址(后续打算通过调试 OpenJDK 源码进行证明)。那为什么是地址呢,其实可以猜想下,完全没有必要对堆中的对象实例进行拷贝。

接下来就是举几个例子。

第一个例子,传递 int

/**
 * @author Dongguabai
 * @Description
 * @Date 创建于 2020-06-06 16:54
 */
public class PassTest2 {
    
    public static void main(String[] args) {
        int i = 10;
        System.out.println("before:" + i);
        updateInt(i);
        System.out.println("after:" + i);
    }

    private static void updateInt(int j) {
        j = 20;
    }
}

输出结果:

before:10
after:10

在 Java 中,基本数据类型是直接存放在虚拟机栈(局部变量表)中,在调用 updateInt() 方法时会将 i 的值进行拷贝:

在这里插入图片描述

第二个例子:

public class PassTest {

    public static void main(String[] args) {
        User user1 = new User("zhangsan1");
        System.out.println(user1);
        updateUser(user1);
        System.out.println(user1);
    }

    public static void updateUser(User user2) {
        user2.setUsername("修改");
    }

}

输出:

User{username='zhangsan1'}
User{username='修改'}

这时候发现原来的 user1 对象被更改了。再看第三个例子:

public class PassTest {

    public static void main(String[] args) {
        User user1 = new User("zhangsan1");
        User user2 = new User("zhangsan2");
        System.out.println(user1);
        System.out.println(user2);
        System.out.println("=================");
        swap(user1, user2);
        System.out.println(user1);
        System.out.println(user2);
    }

    public static void swap(User x, User y) {
        User temp = x;
        x = y;
        y = temp;
    }

}

输出:

User{username='zhangsan1'}
User{username='zhangsan2'}
=================
User{username='zhangsan1'}
User{username='zhangsan2'}

发现并未更改原始值。

先分析第二个例子:
在这里插入图片描述
user2 对象拷贝了 user1 对象的地址,这时候 user2 和 user1 指向的是堆中同一块内存(这里称为原始对象),所以这时候在 updateUser 函数中通过 user2 去修改原始对象,能更改是一个很正常的操作。

再分析第三个例子:
在这里插入图片描述

x 和 y 分别拷贝了 user1 和 user2 的地址值。发生交换:

在这里插入图片描述

数据没有发生变化的原因很简单,因为这里只更改了 x 和 y 的引用地址,跟 user1 和 user2 没有任何关系。

最后再总结一下,最关键的是理解传递的是什么,Java 传递的是值,如果是基本数据类型,这个值就是字面量的值,如果是引用数据类型,这个值就是地址。毋需纠结于“值传递”、“引用传递”这些概念。

欢迎关注公众号
​​​​​​在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章