Java到底是值传递还是引用传递?

这虽然是一个老生常谈的问题,但是对于没有深入研究过这块,或者Java基础不牢的同学,还是很难回答得让人满意。

可能很多同学能够很轻松的背出JVM、分布式事务、高并发、秒杀系统、领域模型等高难度问题,但是对于Java基础问题不屑一顾。这种抓大放小的初衷是对的,要是碰到深究基础细节的面试官,就抓瞎了。

今天一灯带你一块深入剖析Java传递的底层原理,看完这篇文章再去面试,面试官肯定要竖起大拇哥夸你:

“小伙子,你是懂Java传递的!”

1. 什么是形参和实参

形参: 就是形式参数,用于定义方法的时候使用的参数,是用来接收调用者传递的参数的。

实参: 就是实际参数,用于调用时传递给方法的参数。实参在传递给别的方法之前是要被预先赋值的。

/**
* @author 一灯架构
* @apiNote Java传递示例
**/
public class Demo { public static void main(String[] args) {
String name = "一灯架构"; // 这里的name就是实际参数
update(name);
System.out.println(name);
} // 这里方法参数列表中name就是形式参数
private static void update(String name) {
// doSomething
} }

在Java方法调用的过程中,就是把实参传递给形参,形参的作用域在方法内部。

2. 什么是值传递和引用传递

值传递: 是指在调用方法时,将实际参数拷贝一份传递给方法,这样在方法中修改形式参数时,不会影响到实际参数。

引用传递: 也叫地址传递,是指在调用方法时,将实际参数的地址传递给方法,这样在方法中对形式参数的修改,将影响到实际参数。

也就是说值传递,传递的是副本。引用传递,传递的是实际内存地址。这是两者的本质区别,下面会用到。

3. 测试验证

3.1 基本数据类型验证

先用基本数据类型验证一下:

/**
* @author 一灯架构
* @apiNote Java传递示例
**/
public class Demo { public static void main(String[] args) {
int count = 0;
update(count);
System.out.println("main方法中count:" + count);
} private static void update(int count) {
count++;
System.out.println("update方法中count:" + count);
} }

输出结果:

update方法中count:1
main方法中count:0

可以看到虽然update方法修改了形参count的值,但是main方法中实参count的值并没有变,但是为什么没有变?我们深究一下底层原理。

我们都知道Java基本数据类型是存储在虚拟机栈内存中,栈中存放着栈帧,方法调用的过程,就是栈帧在栈中入栈、出栈的过程。

当执行main方法的时候,就往虚拟机栈中压入一个栈帧,栈帧中存储的局部变量信息是count=0。

当执行update方法的时候,再往虚拟机栈中压入一个栈帧,栈帧中存储的局部变量信息是count=0。

修改update栈帧中数据,显然不会影响到main方法栈帧的数据。

3.2 引用类型验证

再用引用类型数据验证一下:

/**
* @author 一灯架构
* @apiNote Java传递示例
**/
public class Demo { public static void main(String[] args) {
User user = new User();
user.setId(0);
update(user);
System.out.println("main方法中user:" + user);
} private static void update(User user) {
user = new User();
user.setId(1);
System.out.println("update方法中user:" + user);
} }

输出结果:

update方法中user:User(id=1)
main方法中user:User(id=0)

由代码得知,update方法中重新初始化了user对象,并重新赋值,并不影响main方法中实参数据。

当执行main方法时,会在堆内存中开辟一块内存,在栈内存中压入一个栈帧,栈帧中存储一个引用,指向堆内存中的地址。

当调用update方法时,会把main方法的栈帧拷贝一份,再压入栈内存中,指向同一个堆内存地址。

当执行update方法,重新初始化user对象,并重新赋值的时候。会在堆内存中再开辟一块内存,再把栈内存中update栈帧指向新的堆内存地址,并修改新的堆内存中的数据。

从这里可以看出是值传递,修改了形参里面数据,实参并没有跟着变化。

3.3 同一地址的引用类型验证

/**
* @author 一灯架构
* @apiNote Java传递示例
**/
public class Demo { public static void main(String[] args) {
User user = new User();
user.setId(0);
update(user);
System.out.println("main方法中user:" + user);
} private static void update(User user) {
user.setId(1);
System.out.println("update方法中user:" + user);
} }

输出结果:

update方法中user:User(id=1)
main方法中user:User(id=1)

可以看出update方法修改user对象的属性,main方法中user对象也跟着变了。

这是不是说明Java支持引用传递呢?

并不是。这里在参数传递的过程中,只是把实参的地址拷贝了一份传递给形参,update方法中只修改了参数地址里面的内容,并没有对形参本身进行修改。

4. 总结

经过上述分析,Java参数传递中,不管传递的是基本数据类型还是引用类型,都是值传递

当传递基本数据类型,比如原始类型(int、long、char等)、包装类型(Integer、Long、String等),实参和形参都是存储在不同的栈帧内,修改形参的栈帧数据,不会影响实参的数据。

当传参的引用类型,形参和实参指向同一个地址的时候,修改形参地址的内容,会影响到实参。当形参和实参指向不同的地址的时候,修改形参地址的内容,并不会影响到实参。

我是「一灯架构」,如果本文对你有帮助,欢迎各位小伙伴点赞、评论和关注,感谢各位老铁,我们下期见

