我的Java开发学习之旅------>Java语言中方法的参数传递机制

简介:        实参:如果声明方法时包含来了形参声明,则调用方法时必须给这些形参指定参数值,调用方法时传给形参的参数值也被称为实参。      Java的实参值是如何传入方法?这是由Java方法的参数传递机制来控制的,Java里方法的参数传递方式只有一种:值传递。

      

实参:如果声明方法时包含来了形参声明,则调用方法时必须给这些形参指定参数值,调用方法时传给形参的参数值也被称为实参。

     Java的实参值是如何传入方法?这是由Java方法的参数传递机制来控制的,Java里方法的参数传递方式只有一种:值传递。所谓值传递,就是将实际参数的副本(复制品)传入方法内,而参数本身不会收到任何影响。


一、参数类型是原始类型值传递


下面通过一个程序来演练 参数类型是原始类型值传递的效果:

public class ParamTransferTest {
	
	public static void swap(int a,int b){
		//下面三行代码实现a、b变量的值交换
		//定义一个临时变量来保存a变量的值
		int temp=a;
		//把b的值赋给a
		a=b;
		//把临时变量temp的值赋给a
		b=temp;
		System.out.println("swap方法中,a的值是:"+a+", b的值是:"+b);
	}
	
	public static void main(String[] args) {
		int a=6;
		int b=9;
		swap(a, b);
		System.out.println("交换结束后,变量a的值是:"+a+", 变量b的值是:"+b);
	}

}

     程序结果为:

swap方法中,a的值是:9, b的值是:6
交换结束后,变量a的值是:6, 变量b的值是:9

从上面运行结果来看,swap方法中的a和b的值是9、6,交换结束后,变量a和b的值依然是6、9。

从这个运行结果可以看出,main方法里的变量a和b,并不是swap方法中的a和b。

swap方法中的a和b只是main方法中的变量a和b的复制品。下面通过示意图来说明上面程序的执行过程。

Java程序总是从main方法开始执行,main方法开始定义了a、b两个局部变量,两个变量在内存中存储示意图如图1所示。

      当程序执行swap方法是,系统进入swap方法,并将main方法中的a、b变量作为参数传入swap方法,传入swap方法的只是a、b的副本,而不是a、b本身,进入swap方法后系统产生了4个变量,这4个变量在内存中的存储示意图如图2所示。

     

在main方法中调用swap方法时,main方法还未结束。因此,系统分别为main方法和swap方法分配两块栈区,用于保存main方法和swap方法的局部变量。main方法中的a、b变量作为参数值传入swap方法,实际上是在swap方法栈区中重新产生两个变量a、b,并将main方法栈区中的a、b变量的值分别赋给swap方法栈区中的a、b参数(就是对swap方法的a、b形参进行初始化)。此时,系统存在两个a变量,两个b变量,只是存在于不同的方法栈区中而已。

 程序在swap方法中交换a、b两个变量的值,实际上是对图2中灰色覆盖区域的a、b变量进行交换,交换结束后swap方法中输出a、b变量的值,看到a的值为9,b的值为6,此时内存中的存储示意图如图3所示。


对比图3和图2,两个示意图中main方法栈区的a、b值并未有任何改变,程序改变的只是swap方法栈区中的a、b值。

这就是值传递的实质:当系统开始执行方法时,系统为形参执行初始化,就是把实参变量的值赋给方法的形参变量,方法里操作并不是实际的参数变量。




二、参数类型是引用类型值传递

下面通过一个程序来演练 参数类型是引用类型值传递的效果:

class DataWrap {
	public int a;
	public int b;
}

public class ReferenceTransferTest {

	public static void swap(DataWrap dw) {
		// 下面三行代码实现dw对象的a、b两个Field交换
		// 定义一个临时变量来保存dw对象a Field的值
		int temp = dw.a;
		// 把dw对象b Field的值赋给a Field
		dw.a = dw.b;
		// 把临时变量temp的值赋给dw对象b Field
		dw.b = temp;
		System.out.println("swap方法中,a Field的值是:" + dw.a + ", b Field的值是:"
				+ dw.b);
	}

	public static void main(String[] args) {
		DataWrap dw = new DataWrap();
		dw.a = 6;
		dw.b = 9;
		swap(dw);
		System.out.println("交换结束后,a Field的值是:" + dw.a + ", b Field的值是:" + dw.b);
	}

}

程序结果为:

