多图证明,Java到底是值传递还是引用传递?

共 2779字,需浏览 6分钟

 ·

2020-09-05 16:03

作者 | 王磊

来源 | Java中文社群(ID:javacn666)

转载请联系授权(微信ID:GG_Stone)

开篇先来曝答案,在 Java 语言中,本质只有值传递,而无引用传递,解释和证明详见正文。

说到值传递和引用传递我们不得不提到两个概念:值类型和引用类型。

1.值类型

通俗意义上来说,所谓的值类型指的就是 Java 中的 8 大基础数据类型:

  • 整数型:byte、int、short、long
  • 浮点型:float、double
  • 字符类型:char
  • 布尔类型:boolean

从 JVM 层面来讲:所谓的值类型指的是在赋值时,直接在栈中(Java 虚拟机栈)生成值的类型,如下图所示:

2.引用类型

引用类型是指除值类型之外的数据类型,比如:

  • 接口
  • 数组
  • 字符串
  • 包装类(Integer、Double...)


从 JVM 的层面来讲,所谓的引用类型是指,在初始化时将引用生成栈上,而值生成在堆上的这些数据类型
,如下图所示:


PS:关于包装类为什么是引用类型?我们后面的文章会单独讲,记得关注:Java中文社群

3.值传递

值传递(Pass By Value)指的是方法传参时,传递的是原内容的副本,因此对副本进行如何修改都不会影响原内容。

实现代码如下:

public class PassTest {
    public static void main(String[] args) {
        int age = 18;
        System.out.println("调用方法前:" + age);
        intTest(age);
        System.out.println("调用方法后:" + age);
    }

    private static void intTest(int age) {
        age = 30;
        System.out.println("方法中修改为:" + age);
    }
}

程序的执行结果为:

调用方法前:18

方法中修改为:30

调用方法后:18

从上述结果可以看出,在方法中修改参数并未影响原内容,我们把这种传参方式称之为值传递。

4.引用传递

引用传递(Pass By Reference)指的是方法传参时,传递的是参数本身,因此对参数进行任意修改都会影响原内容。

模拟“引用传递”的实现代码如下:

public class PassTest {
    public static void main(String[] args) {
        char[] name = {'磊''哥'};
        System.out.println("调用方法前:" + new String(name));
        paramTest(name);
        System.out.println("调用方法后:" + new String(name));
    }
    private static void paramTest(char[] n) {
        n[1] = '神';
        System.out.println("方法中修改为:" + new String(n));
    }
}

程序的执行结果为:

调用方法前:磊哥

方法中修改为:磊神

调用方法后:磊神

从上述的结果可以看出在 paramTest 方法中修改了参数之后,在 main 方法中再打印参数时,发现参数的值也跟着发生了改变,那么似乎我们可以得出结论,Java 中貌似也有“引用传递”,然而实事并如此,我们接着看。

5.真假“引用传递”

我们给上面的代码添加一行,如下所示:

public class PassByValue {
    public static void main(String[] args) {
        char[] name = {'磊''哥'};
        System.out.println("调用方法前:" + new String(name));
        paramTest(name);
        System.out.println("调用方法后:" + new String(name));
    }
    private static void paramTest(char[] n) {
        n = new char[2]; // 添加此行代码
        n[1] = '神';
        System.out.println("方法中修改为:" + new String(n));
    }
}

程序的执行结果为:

调用方法前:磊哥

方法中修改为:神

调用方法后:磊哥

从上述结果可以看出,当我们在 paramTest 方法中添加 new char[] 之后,“引用传递”就突然变值传递了?为什么?

这是因为,在 Java 语言中本质上只有值传递,也就说 Java 的传参只会传递它的副本,并不会传递参数本身。

前面那个带引号的“引用传递”其实只是传递了它的引用副本,如下图所示:


PS:《Java虚拟机规范》中对  Java 堆的描述是:“所有的对象实例以及数组都应当在堆上分配”。

所以我们在调用 new char[] 之后,可以看出 n 对象有了新地址,而原内容并未被修改,如果按照引用传递的思路来看的话,不管执行任何方式的修改都会改变原内容,因此我们可以更加确认 Java 语言中只有值传递,如下图所示:

总结

通过本文的内容,我们可以得出:在 Java 语言中只有值传递,方法传参时只会传递副本信息而非原内容。我们还知道了基础数据类型会直接生成到栈上,而对象或数组则会在栈和堆上都生成信息,并将栈上生成的引用,直接指向堆中生成的数据,如下图所示:

1. 人人都能看懂的 6 种限流实现方案!

2. 一个空格引发的“惨案“

3大型网站架构演化发展历程

4Java语言“坑爹”排行榜TOP 10

5. 我是一个Java类(附带精彩吐槽)

6. 看完这篇Redis缓存三大问题,保你能和面试官互扯

7. 程序员必知的 89 个操作系统核心概念

8. 深入理解 MySQL:快速学会分析SQL执行效率

9. API 接口设计规范

10. Spring Boot 面试,一个问题就干趴下了!



扫码二维码关注我


·end·

—如果本文有帮助,请分享到朋友圈吧—

我们一起愉快的玩耍!



你点的每个赞,我都认真当成了喜欢

浏览 32
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报