Java只有值传递,没有引用传递!

结论:Java中的参数传递,只有值传递,没有引用传递!
以下均为错误理解:

  • 值传递和引用传递,区别在于传递的内容。如果是个值,就是值传递;如果是个引用,就是引用传递
  • Java是引用传递
  • 传递参数如果是普通类型,就是值传递,如果是对象,就是引用传递

1.基本概念

1.1.基本类型与引用类型

int num = 20;
String str = "hello";

  • num是基本类型,值直接保存在变量中
  • str是引用类型,变量中保存的是实际对象的地址,称这种变量为“引用”,引用指向实际对象,实际对象内容保存在堆中

1.2.赋值运算符“=”

基于上述定义变量:

num = 20;
str = "java";

  • 基本类型num,赋值运算符直接改变变量值,原本值被覆盖掉
  • 引用类型str,赋值运算符改变引用中保存的地址,原本的地址被覆盖(但原本对象不会被改变)

上述“hello”字符串对象未被改变(未被任何引用指向的对象会被GC回收掉)

2.值传递与引用传递

当调用一个有参函数时,会将实际参数传递给形参。在程序语言中,传递过程中传递有两种情况,即值传递和引用传递

  • 值传递:在调用函数时将实际参数复制一份传递到函数中,这样在函数对形参进行修改,将不会影响到原参
  • 引用传递:在调用函数时将实参的地址直接传递到函数中,在函数中对参数进行修改,会影响到原参

2.1.传递基本类型,不修改原值

public class Test {
    public static void main(String[] args) {
        Test test = new Test();
        int i = 10;
        test.pass(10);
        System.out.println("main:"+i); // 10
    }

    public void pass(int j) {
        // 修改参数j的值
        j = 20;
        System.out.println("pass:" + j); // 20
    }
}

在pass方法中,修改了参数j的值。从输入结果可知,pass方法内部对j的值修改并未改变实际参数i的值

2.2.传递引用类型,会修改原对象值

public class Test {
    public static void main(String[] args) {
        Test test = new Test();
        User user = new User("Mr.Q", "man");
        // 修改属性值
        test.pass(user);
        System.out.println("main:" + user);
    }

    public void pass(User user) {
        // 修改name的值
        user.setName("Tom");
        System.out.println("pass:" + user);
    }
}


class User {
    private String name;
    private String sex;

    public User(String name, String sex) {
        this.name = name;
        this.sex = sex;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                '}';
    }
}

image.png
可见,实参的值被改变了!所以很多人得出结论:“Java方法中,传递普通类型时就是值传递,传递对象类型时就是引用传递。”但是这种表述是错误的!

在参数传递过程中,实际参数的地址0x666被拷贝给形参,这个过程就是值传递(引用地址),只是传递的值的内容是对象的引用
那为什么改了user中的属性值,会对原本的user产生影响呢?
Java中对象的传递,是通过复制的方式把引用关系传递。如果没有改引用关系,而是找到引用的地址,将里面的内容改了,会对调用方产生影响的,因为大家指向的是同一个共享对象(即)!

当在pass方法中,重新new一个对象,并改变其值,这样就不会对原参数产生影响了

public void pass(User user) {
    // 重新创建对象
    user = new User();
    // 修改name的值
    user.setName("Tom");
    System.out.println("pass:" + user);
}


这样,堆中就有两个对象地址。此处可知,肯定不是引用传递,如果是引用传递的话,根据上面所讲“=”赋值,在user = new User()时,实参的引用也应该指向0x999,但实际上并没有

2.3.传递引用类型,不修改原对象值

public class Test {
    public static void main(String[] args) {
        Test test = new Test();
        String name = "Mr.Q";
        // 修改属性值
        test.pass(name);
        System.out.println("main:" + name); // Mr.Q
    }

    public void pass(String name) {
        // 修改name的值
        name = "Tom";
        System.out.println("pass:" + name); //Tom
    }
}

String是引用类型,new String("Mr.Q")在堆上创建了对象,name指向了“Mr.Q”的引用。按照引用传递的说法,原参name的值应该会被修改为“Tom”,但实际并没有。
原因是:传递的地址值发生了改变!

String类型对于常量字符串的创建,判断对象在堆中不存在的话,就会创建一个新的,如果创建新对象,那么引用地址都会变

