java中方法的参数传递机制

转载自 :https://www.cnblogs.com/lixiaolun/p/4311863.html

问:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递? 
  答:是值传递。Java 编程语言只有值传递参数。当一个对象实例作为一个参数被传递到方法中时,参数的值就是该对象的引用一个副本。指向同一个对象,对象的内容可以在被调用的方法中改变,但对象的引用(不是引用的副本)是永远不会改变的。

  Java参数,不管是原始类型还是引用类型,传递的都是副本(有另外一种说法是传值,但是说传副本更好理解吧,传值通常是相对传址而言)。

如果参数类型是原始类型,那么传过来的就是这个参数的一个副本,也就是这个原始参数的值,这个跟之前所谈的传值是一样的。如果在函数中改变了副本的值不会改变原始的值.
如果参数类型是引用类型,那么传过来的就是这个引用参数的副本,这个副本存放的是参数的地址。如果在函数中没有改变这个副本的地址,而是改变了地址中的 值,那么在函数内的改变会影响到传入的参数。如果在函数中改变了副本的地址,如new一个,那么副本就指向了一个新的地址,此时传入的参数还是指向原来的 地址,所以不会改变参数的值。
基本类型参数传递:不改变值
引用类型参数传递:改变值

无论是什么语言,要讨论参数传递方式,就得从内存模型说起,主要是我个人觉得从内存模型来说参数传递更为直观一些。闲言少叙,下面我们就通过内存模型的方式来讨论一下Java中的参数传递。

这里的内存模型涉及到两种类型的内存:栈内存(stack)和堆内存(heap)。基本类型作为参数传递时,传递的是这个值的拷贝。无论你怎么改变这个拷贝,原值是不会改变的。看下边的一段代码,然后结合内存模型来说明问题:

1
2
3
4
5
6
7
8
9
10
11
12
public class ParameterTransfer {
     public static void main(String[] args) {
        int num = 30;
        System.out.println("调用add方法前num=" + num);
        add(num);
        System.out.println("调用add方法后num=" + num);
     }
  
     public static void add(int param) {
          param = 100;
     }
}                

这段代码运行的结果如下:

1
2
调用add方法前num=30
调用add方法后num=30

程序运行的结果也说明这一点,无论你在add()方法中怎么改变参数param的值,原值num都不会改变。

  下边通过内存模型来分析一下。

  当执行了int num = 30;这句代码后,程序在栈内存中开辟了一块地址为AD8500的内存,里边放的值是30,内存模型如下图:

                    Java中的参数传递方式
  执行到add()方法时,程序在栈内存中又开辟了一块地址为AD8600的内存,将num的值30传递进来,此时这块内存里边放的值是30,执行param = 100;后,AD8600中的值变成了100。内存模型如下图:   
                 Java中的参数传递方式

  地址AD8600中用于存放param的值,和存放num的内存没有任何关系,无论你怎么改变param的值,实际改变的是地址为AD8600的内存中的值,而AD8500中的值并未改变,所以num的值也就没有改变。
   以上是基本类型参数的传递方式,下来我们讨论一下对象作为参数传递的方式。

  先看下边的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
public class ParameterTransfer {
     public static void main(String[] args) {
          String[] array = new String[] {"huixin"};
          System.out.println("调用reset方法前array中的第0个元素的值是:" + array[0]);
          reset(array);
          System.out.println("调用reset方法后array中的第0个元素的值是:" + array[0]);
     }
  
     public static void reset(String[] param) {
          param[0] = "hello, world!";
     }
}   

运行的结果如下:

1
2
调用reset方法前array中的第0个元素的值是:huixin
调用reset方法后array中的第0个元素的值是:hello, world!

