Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Java 基础概念·Java 只有值传递

Java 基础概念·Java 只有值传递

作者头像
数媒派
发布于 2022-12-01 03:33:28
发布于 2022-12-01 03:33:28
42900
代码可运行
举报
文章被收录于专栏:产品优化产品优化
运行总次数:0
代码可运行

Java 只有值传递

本文为个人学习摘要笔记。 原文地址:为什么说 Java 中只有值传递

Java 中的参数传递是值传递还是引用传递的错误理解:

  • 错误理解一:值传递和引用传递,区分的条件是传递的内容,如果是个值,就是值传递。如果是个引用,就是引用传递。
  • 错误理解二:Java 是引用传递。
  • 错误理解三:传递的参数如果是普通类型,那就是值传递,如果是对象,那就是引用传递。

实参与形参

参数在程序语言中分为形式参数和实际参数。

  • 形式参数:是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数。
  • 实际参数:在调用有参函数时,主调函数和被调函数之间有数据传递关系。在主调函数中调用一个函数时,函数名后面括号中的参数称为“实际参数”。

举个栗子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static void main(String[] args) {
  ParamTest pt = new ParamTest();
  pt.sout("Hollis"); // 实际参数为 Hollis
}

public void sout(String name) { // 形式参数为 name
  System.out.println(name);
}

实际参数是调用有参方法的时候真正传递的内容,而形式参数是用于接收实参内容的参数

