首页 > 编程语言 >死磕面试系列,Java到底是值传递还是引用传递?

死磕面试系列,Java到底是值传递还是引用传递?

时间:2022-11-04 10:48:20浏览次数:42  
标签:Java update 传递 面试 user main 方法

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等),实参和形参都是存储在不同的栈帧内,修改形参的栈帧数据,不会影响实参的数据。

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

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

image

标签:Java,update,传递,面试,user,main,方法
From: https://www.cnblogs.com/yidengjiagou/p/16856912.html

相关文章

  • Java核心工具库Guava介绍以及Optional和Preconditions使用进行非空和数据校验
    场景GuavaGuava项目是Google公司开源的Java核心库,它主要是包含一些在Java开发中经常使用到的功能,如数据校验、不可变集合、计数集合,集合增强操作、I/O、缓存、字......
  • JavaScript异或运算
    相关性质任何数和自己做异或运算,结果为0,即a⊕a=0a⊕a=0。任何数和0做异或运算,结果还是自己,即a⊕0=⊕a⊕0=⊕。异或运算中,满足交换律和结合律,也就是a⊕b⊕a=b⊕a⊕......
  • Java(screw)生成数据库表结构
    数据库支持MySQLMariaDBTIDBOracleSqlServerPostgreSQLCacheDB(2016)文档生成支持htmlwordmarkdown方式一:代码生成<dependency><groupId>cn......
  • java.lang.StackOverflowError错误的解决方法
    对于java.lang.StackOverflowError认识如下图所示,报出来这种错误的话,很大概率是有以下几种原因:现在来看一看我的报错界面:不难看出,这是无限循环的那种情况,所以,我就去看......
  • cmake-子cmakelists传递变量
    主cmake子cmakePARENT_SCOPE传递回主cmake结果......
  • Java四舍五入的常见方法(DecimalFormat 用法详解)
    对Java中DecimalFormat的所有基础用法进行了一个汇总。DecimalFormat类主要靠#和0两种占位符号来指定数字长度。0表示如果位数不足则以0填充,#表示只要有可能就......
  • 年前端react面试打怪升级之路
    react和vue的区别相同点:数据驱动页面,提供响应式的试图组件都有virtualDOM,组件化的开发,通过props参数进行父子之间组件传递数据,都实现了webComponents规范数据流动单......
  • 百度前端react面试题总结
    componentWillReceiveProps调用时机已经被废弃掉当props改变的时候才调用,子组件第二次接收到props的时候在调用setState之后发生了什么状态合并,触发调和:setState......
  • Java函数式编程(1):Lambda表达式(2)
    您好,我是湘王,这是我的博客园,欢迎您来,欢迎您再来~ 从之前的Lambda表达式的演变过程可以知道,Lambda表达式其实是一个对匿名内部类的简化过程:去掉了多余的语法修饰,只保留最......
  • javascript Swap two nodes调换两个元素的位置
    SwaptwonodesThefunctionbelowswapstwogivennodes,nodeAandnodeB:constswap=function(nodeA,nodeB){constparentA=nodeA.parentNode;c......