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

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

引用和实例化对象

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

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

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

关系如图:

值传递和引用传递

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

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

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



对比两者

值传递

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

引用传递

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

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

引用传递:是将实参的引用直接传给方法的形参,personperson2 实际共享了相同的栈内存地址(如 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语言的缺点,降低了灵活性,无法直接通过方法修改引用变量的指向。

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

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

单例模式及其思想

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

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

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

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

软考证书=职称证书?

什么是设计模式?

秒懂Java为什么只有值传递的更多相关文章

  1. Java 中的值传递和参数传递

    Java中没有指针,所以也没有引用传递了,仅仅有值传递不过可以通过对象的方式来实现引用传递 类似java没有多继承 但可以用多次implements 接口实现多继承的功能 值传递:方法调用时,实际参数 ...

  2. Java中的值传递和引用传递

    这几天一直再纠结这个问题,今天看了这篇文章有点思路了,这跟C++里函数参数为引用.指针还是有很大区别. 当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里 ...

  3. java是通过值传递,也就是通过拷贝传递——通过方法操作不同类型的变量加深理解(勿删)

    head first java里写到“java是通过值传递的,也就是通过拷贝传递”,由此得出结论,方法无法改变调用方传入的参数.该怎么理解呢? 看例子: public class Test1 { pu ...

  4. Java中的值传递

    1.先比较下字符串的比较 == 代表全等于 值和地址(存放地址) 全部相等于. equals 值等于== 和 equals的区别 列如下面的 如果name1==name2是等于的 然而name1==n ...

  5. 为什么说Java中只有值传递

    本文转载自公众号 Hollis 对于初学者来说,要想把这个问题回答正确,是比较难的.在第二天整理答案的时候,我发现我竟然无法通过简单的语言把这个事情描述的很容易理解,遗憾的是,我也没有在网上找到哪篇文 ...

  6. 一道笔试题来理顺Java中的值传递和引用传递

      题目如下: private static void change(StringBuffer str11, StringBuffer str12) { str12 = str11; str11 = ...

  7. java中的值传递和引用传递有什么区别呀?

    值传递: (形式参数类型是基本数据类型和String):方法调用时,实际参数把它的值传递给对应的形式参数,形式参数只是用实际参数的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形式参 ...

  8. 为什么说Java中只有值传递(转载)

    出处:https://www.hollischuang.com/archives/2275 关于这个问题,在StackOverflow上也引发过广泛的讨论,看来很多程序员对于这个问题的理解都不尽相同, ...

  9. 为什么说Java中只有值传递?

    一.为什么说Java中只有值传递? 对于java中的参数传递方式中是否有引用传递这个话题,很多的人都认为Java中有引用传递,但是我个人的看法是,Java中只有值传递,没有引用传递. 那么关于对象的传 ...

  10. JAVA中只有值传递

    今天,我在一本面试书上看到了关于java的一个参数传递的问题: 写道 java中对象作为参数传递给一个方法,到底是值传递,还是引用传递? 我毫无疑问的回答:“引用传递!”,并且还觉得自己对java的这 ...

随机推荐

  1. 肉夹馍(Rougamo)4.0.1 异步方法变量调试修复与IoC系列扩展

    肉夹馍(https://github.com/inversionhourglass/Rougamo),一款编译时AOP组件,无需在应用启动时进行初始化,也无需繁琐的配置:支持所有种类方法(同步和异步. ...

  2. 同步多个mysql 到一个

    了解大概 Ref: is it possible that canal set with multiple mysql database source 使用 canal https://dev.mys ...

  3. Openstack-dashboard默认配额

    创建实例不超过10个 方法一 修改配置文件 在使用openstack的过程中,默认创建的实例最多10个,这是因为配额默认实例就是10 所以我们需要修改配置文件/etc/nova/nova.conf中的 ...

  4. Opensack-T版脚本安装

    openStack-train 搭建部署 项目环境: 主机名 外网口(net) 内口(仅主机)s 配置 controller 192.168.220.10/192.168.220.1/24 192.1 ...

  5. containerd 拉取k8s.gcr.io/pause镜像i/o timeout

    由于k8s.gcr.io 需要连外网才可以拉取到,导致 k8s 的基础容器 pause 经常无法获取.k8s docker 可使用代理服拉取,再利用 docker tag 解决问题 docker pu ...

  6. Failed to convert value of type 'java.lang.String' to required type

    DEBUG 微信小程序Java后台 Failed to convert value of type 'java.lang.String' to required type 产生这种条件的原因一般是使用 ...

  7. 适用于 VitePress 的公告插件开发实记

    前言 笔者维护的 VitePress 博客主题在近1年多的时间里集成了非常多功能,不少用户希望将里面的部分功能分离出来,方便在其它 VitePress 站点也可以独立使用. 其中分离的第一个组件类型的 ...

  8. 浏览器中生成 OSS 令牌 | Web Crypto API

    笔者写文章的时候,都会把图片通过自己搭建的一个简单站点 https://imgbed.sugarat.top/ 把图片上传到各种云的对象存储服务(OSS)上. 然后通过CDN访问,保证图片有可靠的访问 ...

  9. USB2.0 的LPM和USB3.0的LPM区别

    USB 2.0 和 USB 3.0 都支持低功耗管理机制(LPM,Link Power Management),但两者的实现方式和目标不同.以下是 USB 2.0 的 LPM 和 USB 3.0 的 ...

  10. USB TCPM

    USB TCPM(Type-C Port Manager)的主要作用是管理 USB Type-C 端口的连接和电源传输协议(USB Power Delivery, PD),确保设备正确识别.协商和切换 ...