swap方法中,a Field的值是:9, b Field的值是:6
交换结束后,a Field的值是:9, b Field的值是:6

从上面运行结果来看,在swap方法里,a、b两个Field值被交换成功。不仅如此,main方法里swap方法执行结束后,a、b两个Field值也被交换了。这很容易造成一个错觉:调用swap方法时,传入swap方法的就是dw对象本身,而不是它的复制品。但是这仅仅是一种错觉,下面还是结合示意图来说明程序的执行过程。

程序从main方法开始执行,main方法开始创建了一个DataWrap对象,并定义了一个dw引用变量来指向DataWrap对象,这是一个与基本类型不同的地方。创建一个对象时,系统内存中有两个东西:堆内存中保存对象本身,栈内存中保存了引用该对象的引用变量。接着程序通过引用来操作DataWrap对象,把该对象的a、b两个Field分别赋值给6、9。此时系统内存中的存储示意图如图4所示。




接下来,main方法中开始调用swap方法,main方法并未结束,系统会分别开辟出main和swap两个栈区,用于存放main和swap方法的局部变量。调用swap方法时,dw变量作为实参传入swap方法,同样采用值传递方式:把main方法里的dw变量的值赋给swap方法里的dw形参,从而完成swap方法的dw形参的初始化。值得指出的是:main方法中的dw是一个引用(也就是一个指针),它保存了DataWrap对象的地址值,当把dw的值赋给swap方法的dw形参后,即让swap方法的dw形参也保存了这个地址值,即也会引用到堆内存中的DataWrap对象。图5显示了dw传入swap方法后的存储示意图。



从图5来看,这种参数传递方式是不折不扣的值传递方式,系统一样复制了dw的副本传入swap方法,但关键在于dw只是一个引用变量,所以系统复制了dw变量,但并未复制DataWrap对象 。

当程序在swap方法中操作dw形参时,由于dw只是一个引用变量,故实际操作的还是堆内存中的DataWrap对象。此时,不管操作main方法里的dw变量,还是操作swap方法里的dw变量,其实都是操作它所引用的DataWrap对象,它们操作的是同一个对象。因此,当swap方法中交换dw参数所引用DataWrap对象的a、b两个Field值后,我们看到main方法中的dw变量所引用DataWrap对象的a、b两个Field值也被交换了。


为了更好地证明main方法中的dw和swap方法中的dw是两个变量,我们在swap方法的最好一行增加如下代码:dw=null;

public static void swap(DataWrap dw) {
		// 下面三行代码实现dw对象的a、b两个Field交换
		// 定义一个临时变量来保存dw对象a Field的值
		int temp = dw.a;
		// 把dw对象b Field的值赋给a Field
		dw.a = dw.b;
		// 把临时变量temp的值赋给dw对象b Field
		dw.b = temp;
		System.out.println("swap方法中,a Field的值是:" + dw.a + ", b Field的值是:"
				+ dw.b);
		//把dw直接赋值为null,让它不再指向任何有效地址
		dw=null;
	}

执行上面修改后的代码结果是swap方法中的dw变量不再指向任何有效内存,程序其他地方不做任何修改。main方法调用swap方法后,再次访问dw变量a、b两个Field,依然可以输出9、6。可见,main方法中的dw变量没有收到任何影响。实际上,当swap方法中增加dw=null;代码后,内存中的存储示意图如图6所示。

从图6来看,把swap方法中的dw赋值为null后,swap方法中失去了DataWrap对象的引用,不可再访问堆内存中的DataWrap对象。但main方法中的dw变量不受任何影响,依然引用DataWrap对象,所以依然可以输出DataWrap对象的a、b Field值。


三、总结


      Java 编程语言只有值传递参数。当一个对象实例作为一个参数被传递到方法中时,参数的值就是该对象的引用一个副本。指向同一个对象,对象的内容可以在被调用的方法中改变,但对象的引用(不是引用的副本)是永远不会改变的。

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

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

                        

                 ========以上内容出处为李刚老师的《疯狂Java讲义》=======




==================================================================================================

  作者:欧阳鹏  欢迎转载,与人分享是进步的源泉!

  转载请保留原文地址http://blog.csdn.net/ouyang_peng

==================================================================================================