public static void main(String[] args) {
    String a = "hello";
    String b = a;
    b = "你好";
    System.out.println("a:" + a + "、b:" + b); // a:hello、b:你好
}
  • String a = “hello”:在String池中创建一个常量“hello”,给a分配一个栈内存,存储常量“hello”的地址
  • String b = a:给b分配一个栈内存,存储常量“hello”的地址,相当于把a自己存储的地址,复制给了b
  • b = “你好”:在String池中检查是否有“你好”的常量,没有的话,就在堆内存中创建“你好”常量,并将b地址指向“你好”;有的话,就将b的地址直接指向“你好”的地址

2.4.传递对象,证明Java中没有引用传递

public class Test {
    public static void main(String[] args) {
        Student s1 = new Student("张");
        Student s2 = new Student("李");
        // 交换
        Test.swap(s1,s2);
        System.out.println("s1:"+s1.getName());
        System.out.println("s2:"+s2.getName());
    }

    public static void swap(Student x, Student y) {
        // 中间变量
        Student temp = x;
        x = y;
        y =temp;
        System.out.println("x:"+x.getName());
        System.out.println("y:"+y.getName());
    }
}

class Student {
    private String name;

    public Student(String name) {
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

image.png
swap方法并未改变变量s1、s2中的对象引用,swap方法的参数x和y被初始化为两个对象引用的拷贝,该方法交换的是这两个拷贝,所以并未改变原值

3.Java中的值传递

值传递和引用传递最大的区别就是:直接传递的还是传递的是副本

值传递和引用传递区别并不是传递的内容,而是实参到底有没被复制一份给形参。在Java中,还是值传递,只是对于对象参数,值的内容是对象的引用地址!

  • 传递的值在栈中(基本数据类型),直接拷贝一份值传递,改变的形参不会对实参产生影响
  • 传递的值在栈中存放的是地址(引用类型),先根据栈中地址找到堆中的值,然后将地址拷贝一份(拷贝的地址也是一个值),此时形参和实参指向堆中的同一个地址,形参的修改导致堆中的对象修改,从而影响到实参的改变

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/250483.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

力扣日记12.13-【二叉树篇】从中序与后序遍历序列构造二叉树

力扣日记:【二叉树篇】从中序与后序遍历序列构造二叉树 日期:2023.12.13 参考:代码随想录、力扣 106. 从中序与后序遍历序列构造二叉树 题目描述 难度:中等 给定两个整数数组 inorder 和 postorder ,其中 inorder 是二…

Prometheus

Prometheus [系统性能优化实践]JVM进阶实战之监控工具(Prometheus) https://www.cnblogs.com/johnnyzen/p/17388354.html ubuntu 22.04 配置 Prometheus 和 Grafana 服务器监控 https://blog.csdn.net/nvd11/article/details/128030197 Prometheus 是一个开源的监控系统&…

深入理解网络 I/O:单 Group 混杂模式|多 Group 主从模式

🔭 嗨,您好 👋 我是 vnjohn,在互联网企业担任 Java 开发,CSDN 优质创作者 📖 推荐专栏:Spring、MySQL、Nacos、Java,后续其他专栏会持续优化更新迭代 🌲文章所在专栏&…

Threejs漫天多彩粒子天空--粒子系统打造

一、导语 漫天多彩粒子天空特效应该也是Threejs项目中挺常见的一个需求,因为它是基于粒子系统,可以衍生出许多的不一样的方案,比如,星空特效,下雨特效,飘雪特效等等,不仅可以用在项目中增加氛围…

二叉搜索树的实现

本文旨在讲解如何编写一颗二叉搜索树,包括基本的增删查改的操作。 目录 一、二叉搜索树的概念 ​编辑二、二叉搜索树的编写 2.1节点的编写 2.2节点的插入 2.3节点的查找 2.4节点的删除 三、二叉搜索树的应用 四、 二叉搜索树的性能分析 五、完整代码 一、…

CD8+T细胞通过NKG2D-NKG2DL轴维持对MHC-I阴性肿瘤细胞的杀伤

今天给同学们分享一篇实验文章“CD8 T cells maintain killing of MHC-I-negative tumor cells through the NKG2D-NKG2DL axis”,这篇文章发表在Nat Cancer期刊上,影响因子为22.7。 结果解读: MHC-I阴性肿瘤的免疫疗法需要CD8 T细胞 作者先…

现代雷达车载应用——第2章 汽车雷达系统原理 2.2节 汽车雷达架构

经典著作,值得一读,英文原版下载链接【免费】ModernRadarforAutomotiveApplications资源-CSDN文库。 2.2 汽车雷达架构 从顶层来看,基本的汽车雷达由发射器,接收器和天线组成。图2.2给出了一种简化的单通道连续波雷达结构[2]。这…

什么是网络丢包以及如何解决

丢包的概念一直是网络行业争论的话题,在设计和实现网络时,它始终是考虑的关键因素,其重要性在于它对网络和网络系统的效率和整体性能的直接影响,即使是单个故障设备或配置错误的设置也会导致数据包丢失,也会严重影响整…

2 Mycat2 安装与启动

1、制作安装包 Mycat2不提供安装包,只提供核心JAR包,JAR包可以独立运行,安装包是使用Java Service Wrapper做壳的,如果需要安装包,需要自己制作。JAR可以作为Java库引入自己业务项目中使用Mycat2中的各个组件的设计都是可以独立使用的。 步骤如下: 1.…

【C++干货铺】继承后的多态 | 抽象类

个人主页点击直达:小白不是程序媛 C系列专栏:C干货铺 代码仓库:Gitee 目录 多态的概念 多态的定义和实现 多态的定义条件 虚函数 虚函数的重写 特殊情况 协变(基类和派生类的虚函数返回值不同) 析构函数的重…

ffmpeg踩坑之手动编译报错Unrecognized option ‘preset‘及rtsp/rtmp推流

本文解决的问题记录: 报错1:Unrecognized option preset. Error splitting the argument list: Option not found 报错2:ERROR: x264 not found using pkg-config 报错3:ffmpeg: error while loading shared libraries: libavd…

【linux】Debian不能运行sudo的解决

一、问题: sudo: 没有找到有效的 sudoers 资源,退出 sudo: 初始化审计插件 sudoers_audit 出错 二、可用的方法: 出现 "sudo: 没有找到有效的 sudoers 资源,退出" 和 "sudo: 初始化审计插件 sudoers_audit 出错&q…

spring面试:一、面试题分类总览+bean线程安全问题+AOP相关问题(定义、使用步骤、编程式事务管理和声明式事务管理和声明式事务管理失效)

面试题分类总览 bean线程安全问题 单例/多例 单例(singleton):在每个spring ioc容器中都只有一个实例。 多例(prototype):在每个spring ioc容器中有多个实例。 默认情况下spring中的bean都是单例的。但是…

基于Java SSM框架实现智能停车场系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现智能停车场系统演示 摘要 本论文主要论述了如何使用JAVA语言开发一个智能停车场管理系统,本系统将严格按照软件开发流程进行各个阶段的工作,采用B/S架构,面向对象编程思想进行项目开发。在引言中,作者将论述…

call 和 apply:改变对象行为的秘密武器(上)

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云…

TSINGSEE视频智能解决方案边缘AI智能与后端智能分析的区别与应用

视频监控与AI人工智能的结合是当今社会安全领域的重要发展趋势。随着科技的不断进步,视频监控系统已经不再局限于简单的录像和监视功能,而是开始融入人工智能技术,实现更加智能化的监控和安全管理。传统的监控系统往往需要人工操作来进行监控…

内网渗透测试基础——Windows PowerShell篇

内网渗透测试基础——Windows PowerShell篇 1. Windows PowerShell基础 Windows PowerShell是一种命令行外壳程序和脚本环境,它内置在每个受支持的Windows版本中(Windows7、Windows Server 2008 R2及更高版本),为Windows命令行使…

讨好型人格最适合从事什么职业?

讨好型人格,其言行不是考虑个人,而是以满足对方为主,只要是他人的想法,都会尽力去满足,特别害怕自己做了什么事情,让对方产生不满的想法。遇到事情,也很难主动请求别人,总是依靠自己…

计算机组成原理-函数调用的汇编表示(call和ret指令 访问栈帧 切换栈帧 传递参数和返回值)

文章目录 call指令和ret指令高级语言的函数调用x86汇编语言的函数调用call ret指令小结其他问题 如何访问栈帧函数调用栈在内存中的位置标记栈帧范围:EBP ESP寄存器访问栈帧数据:push pop指令访问栈帧数据:mov指令小结 如何切换栈帧函数返回时…

APP安全测试填坑

在实习过程中,我接触到了一些SDL安全提测的工作。原来我是学web端渗透比较多的,移动端这块基本没怎么试过手,结果刚开始一直踩坑,连抓包都抓不到(T▽T)。 下面记录下我遇到的部分问题和解决方法&#xff0…