java是值传递还是引用传递_Java 到底是值传递还是引用传递?

点赞、收藏还挺多,求一波关注

------------------------------------------------------------

没人邀请,自己强行回答一波。

关于这个问题,引发过很多广泛的讨论,看来很多程序员对于这个问题的理解都不尽相同,甚至很多人理解的是错误的。还有的人可能知道Java中的参数传递是值传递,但是说不出来为什么。

在开始深入讲解之前,有必要纠正一下大家以前的那些错误看法了。如果你有以下想法,那么你有必要好好阅读本文。错误理解一:值传递和引用传递,区分的条件是传递的内容,如果是个值,就是值传递。如果是个引用,就是引用传递。

错误理解二:Java是引用传递。

错误理解三:传递的参数如果是普通类型,那就是值传递,如果是对象,那就是引用传递。

实参与形参

我们都知道,在Java中定义方法的时候是可以定义参数的。比如Java中的main方法,public static void main(String[] args),这里面的args就是参数。参数在程序语言中分为形式参数和实际参数。形式参数:是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数。

实际参数:在调用有参函数时,主调函数和被调函数之间有数据传递关系。在主调函数中调用一个函数时,函数名后面括号中的参数称为“实际参数”。

简单举个例子:

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中到底是值传递还是引用传递 ,于是,最简单的一段代码出来了:

public static void main(String[] args) {

ParamTest pt = new ParamTest();

int i = 10;

pt.pass(10);

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方法中打印参数的值。输出结果如下:

print in pass , j is 20

print in main , i is 10

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

但是,很快就有人提出质疑了(哈哈,所以,不要轻易下结论咯。)。然后,他们会搬出以下代码:

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方法内修改参数的值。输出结果如下:

print in pass , user is User{name='hollischuang', gender='Male'}

print in main , user is User{name='hollischuang', gender='Male'}

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

但是,这种表述仍然是错误的。不信你看下面这个参数类型为对象的参数传递:

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);

}

上面的代码输出结果为

print in pass , name is hollischuang

print in main , name is Hollis

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

Java中的值传递

上面,我们举了三个例子,表现的结果却不一样,这也是导致很多初学者,甚至很多高级程序员对于Java的传递类型有困惑的原因。

其实,我想告诉大家的是,上面的概念没有错,只是代码的例子有问题。来,我再来给大家画一下概念中的重点,然后再举几个真正恰当的例子。值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。