相关文章
|
20天前
|
Java 开发者 微服务
Spring Boot 入门:简化 Java Web 开发的强大工具
Spring Boot 是一个开源的 Java 基础框架,用于创建独立、生产级别的基于Spring框架的应用程序。它旨在简化Spring应用的初始搭建以及开发过程。
38 6
Spring Boot 入门:简化 Java Web 开发的强大工具
|
8天前
|
存储 JavaScript 前端开发
基于 SpringBoot 和 Vue 开发校园点餐订餐外卖跑腿Java源码
一个非常实用的校园外卖系统,基于 SpringBoot 和 Vue 的开发。这一系统源于黑马的外卖案例项目 经过站长的进一步改进和优化,提供了更丰富的功能和更高的可用性。 这个项目的架构设计非常有趣。虽然它采用了SpringBoot和Vue的组合,但并不是一个完全分离的项目。 前端视图通过JS的方式引入了Vue和Element UI,既能利用Vue的快速开发优势,
55 13
|
13天前
|
算法 Java API
如何使用Java开发获得淘宝商品描述API接口?
本文详细介绍如何使用Java开发调用淘宝商品描述API接口,涵盖从注册淘宝开放平台账号、阅读平台规则、创建应用并申请接口权限,到安装开发工具、配置开发环境、获取访问令牌,以及具体的Java代码实现和注意事项。通过遵循这些步骤,开发者可以高效地获取商品详情、描述及图片等信息,为项目和业务增添价值。
46 10
|
6天前
|
前端开发 Java 测试技术
java日常开发中如何写出优雅的好维护的代码
代码可读性太差,实际是给团队后续开发中埋坑,优化在平时,没有那个团队会说我专门给你一个月来优化之前的代码,所以在日常开发中就要多注意可读性问题,不要写出几天之后自己都看不懂的代码。
42 2
|
16天前
|
JavaScript 安全 Java
java版药品不良反应智能监测系统源码,采用SpringBoot、Vue、MySQL技术开发
基于B/S架构,采用Java、SpringBoot、Vue、MySQL等技术自主研发的ADR智能监测系统,适用于三甲医院,支持二次开发。该系统能自动监测全院患者药物不良反应,通过移动端和PC端实时反馈,提升用药安全。系统涵盖规则管理、监测报告、系统管理三大模块,确保精准、高效地处理ADR事件。
|
17天前
|
Java 程序员
深入理解Java异常处理机制
Java的异常处理是编程中的一块基石,它不仅保障了代码的健壮性,还提升了程序的可读性和可维护性。本文将深入浅出地探讨Java异常处理的核心概念、分类、处理策略以及最佳实践,旨在帮助读者建立正确的异常处理观念,提升编程效率和质量。
|
18天前
|
Java 开发者 UED
深入探索Java中的异常处理机制##
本文将带你深入了解Java语言中的异常处理机制,包括异常的分类、异常的捕获与处理、自定义异常的创建以及最佳实践。通过具体实例和代码演示,帮助你更好地理解和运用Java中的异常处理,提高程序的健壮性和可维护性。 ##
42 2
|
18天前
|
Java 开发者
Java中的异常处理机制深度剖析####
本文深入探讨了Java语言中异常处理的重要性、核心机制及其在实际编程中的应用策略,旨在帮助开发者更有效地编写健壮的代码。通过实例分析,揭示了try-catch-finally结构的最佳实践,以及如何利用自定义异常提升程序的可读性和维护性。此外,还简要介绍了Java 7引入的多异常捕获特性,为读者提供了一个全面而实用的异常处理指南。 ####
39 2
|
21天前
|
Java 程序员 UED
深入理解Java中的异常处理机制
本文旨在揭示Java异常处理的奥秘,从基础概念到高级应用,逐步引导读者掌握如何优雅地管理程序中的错误。我们将探讨异常类型、捕获流程,以及如何在代码中有效利用try-catch语句。通过实例分析,我们将展示异常处理在提升代码质量方面的关键作用。
31 3
|
21天前
|
Java 数据库连接 开发者
Java中的异常处理机制:深入解析与最佳实践####
本文旨在为Java开发者提供一份关于异常处理机制的全面指南,从基础概念到高级技巧,涵盖try-catch结构、自定义异常、异常链分析以及最佳实践策略。不同于传统的摘要概述,本文将以一个实际项目案例为线索,逐步揭示如何高效地管理运行时错误,提升代码的健壮性和可维护性。通过对比常见误区与优化方案,读者将获得编写更加健壮Java应用程序的实用知识。 --- ####
下一篇
DataWorks