值传递与引用传递

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

  • 值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
  • 引用传递(pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

这里通过三个栗子来说明 java 中是按值传递还是引用传递的。

栗子一

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static void main(String[] args) {
  ParamTest pt = new ParamTest();

  int i = 10;
  pt.pass(i);
  System.out.println("print in main , i is " + i);
}

public void pass(int j) {
  j = 20;
  System.out.println("print in pass , j is " + j);
}

上面的代码中,我们在 pass 方法中修改了参数 j 的值,然后分别在 pass 方法和 main 方法中打印参数的值。输出结果如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
print in pass , j is 20
print in main , i is 10

可见,pass 方法内部对 name 的值的修改并没有改变实际参数 i 的值。那么,按照上面的定义,有人得到结论:Java 的方法传递是值传递。

栗子二

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static void main(String[] args) {
  ParamTest pt = new ParamTest();

  User hollis = new User();
  hollis.setName("Hollis");
  hollis.setGender("Male");
  pt.pass(hollis);
  System.out.println("print in main , user is " + hollis);
}

public void pass(User user) {
  user.setName("hollischuang");
  System.out.println("print in pass , user is " + user);
}

同样是一个 pass 方法,同样是在 pass 方法内修改参数的值。输出结果如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
print in pass , user is User{name='hollischuang', gender='Male'}
print in main , user is User{name='hollischuang', gender='Male'}

经过 pass 方法执行后,实参的值竟然被改变了,那按照上面的引用传递的定义,实际参数的值被改变了,这不就是引用传递了么。于是,根据上面的两段代码,有人得出一个新的结论:Java 的方法中,在传递普通类型的时候是值传递,在传递对象类型的时候是引用传递(错误理解三)。

栗子三

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static void main(String[] args) {
  ParamTest pt = new ParamTest();

  String name = "Hollis";
  pt.pass(name);
  System.out.println("print in main , name is " + name);
}

public void pass(String name) {
  name = "hollischuang";
  System.out.println("print in pass , name is " + name);
}

上面的代码输出结果为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
print in pass , name is hollischuang
print in main , name is Hollis

这又作何解释呢?同样传递了一个对象,但是原始参数的值并没有被修改,难道传递对象又变成值传递了?

Java 中的值传递

上面的三个栗子,表现的结果不一样,导致大家对 Java 中传递类型产生困惑。

其实,上面的概念没有错,只是代码的例子有问题。这里再复习下值传递和引用传递的概念,然后再举几个真正恰当的例子。

  • 值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
  • 引用传递(pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

总结值传递和引用传递之前的区别的重点:

值传递

引用传递

根本区别

会创建副本

不创建副本

所以

函数中无法改变原始对象

函数中可以改变原始对象

上面几个错误的例子中,都只关注了实际参数内容是否有改变。如传递的是 User 对象,我们试着改变他的 name 属性的值,然后检查是否有改变。其实,在实验方法上就错了,当然得到的结论也就有问题了。

为什么说实验方法错了呢?这里我们来举一个形象的栗子:

  • 场景一:你有一把钥匙,当你的朋友想要去你家的时候,如果你直接把你的钥匙给他了,这就是引用传递。这种情况下,如果他对这把钥匙做了什么事情,比如他在钥匙上刻下了自己名字,那么这把钥匙还给你的时候,你自己的钥匙上也会多出他刻的名字。
  • 场景二:你有一把钥匙,当你的朋友想要去你家的时候,你复刻了一把新钥匙给他,自己的还在自己手里,这就是值传递。这种情况下,他对这把钥匙做什么都不会影响你手里的这把钥匙。

但是,不管上面那种情况,你的朋友拿着你给他的钥匙,进到你的家里,把你家的电视砸了。那你说你会不会受到影响?而我们在上面的例子中,改变 user 对象的 name 属性的值的时候,不就是在“砸电视”么。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static void main(String[] args) {
  ParamTest pt = new ParamTest();

  User hollis = new User();
  hollis.setName("Hollis");
  hollis.setGender("Male");
  pt.pass(hollis);
  System.out.println("print in main , user is " + hollis);
}

public void pass(User user) {
  user = new User();
  user.setName("hollischuang");
  user.setGender("Male");
  System.out.println("print in pass , user is " + user);
}

上面的代码中,我们在 pass 方法中,改变了 user 对象,输出结果如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
print in pass , user is User{name='hollischuang', gender='Male'}
print in main , user is User{name='Hollis', gender='Male'}

图解上面的过程:

当我们在 main 中创建一个 User 对象的时候,在堆中开辟一块内存,其中保存了 name 和 gender 等数据。然后 hollis 持有该内存的地址 0x123456(图 1)。当尝试调用 pass 方法,hollis 作为实际参数传递给形式参数 user 的时候,会把这个地址 0x123456 交给 user,这时,user 也指向了这个地址(图 2)。然后在 pass 方法内对参数进行修改的时候,即 user = new User();,会重新开辟一块 0X456789 的内存,赋值给 user。后面对 user 的任何修改都不会改变内存 0X123456 的内容。

通过概念我们也能知道,这里是把实际参数的引用的地址复制了一份,传递给了形式参数。所以,上面的参数其实是值传递,把实参对象引用的地址当做值传递给了形式参数

这时我们再回顾上面的栗子二,在参数传递的过程中,实际参数的地址被拷贝给了形参,只是,在这个方法中,并没有对形参本身进行修改,而是修改的形参持有的地址中存储的内容。

所以,值传递和引用传递的区别并不是传递的内容。而是实参到底有没有被复制一份给形参。在判断实参内容有没有受影响的时候,要看传的的是什么,如果你传递的是个地址,那么就看这个地址的变化会不会有影响,而不是看地址指向的对象的变化。就像钥匙和房子的关系。

那么,既然这样,为啥上面同样是传递对象,传递的 String 对象和 User 对象的表现结果不一样呢?我们在 pass 方法中使用 name = "hollischuang"; 试着去更改 name 的值,阴差阳错的直接改变了 name 的引用的地址。因为这段代码,会 new 一个 String,在把引用交给 name,即等价于 name = new String("hollischuang");。而原来的那个"Hollis"字符串还是由实参持有着的,所以,并没有修改到实际参数的值。

所以说,Java 中其实还是值传递的,只不过对于对象参数,值的内容是对象的引用

总结

无论是值传递还是引用传递,其实都是一种求值策略(Evaluation strategy)。在求值策略中,还有一种叫做按共享传递(call by sharing)。其实 Java 中的参数传递严格意义上说应该是按共享传递。

按共享传递,是指在调用函数时,传递给函数的是实参的地址的拷贝(如果实参在栈中,则直接拷贝该值)。在函数内部对参数进行操作时,需要先拷贝的地址寻找到具体的值,再进行操作。如果该值在栈中,那么因为是直接拷贝的值,所以函数内部对参数进行操作不会对外部变量产生影响。如果原来拷贝的是原值在堆中的地址,那么需要先根据该地址找到堆中对应的位置,再进行操作。因为传递的是地址的拷贝所以函数内对值的操作对外部变量是可见的。

简单点说,Java 中的传递,是值传递,而这个值,实际上是对象的引用。而按共享传递其实只是按值传递的一个特例罢了。所以我们可以说 Java 的传递是按共享传递,或者说 Java 中的传递是值传递。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
为什么Java中只有值传递【详解】
这里,我们可以清晰地看出x的值在函数执行前后没有发生改变,函数对形参的任何操作,不影响实参;
訾博ZiBo
2025/01/06
760
为什么Java中只有值传递【详解】
Java 02 - 值传递与引用传递
首先我们要知道, 值传递和引用传递是一种求值策略(Evaluation Stragtegy), 表示的是调用函数的时候, 对于参数传递方式的描述, 而不是对参数本身类型的描述. 值类型和引用类型是两种内存分配方式, 值类型是在调用栈上分配, 而引用类型是在堆上分配. 一个是描述的内存分配方式, 一个是描述参数求值策略, 二者并无依赖和约束关系.
Reck Zhang
2021/08/11
7740
Stack Overflow上188万浏览量的提问:Java 到底是值传递还是引用传递?
在逛 Stack Overflow 的时候,发现了一些访问量像阿尔卑斯山一样高的问题,比如说这个:Java 到底是值传递还是引用传递?访问量足足有 188万+,这不得了啊!说明有很多很多的程序员被这个问题困扰过。实话实说吧,我就是其中之一。
沉默王二
2019/11/28
5490
说说Java到底是值传递还是引用传递
首先,我们必须要搞清楚,到底什么是值传递,什么是引用传递,否则,讨论 Java 到底是值传递还是引用传递就显得毫无意义。
沉默王二
2020/04/23
4610
说说Java到底是值传递还是引用传递
一文读懂 Python 值传递和引用传递
👋 你好,我是 Lorin 洛林,一位 Java 后端技术开发者!座右铭:Technology has the power to make the world a better place.
Lorin 洛林
2024/02/26
2.4K9
一文读懂 Python 值传递和引用传递
值传递OR引用传递?大部人都答错了!
Java 是值传递还是引用传递?这是 Java 中比较基础的一道常见面试题,但对于这道问题的大部分答案都是错的,大部人会这样回答这个问题:
磊哥
2024/09/05
600
值传递OR引用传递?大部人都答错了!
多图证明,Java到底是值传递还是引用传递?
开篇先来曝答案,在 Java 语言中,本质只有值传递,而无引用传递,解释和证明详见正文。
Bug开发工程师
2021/01/28
6380
多图证明,Java到底是值传递还是引用传递?
为什么大家都说Java中只有值传递?
最近跟Java中的值传递和引用传递杠上了,一度怀疑人生。查了很多资料,加上自己的理解,终于搞清楚了,什么是值传递和引用传递。也搞明白了,为什么大家都说Java只有值传递,没有引用传递。原来,我一直以来的认知都是错误的。。。
烟雨星空
2020/06/16
1.6K0
解惑4:java是值传递还是引用传递
曾经纠结了很久java的参数传递方式是什么样的,后面粗略的了解了一鳞半爪以后有了大概的印象:“传参数就是值传递,传对象就是引用传递”,后面进一步查找了相关资料和文章以后,发现这么理解是不正确的。
全栈程序员站长
2022/09/23
6550
解惑4:java是值传递还是引用传递
Java 值传递与引用传递
以下是包含引用的完整博客文章,以markdown格式输出,附带“Java 只有值传递”的相关参考来源。
井九
2024/10/12
1890
Java 值传递与引用传递
图说Java值传递原理
Java是一门面向对象的程序设计语言,类是其基本抽象单元,而方法是类中可复用的执行单元;当一个Java方法被调用,方法参数的传递方式究竟是基于值传递还是引用传递呢?答案是:值传递 !
程序猿杜小头
2022/12/01
4870
图说Java值传递原理
Java真的只有值传递?
值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
一觉睡到小时候
2019/07/02
7080
Java真的只有值传递?
Java中真的只有值传递么?
关于这个问题应该是存在争议的。根据测试出来的结果和我们自己的经验,以及口口相传或是上学时老师讲的,我们认为是第一种。但第二种说法的呼声也很高,渐渐地我们也认为第2中才是对的。那么下面我们就来分析一下这个问题。
编程大道
2020/02/11
1.2K0
这一次,让你彻底理解Java的值传递和引用传递!
学过Java基础的人都知道:值传递和引用传递是初次接触Java时的一个难点,有时候记得了语法却记不得怎么实际运用,有时候会的了运用却解释不出原理,而且坊间讨论的话题又是充满争议:有的论坛帖子说Java只有值传递,有的博客说两者皆有;这让人有点摸不着头脑,下面我们就这个话题做一些探讨,对书籍、对论坛博客的说法,做一次考证,以得出信得过的答案。
Java团长
2019/06/26
8880
这一次,让你彻底理解Java的值传递和引用传递!
Java值传递与引用传递
Java面试题: 当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?   答案基本上是:值传递 说明:得出这种结论的前提必须是
阳光岛主
2019/02/19
1.1K0
每天一个Java面试题之为什么 Java 只有值传递
在编程语言中,参数传递的方式主要有两种:值传递和引用传递。值传递是指将实际参数的值复制一份传递到函数中,而引用传递则是将实际参数的地址传递到函数中。Java,作为一种广泛使用的编程语言,采用的是值传递方式。本文将深入探讨Java为什么只有值传递,并提供代码示例来说明这一概念。
灬沙师弟
2024/10/28
1370
每天一个Java面试题之为什么 Java 只有值传递
细说值传递、引用传递和地址传递
形式参数:是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数。
闫同学
2023/10/14
2570
Java的JVM介绍以及java的值传递和引用传递
面试的时候碰到的了一个java基础问题,竟然给问蒙了,回来之后感觉针对这个问题总结一下
包子388321
2020/06/16
1K0
面试专题:值传递和引用传递的问题
在Java编程中,参数传递有两种方式:值传递和引用传递。这两种传递方式决定了变量在方法内部的变化如何影响原始变量。本文将深入探讨这两种传递方式的概念、应用和注意事项,并且从面试题入手,分栈堆内存的情况。
小明爱吃火锅
2023/12/26
4130
详解Java中的值传递
如果大家学过c++,那这块肯定是明白的,但是Java中没有引用传递,在编程语言中,将实参传递给方法的方式有俩种就是我们即将要说的,
秋名山码神
2022/12/13
6950
详解Java中的值传递
相关推荐
为什么Java中只有值传递【详解】
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验