引用传递(pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

那么,我来给大家总结一下,值传递和引用传递之前的区别的重点是什么。

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

为什么说实验方法错了呢?这里我们来举一个形象的例子。再来深入理解一下值传递和引用传递,然后你就知道为啥错了。

你有一把钥匙,当你的朋友想要去你家的时候,如果你直接把你的钥匙给他了,这就是引用传递。这种情况下,如果他对这把钥匙做了什么事情,比如他在钥匙上刻下了自己名字,那么这把钥匙还给你的时候,你自己的钥匙上也会多出他刻的名字。

你有一把钥匙,当你的朋友想要去你家的时候,你复刻了一把新钥匙给他,自己的还在自己手里,这就是值传递。这种情况下,他对这把钥匙做什么都不会影响你手里的这把钥匙。

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

还拿上面的一个例子来举例,我们真正的改变参数,看看会发生什么?

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对象,输出结果如下:

print in pass , user is User{name='hollischuang', gender='Male'}

print in main , user is User{name='Hollis', gender='Male'}

我们来画一张图,看一下整个过程中发生了什么,然后我再告诉你,为啥Java中只有值传递。

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

上面这种传递是什么传递?肯定不是引用传递,如果是引用传递的话,在user=new User()的时候,实际参数的引用也应该改为指向0X456789,但是实际上并没有。

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

我们再来回顾下之前的那个“砸电视”的例子,看那个例子中的传递过程发生了什么。

同样的,在参数传递的过程中,实际参数的地址0X1213456被拷贝给了形参,只是,在这个方法中,并没有对形参本身进行修改,而是修改的形参持有的地址中存储的内容。

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

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

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

总结

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

简单点说,Java中的传递,是值传递,而这个值,实际上是对象的引用。

而按共享传递其实只是按值传递的一个特例罢了。所以我们可以说Java的传递是按共享传递,或者说Java中的传递是值传递。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java趣事_【趣事】Java程序员最年轻,C++程序员最年老

原标题:【趣事】Java程序员最年轻,C程序员最年老说起我们对编程世界现有的刻板印象,你一定听说过类似于没有人喜欢用Java编码或者使用C 都是老人家,等等这样的话。为了分析这些刻板印象背后的真相,Trestle Technology的…

koa2 mysql 中间件_Koa2 和 Express 中间件对比

koa2 中间件koa2的中间件是通过 async await 实现的,中间件执行顺序是“洋葱圈”模型。中间件之间通过next函数联系,当一个中间件调用 next() 后,会将控制权交给下一个中间件, 直到下一个中间件不再执行 next() 后, 将会沿路折返,将控制权依次交换给前一…

mysql 索引 内存_mysql索引【第三篇】

##innodb引擎的数据是按页为单位的,当需要读一条记录时,并不是将这个记录本身从磁盘中读取出来,而是以页为单位将整个页读取到内存中,每个页默认大小为16kb数据查询过程: 磁盘---->内存---->返回结果数据更新过…

python notebook配置_使用 Jupyter Notebook 配置 Stata\Python\Julia\R

作者:许梦洁 (中山大学)E-mail: xumj9mail2.sysu.edu.cn原文链接:磐石若水 → 使用 Jupyter Notebook 配置 Stata/Python/Julia/RStata 连享会   课程主页 || 直播视频 || 知乎推文扫码查看连享会最新专题、公开课视频和 100 多个码云计量仓库链接。早在…

qt 飞扬青云_Qt编写带频谱的音乐播放器

之前有个项目需要将音频文件的频谱显示出来,想了很多办法,后面发现fmod这个好东西,还是跨平台的,就一个头文件一个库文件就行,简单小巧功能强大,人家做的真牛逼。为了不卡住界面,采用了多线程处…

java什么是工作流程_springmvc工作流程是什么

springmvc工作流程是什么:1、springmvc工作原理图2、springmvc工作流程1、 用户向服务端发送一次请求,这个请求会先到前端控制器DispatcherServlet(也叫中央控制器)。2、DispatcherServlet接收到请求后会调用HandlerMapping处理器映射器。由此得知&#…

java se入门_java SE 入门之运算符(第三篇)

在 java 中主要有以上运算符,分为 赋值运算符 ,算术运算符、关系运算符(比较运算符),逻辑运算符和三目运算符(三元运算符).我们一起来学习下吧!一、赋值运算符赋值运算符主要有五个、分别是 (赋值)、(加上一个在赋值)、-(减去一个在赋值)、*…

java 封闭实例_不能访问类型…的封闭实例

出于教育目的,我尝试制作一个服务器和一个客户端,其中服务器从多个客户端接收数据并回显每条消息。问题是,当我尝试让服务器一次将回显发送到所有客户端时。public class SocketServer {ArrayList ts new ArrayList();ServerSocket serv;sta…

java接口权限管理在哪里_JAVA-访问权限控制

以前对于public、private、protect、default,都只是简单的知道其对应的访问权限范围,现在看了think in java中的描述,讲了这几种访问权限在什么场景下使用,觉得进一步的了解的它存在的意义和合理性。因为在我们的日常开发中&#…

java 重写session_使用Shiro重写Session 自定义SESSION

引入shiro的jar包1.2.3org.apache.shiroshiro-core${shiroVersion}org.apache.shiroshiro-web${shiroVersion}org.apache.shiroshiro-ehcache${shiroVersion}org.apache.shiroshiro-spring${shiroVersion}配置shiro(web.xml)shiroFilterorg.springframework.web.filter.Delegat…

java7最后战线_我的世界最后战线2.0整合包

我的世界1.7.2最后战线The end battle line正式V2修复版是我的世界最后战线2.0整合包。游戏信息地图名字: The End Battle Line「正式版」地图分类: 其他地图类型: 存档地图版本: 后期Anvil格式(1.3)游戏介绍>>前言<<经过了半学期加整整大半个暑假的努力&#xff0…

js添加多marker 高德地图_覆盖物-参考手册-地图 JS API | 高德地图API

覆盖物覆盖物是指叠加在地图底图之上的一些常见要素&#xff0c;包括Marker类点标记。 构造函数 说明AMap.Marker( 构造一个点标记对象&#xff0c;通过MarkerOptions设置点标记对象的属性MarkerOptions 类型 说明map 要显示该marker的地图对象position 点标记在地图上显示的位…

java敏感异常是什么_java中的异常是什么?

Throwable(所有error和exception的父类:超类)Java将错误变成对象交给异常处理机制Error(仅靠程序本身无法恢复的严重错误)一般是环境的问题&#xff0c;JVM的问题&#xff0c;并非程序的问题&#xff1b;最典型的就是虚拟机中运行的程序过多&#xff0c;造成的虚拟机内存溢出&a…

java为什么不推荐使用stack_栈和队列的面试题Java实现,Stack类继承于Vector这两个类都不推荐使用...

在 thinking in java中看到过说Stack类继承于Vector&#xff0c;而这两个类都不推荐使用了&#xff0c;但是在做一到OJ题时&#xff0c;我用LinkedList来模拟栈和直接用Stack&#xff0c;发现在进行入栈出栈操作时Stack的速度竟然还快一点然而需要多线程的时候有Collections.sy…

reducebykeyandwindow java_Spark Streaming笔记整理(三):DS的transformation与output操作

[TOC]DStream的各种transformationTransformation Meaningmap(func) 对DStream中的各个元素进行func函数操作&#xff0c;然后返回一个新的DStream.flatMap(func) 与map方法类似&#xff0c;只不过各个输入项可以被输出为零个或多个输出项filter(func) 过滤出所有函数func返回值…

java mysubmail_关于MyBatis10种超好用的写法(收藏)

用来循环容器的标签forEach,查看例子foreach元素的属性主要有item&#xff0c;index&#xff0c;collection&#xff0c;open&#xff0c;separator&#xff0c;close。item&#xff1a;集合中元素迭代时的别名index&#xff1a;集合中元素迭代时的索引open&#xff1a;常用语w…

php学好要多久,零基础php自学要多久

万事开头难&#xff0c;自学编程也是一样&#xff0c;只要能入门&#xff0c;在学起来就容易多了&#xff0c;那么零基础自学编程多久才可以入门呢&#xff0c;如何才能找到自学编程的门道呢&#xff1f; (推荐学习&#xff1a;PHP视频教程)零基础自学编程也可以&#xff0c;关…

php laravel 优点,Laravel 特点有哪些?

Laravel特点&#xff1a;1、“artisan”命令行工具&#xff0c;可以自动化完成手动的工作&#xff1b;2、“migration”管理数据库和版本控制&#xff1b;3、测试功能也很强大&#xff1b;4、适合应用各种开发模式TDD、DDD和BDD&#xff1b;5、支持Composer包管理工具。Laravel…

php枚举mysql,小技巧:取得MYSQL中ENUM(枚举)列的全部可能值。-PHP教程,PHP应用...

取得mysql中enum(枚举)列的全部可能值这里其实并不需要其它的什么函数来支持&#xff0c;只需要使用mysql提供的一些sql语句就可以了。这里为了简单起见&#xff0c;以mysql的系统表user为例&#xff0c;取出select&#xff3f;priv这一列的所有可能值。方法&#xff1a;show c…

php 拼接wav,将两个Wav文件合并为一个 | 学步园

参考了&#xff1a;http://www.codeguru.com/forum/showthread.php?t132649CWave CWave::Mix(LPCTSTR bkWav){CWave wavBK(bkWav);int iTrackLenth;int iHeaderLen 46;iTrackLenth data.dataSIZE;CWave ret_val;ret_val.fmt fmt;ret_val.riff riff;ret_val.data data;ret…