一文带你秒懂Java为什么只有值传递

 更新时间:2024年11月27日 08:33:24   作者:渊渟岳  
值传递是指在调用函数时将实际参数复制一份传递到函数中,引用传递是指在调用函数时将实际参数的引用直接传递到函数中,本文将详细介绍Java中值传递的相关知识,感兴趣的可以了解下

在Java语言中,数据类型分为基本数据类型和引用数据类型。

基本数据类型(如intdoublechar等)的值直接保存在栈上。这些类型的变量在栈内存中有固定的大小,并且值是直接存储在这些变量中的,数据的传递为值传递,这个好理解。以下以引用数据类型来讲解。

引用和实例化对象

比如new一个对象的代码:等号左边的person为引用;等号的右边new Person()为实例化对象。

Person person = new Person();
引用 = 实例化对象

引用和实例化对象在内存中,分别保存在栈和堆中。引用会保存着实例化对象的地址,从而可以通过引用来获取到具体的实例化对象保存在哪里。

关系如图:

值传递和引用传递

顾名思义,值传递是把“值”传递到方法中,而引用传递是把“引用”传递到方法中。

在Java 的参数传递方式中,只有值传递。对于引用对象也是值传递,而这个值是引用的值(即堆地址或句柄)被拷贝传递到方法中。

文字太抽象了,看图和代码:

对比两者

值传递

源引用地址:0x0a --> 对象地址:0x10

传递时:新引用地址:0x0b --> 对象地址:0x10

引用传递

源引用地址:0x0a --> 对象地址:0x10

传递时:方法中仍是引用地址:0x0a --> 对象地址:0x10

值传递:是复制一份“引用”传给方法的形参,person 和 person2 是两个不同的栈内存地址(如 0x0a0x0b

引用传递:是将实参的引用直接传给方法的形参,person 和 person2 实际共享了相同的栈内存地址(如 0x0a

两者有着本质的区别!对比两者的行为和带来的影响。

对比示意图

行为值传递(Java 的实际行为)引用传递(假设机制)
方法参数接收到的值引用地址的副本(如 0x0b)原始引用地址本身(如 0x0a)
引用的改变影响范围改变方法内的引用指向,不影响原始引用改变引用指向会影响原始引用
对象属性的修改通过引用修改对象属性,会影响原始对象通过引用修改对象属性,会影响原始对象

代码验证

测试代码:测试引用的改变影响范围和对象属性的修改

public class ValuePassDemo {

    public static void main(String[] args) {
        Person person = new Person();
        person.setAge(18);
        person.setName("Denny");
        // 初始值
        System.out.println("地址:"+ Integer.toHexString(person.hashCode()) + ">>>" + person);
        modifyReference(person);
        // 是否会被上一个方法修改值
        System.out.println("地址:"+ Integer.toHexString(person.hashCode()) + ">>>" + person);
        modifyReference2(person);
        // 是否会被上一个方法修改值
        System.out.println("地址:"+ Integer.toHexString(person.hashCode()) + ">>>" + person);
    }

    /**
     * 形参和实参引用指向的实例化对象是同一个
     * 实例化对象的值被任意一边修改时,都会改变
     */
    public static void modifyReference(Person person2) {
        person2.setAge(28);
        person2.setName("Jack");
        System.out.println("地址:"+ Integer.toHexString(person2.hashCode()) + ">>>" + person2);
    }

    /**
     * 如果是引用传递,
     *   那实参引用 person 等于形参引用 person2,
     *   那么引用 person2 的指向被改变的话,形参引用 person也会指向新的实例化对象
     * 如果不成立,那就是值传递,引用person2 只是引用person的拷贝,而非本身给了它
     */
    public static void modifyReference2(Person person2) {
        person2 = new Person();
        person2.setAge(20);
        person2.setName("apple");
        System.out.println("地址:"+ Integer.toHexString(person2.hashCode()) + ">>>" + person2);
    }
}

测试结果:

方法内部修改引用的指向

调用modifyReference2方法,会给形参变量赋一个新的实例化对象的情况,

如果是值传递,形参和实参分别指向不同的实例化对象,如图:

如果是引用传递,形参和实参都指向相同的实例化对象,而原来的实例化对象就没有引用指向。

如图:

原来的实例化对象没有引用指向,会导致内存泄漏,用C++的语言描述:按引用传递时,并且在方法内修改引用指向新new的对象时,需要手动释放内存。

  • void myFunction(Person* obj) 是按值传递
  • void myFunction(Person*& obj) 是引用传递
void myFunctionWithReference(Person*& obj) {
    delete obj;  // 先释放原对象的内存
    obj = new Person(20);  // 重新分配新的对象,并让 obj 指向它
}

为什么Java只有值传递

个人觉得Java 选择只有值传递的参数传递机制(pass-by-value)目的应该包含:内存安全性、简化内存管理、保持语言行为一致性和语言简单易用。

这也是Java语言的优点,弱化对内存操作的概念,让这门语言更加简洁易用;同时这也是Java语言的缺点,降低了灵活性,无法直接通过方法修改引用变量的指向。

到此这篇关于一文带你秒懂Java为什么只有值传递的文章就介绍到这了,更多相关Java值传递内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java中接口和抽象类的区别详解

    Java中接口和抽象类的区别详解

    这篇文章主要介绍了Java中接口和抽象类的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • springboot中的@value取不到正确的值问题

    springboot中的@value取不到正确的值问题

    这篇文章主要介绍了springboot中的@value取不到正确的值问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • JAVA多线程处理for循环数据详细讲解

    JAVA多线程处理for循环数据详细讲解

    这篇文章主要给大家介绍了关于JAVA多线程处理for循环数据的相关资料,我们在代码中经常需要使用for循环这个操作来达到目的,而当for循环的次数过多时我们会发现执行效率会变的很低,整体耗时非常多,需要的朋友可以参考下
    2023-07-07
  • Java 并行数据处理和性能分析

    Java 并行数据处理和性能分析

    这篇文章主要介绍了Java 并行数据处理和性能分析,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-10-10
  • SpringBoot注解梳理(小结)

    SpringBoot注解梳理(小结)

    这篇文章主要介绍了SpringBoot注解梳理(小结),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-10-10
  • Maven中jar包冲突原理与解决办法

    Maven中jar包冲突原理与解决办法

    这篇文章主要介绍了Maven中jar包冲突原理与解决办法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • mybatis调用mysql存储过程并获取返回值方式

    mybatis调用mysql存储过程并获取返回值方式

    这篇文章主要介绍了mybatis调用mysql存储过程并获取返回值方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • java 进程是如何在Linux服务器上进行内存分配的

    java 进程是如何在Linux服务器上进行内存分配的

    这篇文章主要介绍了java 进程是如何在Linux服务器上进行内存分配的,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下
    2020-11-11
  • SpringBoot整合Netty的流程步骤

    SpringBoot整合Netty的流程步骤

    Netty是一个基于Java的开源网络应用框架,它提供了高性能、异步事件驱动的网络编程能力,Netty旨在帮助开发者构建高性能、高可靠性的网络应用程序,本文给大家详细介绍了SpringBoot整合Netty的流程步骤,需要的朋友可以参考下
    2023-09-09
  • 简介Java程序的Shell脚本包装

    简介Java程序的Shell脚本包装

    这篇文章主要介绍了简介Java程序的Shell脚本包装,将Java运用于脚本程序当中,有时或许是个不错的主意~需要的朋友可以参考下
    2015-07-07

最新评论