切换语言为:繁体
从底层内存层面分析 Java 引用传递与值传递的不同

从底层内存层面分析 Java 引用传递与值传递的不同

  • 爱糖宝
  • 2024-08-12
  • 2057
  • 0
  • 0

前言

在Java编程中,参数传递的方式主要分为值传递和引用传递。这两种方式决定了方法内部对参数的修改如何影响原始变量。本文将详细探讨这两种传递方式的概念、应用及注意事项,并结合面试题,分析栈堆内存的情况。

一、值传递与引用传递的区别

  1. 值传递

    • 在方法调用时,将实际参数(原始变量)的副本传递给形式参数。

    • 方法内部对形式参数的修改不会影响实际参数。

    • 值传递适用于基本数据类型(如int、float、double等)和引用数据类型的包装类(如Integer、Float、Double等)。

  2. 引用传递

    • 在方法调用时,将实际参数的引用(内存地址)传递给形式参数。

    • 方法内部对形式参数的修改可能会影响实际参数。

    • 引用传递通常发生在对象类型上。

二、面试案例分析

我们来看一个面试题,代码如下,看起来就几行代码很简单,那么到时a和b最终结果是什么呢?

从底层内存层面分析 Java 引用传递与值传递的不同

运行结果:输出的是AB B,而不是AB AB,是不是觉得有点惊讶,下面开始分析一下具体原因。

从底层内存层面分析 Java 引用传递与值传递的不同

栈堆内存分析

  1. 初始状态

    • 在栈内存中,有两个变量ab,它们分别指向堆内存中的两个对象AB

    • 具体来说,a的内存地址是000x1,它指向的对象是Ab的内存地址是000x2,它指向的对象是B

  2. 方法调用

    • 当调用operator(a, b)时,栈内存中新增了两个局部变量xy

    • 这两个局部变量也分别指向堆内存中的对象AB,即x的内存地址是000x1y的内存地址是000x2

  3. 操作过程

    • operator方法内部,执行x.append(y)操作。此时,x指向的对象从A变成了AB,内存地址仍然是000x1

    • 接着执行y = x操作。此时,y不再指向原来的对象B,而是指向x所指向的对象AB,即y的内存地址也变成了000x1

  4. 最终状态

    • operator方法执行完毕后,栈内存中的变量ab的指向没有发生变化。

    • a仍然指向堆内存中的对象AB,内存地址是000x1b仍然指向堆内存中的对象B,内存地址是000x2

    • 因此,输出结果是AB B

如图最终栈堆内存图: 从底层内存层面分析 Java 引用传递与值传递的不同

如果改成下面代码,就可以看到四个变量最终的值了,跟我们上面分析的结果一样。

java代码解读复制代码public class Test {     public static void main(String[] args) {         StringBuffer a = new StringBuffer("A");         StringBuffer b = new StringBuffer("B");         operator(a, b);         System.out.println(a + " " + b);     }      public static void operator(StringBuffer x, StringBuffer y) {         x.append(y);         y = x;         System.out.println("y:" + y + " ,x:" + x);     } }

这样就更加清晰看出,上述通过栈堆内存图分析是正确,运行结果如图所示:

从底层内存层面分析 Java 引用传递与值传递的不同

三、总结

这个问题涉及到了变量的作用范围和方法参数传递机制:

  1. 变量作用范围

    • xy只在operator方法内部有效,不会影响外部变量ab

  2. 方法参数传递机制

    • 形参是基本数据类型时,传递的是数据值。

    • 形参是引用数据类型时,传递的是地址值。

    • 特殊类型如String、数组、包装类等对象是不可变的。

通过这个案例,可以清楚地看到值传递和引用传递的区别及其应用。希望本文能帮助读者更好地理解Java中的参数传递机制。

0条评论

您的电子邮件等信息不会被公开,以下所有项均必填

OK! You can skip this field.