秒懂Java为什么只有值传递

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

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

引用和实例化对象

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

java 复制代码
Person person = new Person();
引用 = 实例化对象

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

关系如图:

值传递和引用传递

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

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

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


对比两者

值传递

java 复制代码
源引用地址:0x0a --> 对象地址:0x10
传递时:新引用地址:0x0b --> 对象地址:0x10

引用传递

java 复制代码
源引用地址:0x0a --> 对象地址:0x10
传递时:方法中仍是引用地址:0x0a --> 对象地址:0x10

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

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

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

对比示意图

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

代码验证

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

java 复制代码
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) 是引用传递。
C++ 复制代码
void myFunctionWithReference(Person*& obj) {
    delete obj;  // 先释放原对象的内存
    obj = new Person(20);  // 重新分配新的对象,并让 obj 指向它
}

为什么Java只有值传递

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

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

超实用的SpringAOP实战之日志记录

软考中级--软件设计师毫无保留的备考分享

单例模式及其思想

2023年下半年软考考试重磅消息

通过软考后却领取不到实体证书?

计算机算法设计与分析(第5版)

Java全栈学习路线、学习资源和面试题一条龙

软考证书=职称证书?

什么是设计模式?

相关推荐
凤凰院凶涛QAQ9 小时前
《Java版数据结构 & 集合类剖析》Map & Set & 反射:“从哈希碰撞到红黑树转换,反射帮你扒开 HashMap 的桶数组看个究竟”
java·数据结构·算法
H.莓飛9 小时前
【数据结构】排序_OJ题(一):计数、桶与排序后的处理
java·开发语言·数据结构
wangchunyu1149 小时前
JAVA AQS与Lock实现原理(含CAS)
java·开发语言·word
w_zero_one11 小时前
算法--模拟
java·数据结构·算法
Javatutouhouduan11 小时前
阿里1658页Java面试题总结(含答案)
java·高并发·java面试·java面试题·后端开发·java程序员·java八股文
秋天的怀念_David11 小时前
算法一千零一夜 · 第十三夜 幻方,武侠世界里的数学迷宫
java·程序人生·算法
专业程序开发源11 小时前
springboot欧缇蔓月子中心管理系统82697-计算机课程设计、毕业设计
java·spring boot·后端·python·spring·django·课程设计
秋天的怀念_David12 小时前
算法一千零一夜 · 第十四夜 幻方与奇妙的二进制
java·程序人生·算法
thefool11226612 小时前
网络编程^
java
Thinker QAQ12 小时前
并发编程(十一):Shared Memory 与 Message Passing——关系与组合使用
java·python·go·并发编程·shared memory·message passing