当对象作为参数传递时,传递的是对象的引用,也就是对象的地址。下边用内存模型图来说明。

       Java中的参数传递方式

  当程序执行了String[] array = new String[] {"huixin"}后,程序在栈内存中开辟了一块地址编号为AD9500内存空间,用于存放array[0]的引用地址,里边放的值是堆内存中的一个地址,示例中的值为BE2500,可以理解为有一个指针指向了堆内存中的编号为BE2500的地址。堆内存中编号为BE2500的这个地址中存放的才是array[0]的值:huixin。

  当程序进入reset方法后,将array的值,也就是对象的引用BE2500传了进来。这时,程序在栈内存中又开辟了一块编号为AD9600的内存空间,里边放的值是传递过来的值,即AD9600。可以理解为栈内存中的编号为AD9600的内存中有一个指针,也指向了堆内存中编号为BE2500的内存地址,如图所示:

       Java中的参数传递方式

  这样一来,栈内存AD9500和AD9600(即array[0]和param的值)都指向了编号为BE2500的堆内存。

  在reset方法中将param的值修改为hello, world!后,内存模型如下图所示:

       Java中的参数传递方式

  改变对象param的值实际上是改变param这个栈内存所指向的堆内存中的值。param这个对象在栈内存中的地址是AD9600,里边存放的值是BE2500,所以堆内存BE2500中的值就变成了hello,world!。程序放回main方法之后,堆内存BE2500中的值仍然为hello,world!,main方法中array[0]的值时,从栈内存中找到array[0]的值是BE2500,然后去堆内存中找编号为BE2500的内存,里边的值是hello,world!。所以main方法中打印出来的值就变成了hello,world!

  

小结:

  无论是基本类型作为参数传递,还是对象作为参数传递,实际上传递的都是值,只是值的的形式不用而已。第一个示例中用基本类型作为参数传递时,将栈内存中的值30传递到了add方法中。第二个示例中用对象作为参数传递时,将栈内存中的值BE2500传递到了reset方法中。当用对象作为参数传递时,真正的值是放在堆内存中的,传递的是栈内存中的值,而栈内存中存放的是堆内存的地址,所以传递的就是堆内存的地址。这就是它们的区别。

补充一下,在Java中,String是一个引用类型,但是在作为参数传递的时候表现出来的却是基本类型的特性,即在方法中改变了String类型的变量的值后,不会影响方法外的String变量的值。关于这个问题,可以参考如下两个地址:

http://freej.blog.51cto.com/235241/168676

http://dryr.blog.163.com/blog/static/58211013200802393317600/

我觉得是这两篇文章中提到的两个原因导致的,一个是String实际上操作的是char[],可以理解为String是char[]的包装类。二是给String变量重新赋值后,实际上没有改变这个变量的值,而是重新new了一个String对象,改变了新对象的值,所以原来的String变量的值并没有改变。

转载于:https://www.cnblogs.com/sf-blog/p/10725189.html

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

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

相关文章

2009'中国GIS优秀工程公示

2009中国GIS优秀工程公示http://www.gislm.com/showtopic-393-1.aspx#950 转载于:https://www.cnblogs.com/mygis_3/archive/2009/09/02/1558492.html

GIT-Linux(CentOS7)系统部署git服务器

GIT-Linux(CentOS7)系统部署git服务器 root账号登录 一、 安装并配置必要的依赖关系在CentOS系统上安装所需的依赖:ssh,防火墙,postfix(用于邮件通知) ,wget,以下这些命令也会打开系统防火墙中的HTTP和SSH端口访问。 1.安装ssh su…

vue 点击渲染ajax,vue中在页面加载时发送ajax请求获取数据渲染不到页面上