死磕面试系列,Java到底是值传递还是引用传递?的更多相关文章

  1. 面试官:兄弟,说说Java到底是值传递还是引用传递

    二哥,好久没更新面试官系列的文章了啊,真的是把我等着急了,所以特意过来催催.我最近一段时间在找工作,能从二哥的文章中学到一点就多一点信心啊! 说句实在话,离读者 trust you 发给我这段信息已经 ...

  2. 为什么不建议给MySQL设置Null值?《死磕MySQL系列 十八》

    大家好,我是咔咔 不期速成,日拱一卒 之前ElasticSearch系列文章中提到了如何处理空值,若为Null则会直接报错,因为在ElasticSearch中当字段值为null时.空数组.null值数 ...

  3. 无法复现的“慢”SQL《死磕MySQL系列 八》

    系列文章 四.S 锁与 X 锁的爱恨情仇<死磕MySQL系列 四> 五.如何选择普通索引和唯一索引<死磕MySQL系列 五> 六.五分钟,让你明白MySQL是怎么选择索引< ...

  4. 为什么不让用join?《死磕MySQL系列 十六》

    大家好,我是咔咔 不期速成,日拱一卒 在平时开发工作中join的使用频率是非常高的,很多SQL优化博文也让把子查询改为join从而提升性能,但部分公司的DBA又不让用,那么使用join到底有什么问题呢 ...

  5. Java调用函数传递参数到底是值传递还是引用传递

    今天翻看微信上有关Java技术的公众号时,看到了一篇关于Java中值传递的问题,文章讨论了在Java中调用函数进行传参的时候到底是值传递还是引用传递这个面试时会问到的问题.之前也接触过类似的问题,但只 ...

  6. 一生挚友redo log、binlog《死磕MySQL系列 二》

    系列文章 原来一条select语句在MySQL是这样执行的<死磕MySQL系列 一> 一生挚友redo log.binlog<死磕MySQL系列 二> 前言 咔咔闲谈 上期根据 ...

  7. S 锁与 X 锁的爱恨情仇《死磕MySQL系列 四》

    系列文章 一.原来一条select语句在MySQL是这样执行的<死磕MySQL系列 一> 二.一生挚友redo log.binlog<死磕MySQL系列 二> 三.MySQL强 ...

  8. 如何选择普通索引和唯一索引《死磕MySQL系列 五》

    系列文章 一.原来一条select语句在MySQL是这样执行的<死磕MySQL系列 一> 二.一生挚友redo log.binlog<死磕MySQL系列 二> 三.MySQL强 ...

  9. 五分钟,让你明白MySQL是怎么选择索引《死磕MySQL系列 六》

    系列文章 二.一生挚友redo log.binlog<死磕MySQL系列 二> 三.MySQL强人"锁"难<死磕MySQL系列 三> 四.S 锁与 X 锁的 ...

随机推荐

  1. mysql存储过程的创建和调用

    描述:存储过程就是具有名字的一段代码,用来完成一个特定的功能.创建的存储过程保存在数据库的数据词典中. --创建一个名为GreetWorld的存储过程,拼接两个值 CREATE PROCEDURE G ...

  2. 若依3.6.0使用Mybatis-plus分页失效以及完美替换Pagehelper

    一.前言 小编最近在经历后端框架的迁移,虽然不是小编来做,但是有个分页的情况让小编和一个同事去搞. 说一下小编这边的需求: 原来框架使用Mybatis-plus进行分页,要更换的新框架若依是使用Pag ...

  3. PicGo+GitHub 图床搭建

    前言 用GitHub搭建图床,在很久之前我就有了解,但由于市面上有挺多免费的图床,比如我之前一直在用的 路过图床,所以一直懒得动手搭建GitHub图床.一直到前两天我在完善博客的相册时,发现 路过图床 ...

  4. 2020牛客NOIP赛前集训营-提高组(第二场)- B.包含 (FWT)

    题面 题解 这题就是个快速沃尔什变换的模板题,输入ai时,令s[ai]=1,对s[]做一遍DWT_AND(s)(快速沃尔什正变换,按位与),然后直接访问s[x]完事. #include<map& ...

  5. Java控制台打印三角形

    for (int i = 1; i <= 5; i++) { //最上面先是五个往下一次4.3.2.1 for (int j = 5; j >= i; j--) { System.out. ...

  6. KingbaseES 中select distinct on 语句

    用法 SELECT DISTINCT ON ( expression [, ...] ) 把记录根据[, -]的值进行分组,分组之后仅返回每一组的第一行. 需要注意的是,如果不指定ORDER BY子句 ...

  7. KingbaseES ksqlrc文件介绍

    ksqlrc文件作用 ksql在连接到数据库后但在接收正常的命令之前,会尝试读取并执行该文件中的命令,如果加上-X参数,则跳过该文件. 系统级的启动文件是ksqlrc,文件在安装好的KingbaseE ...

  8. Sys_Bulkload 工具使用

    一.介绍 sys_bulkload是KingbaseES提供的快速加载数据的命令行工具.用户使用sys_bulkload工具能够把一定格式的文本数据简单.快速的加载到KingbaseES数据库中,或将 ...

  9. JDK 自带的服务发现框架 ServiceLoader 好用吗?

    请点赞关注,你的支持对我意义重大. Hi,我是小彭.本文已收录到 Github · AndroidFamily 中.这里有 Android 进阶成长知识体系,有志同道合的朋友,关注公众号 [彭旭锐] ...

  10. 重复造轮子 SimpleMapper

    接手的项目还在用 TinyMapper 的一个早期版本用来做自动映射工具,TinyMapper 虽然速度快,但在配置里不能转换类型,比如 deleted 在数据库中用 0.1 表示,转换成实体模型时没 ...