ajax是异步执行的。{{bookId}}在setData里面处理数据export default {name: app,data() {bookId : },created(){this.loadData(this.setData);},methods:{loadData(callBack) {const This this;const url https://api.douban.com/v2/book/1220562;getBookPromise().then(res …

2字段添加注释_Tableau学习系列(7):计算字段

计算字段是使用函数和运算符构造公式,对数据源字段(包括维度、度量、参数等)进行重新定义的字段。1 创建计算字段这里使用自带超市的数据,对各省市销售额从高到低排序进行可视化,但是如果想对各省销售额划份为高、中、…

SourceProvider.getJniDirectories

2019独角兽企业重金招聘Python工程师标准>>> 今天android studio跑项目,build了一下,始终跑不起,把gradle和android studio都更新了一下,还是不行, 一直报这个错误 UnsupportedMethodExceptionFailed to se…

虚拟服务器问题,虚拟主机常见的五大问题

导语:关于虚拟主机,很多用户在使用过程中,会遇到各种各样的问题,根据以往的客户咨询经验,我们总结出了几个比较常见的问题和相应的解决方案,希望能够帮到大家。1.空间的使用情况怎么查看有的时候客户想要看…

MPU6050开发 -- 卡尔曼滤波(转)

MPU6050开发 -- 卡尔曼滤波转载于:https://www.cnblogs.com/LittleTiger/p/10735074.html

文章内容分页

因为程序是C#.net编写的,关于C#如此的例子、资料网上一点也找不到;所以只好从程序结构开始分析,一点程序却用了半天的时候完成,汗颜!不足之处,请大家指点,有什么更好的方法请告知。 显示内容部…

ORACLE搭建Stream过程中报错【error收集】

错误一:在配置完源库和目标数据库后,创建复制管理员。连接上复制管理员后,在源库执行MAINTAIN_TABLE过程: declarev_tables DBMS_UTILITY.UNCL_ARRAY; beginv_tables(1) : hr.test01;v_tables(2) : hr.test02;v_tables(3) : hr.te…

元月元日是哪一天_元宵节的农历日期是哪一天 - 中国万年历

摘要元宵节农历时间  2018年03月02日 星期五 (戊戌年(狗年)正月十五)  农历正月十五元宵节,是我国一个重要的传统节日。这一天古代称为“上元”,其夜则称“元夜”、“元夕”或“元宵”。我国古代的历法和月相有密切的关系,每月十五日必逢…

服务器和交换机之间网络协议,网络协议是计算机网络中服务器,计算机,交换机.doc...

网络协议是计算机网络中服务器,计算机,交换机篇一:计算机网络-参考答案(1)不能保证所有题目都在里面,但能保证大部分题目都在这里。(2)建议用快捷方式ctrlf 进行答案的查找,关键字只需复制题目的部分文字即可,这样可以提高速度&am…

5种ASP.NET页面间传递参数实例代码

本文假设第一个页面为send.aspx,第二个页面为receive.aspx 1、通过URL链接地址传递 (1) send.asp代码 protected void Button1_Click(object sender, EventArgs e) { Request.Redirect("Default2.aspx?usernamehonge"); } …

withMisfireHandlingInstruction

调度(scheduleJob)或恢复调度(resumeTrigger,resumeJob)后不同的misfire对应的处理规则 CronTrigger withMisfireHandlingInstructionDoNothing ——不触发立即执行 ——等待下次Cron触发频率到达时刻开始按照Cron频率依次执行 withMisfireHandlingInstructionIgnoreMisfires —…

分类算法总结

2019独角兽企业重金招聘Python工程师标准>>> 最近在研究一些算法,找了一些资料,觉得比较全面,也总结的还不错. 主要分类方法介绍解决分类问题的方法很多, 单一的分类方法主要包括:决策树、贝叶斯、人…

小贝拉机器人是朋友_报废机器人应该属于什么垃圾?《宝莱坞机器人2.0》给你答案...

“你是什么垃圾?”如果你认为这是在骂人,那你就已经被时代远远的抛弃了! 9102年,想要打破与陌生人的次元壁,研究垃圾分类,无疑是最快速有效的方式。但是你确定能分清楚每种垃圾的类型吗?比如大卸…

eclipse源服务器未能找到,eclipse - HTTP状态[404]? [未找到](原始服务器未找到当前表示) - 堆栈内存溢出...

我使用(jsp,servlet,apache tomcat 9,eclipse neon,oracle 11g)创建了一个servlet项目。 我已经创建了index.jsp(以获取登录详细信息并重定向到LoginServlet以便在数据库表中进行用户名和密码验证。在index.jsp中,我什…

初学ActionScript 3.0(一):Hello World

简介: ActionScript是 Macromedia(现已被Adobe收购)为其Flash产品开发的 ,最初是一种简单的脚本语言,现在最新版本3.0,是一种完全的面向对象的编程语言,功能强大,类库丰富&#xff0…

A Web Module That Uses JavaServer Faces Technology: The hello2 Example

hello2详解 1.GreetingServlet.java(显示问候页面表单) 此servlet重写该doGet方法,实现GETHTTP方法。servlet显示一个简单的HTML问候表单,其提交按钮就像hello1指定其操作的响应页面一样。以下摘录以WebServlet注释开头&#xff0…

Java探索之旅(11)——抽象类与接口

1.Java数据类型 ❶不可变类,是指当创建了这个类的实例后,就不允许修改它的属性值。 它包括:Primitive变量:boolean,byte, char, double ,float, integer, long, shortJDK不可变类:Boolean, Byte, Character, Double, F…

LOJ #6052. 「雅礼集训 2017 Day11」DIV

完了我是数学姿势越来越弱了,感觉这种CXRdalao秒掉的题我都要做好久 一些前置推导 首先我们很容易得出\((abi)(cdi)k \Leftrightarrow ac-bdk,adbc0\) 我们可以直接\(adbc0\Rightarrow ad-bc\Rightarrow \frac{a}b-\frac{c}{d}\) 考虑把这个分数化为最简的形式&…