Java中的参数传递机制

通过前一篇文章的介绍,我们从整体上明白了,Java类中变量的差异性、不同变量在内存中的存储位置,以及变量的生命周期等。今天,我们来看一下Java中参数传递的机制。

形参:方法声明时包含的参数声明

实参:调用方法时,实际传给形参的参数值

Java方法的参数传递机制:

Java方法的参数传递只有一种:值传递。所谓值传递,就是将实际参数值的副本,传入方法内,而参数本身不会收到任何影响。

PS:传入方法的时实际参数值的复制品,不管方法中对这个复制品如何操作,实际参数本身不会受到任何影响。

基本类型的参数传递

首先,来看一个demo,

public class TestPrimitiveTransfer {

	public static void swap(int a,int b){
		int temp =a ;
		a = b;
		b = temp;
		System.out.println("swap方法内,a="+a+";b="+b);
	}

	public static void main(String[] args) {
		int a = 6;
		int b =9;
		TestPrimitiveTransfer.swap(a, b);
		System.out.println("main函数里面,a="+a+";b="+b);
	}
}

执行结果是:

swap方法内,a=9;b=6

main函数里面,a=6;b=9

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

在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变量,只是存在于不同的方法栈区中而已。

值传递的实质:

上面的交换程序,main方法栈区中a、b的值并没有任何改变,程序改变的只是swap方法栈中的a、b。

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

引用类型的参数传递

Java对于引用类型的参数传递,一样采用的是值传递方式。但是使用对象引用时,容易发生误解,因为它能够交换成功。

看如下例子:

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

测试:

public class TestReferenceTransfer {
	public static void swap(DataSwap ds){
		int temp = ds.a;
		ds.a=ds.b;
		ds.b=temp;
		System.out.println("swap方法内,a="+ds.a+";b="+ds.b);
//		ds = null ;// 把ds直接赋为null,让它不在指向任何有效地址。
	}
	public static void main(String[] args) {
		DataSwap ds = new DataSwap();
		ds.a=6;
		ds.b=9;
		swap(ds);
		System.out.println("main方法内,a="+ds.a+";b="+ds.b);
	}
}

运行结果:

swap方法内,a=9;b=6

main方法内,a=9;b=6

上面运行结果看:再swap方法里,a、b两个属性值被交换成功。不仅如此,main方法里的swap方法直接结束后,a、b两个属性的值也被交换了。这很容易造成一种错觉:调用swap方法时,传入swap方法的,就是dw对象本身,而不是它的复制品。但这种结论时错误的。

程序从main方法开始执行,main方法创建了一个DataSwap对象,并定义了一个dw引用变量来指向DataSwap对象。

创建一个对象时,系统内存中有两个实体:堆内存中保存了对象本身,栈内存中保存了该对象的引用。

接着,程序通过引用操作DataSwap对象,把该对象的a、b属性分别赋值。如下图:

接下来,main方法里开始调用swap方法,main方法并未结束,系统会分别开辟出main和swap两个栈区,分别用于存放main和swap方法的局部变量。

调用swap方法时,ds变量作为实参,传入swap方法,同样采用值传递方式:

把main方法里ds变量的值,赋给swap方法里的dw形参,从而完成了swap方法的ds形参的初始化。

值得指出的是:main方法里的ds是一个引用,它保存的DataSwap对象的地址值,档把dw的值赋给swap方法的dw形参后,即让swap方法的dw形参也保存这个地址值,即也引用到堆内存的DataSwap对象。

如下图,显示了ds传入swap方法后的存储示意图:

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

真相揭秘:

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

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

dw = null ;//把dw直接赋为null,让它不在指向任何有效地址。

public class TestReferenceTransfer {
	public static void swap(DataSwap ds){
		int temp = ds.a;
		ds.a=ds.b;
		ds.b=temp;
		System.out.println("swap方法内,a="+ds.a+";b="+ds.b);
		ds = null ;// 把ds直接赋为null,让它不在指向任何有效地址。
	}
	public static void main(String[] args) {
		DataSwap ds = new DataSwap();
		ds.a=6;
		ds.b=9;
		swap(ds);
		System.out.println("main方法内,a="+ds.a+";b="+ds.b);
	}
}

运行结果:

swap方法内,a=9;b=6

main方法内,a=9;b=6

示意图:

解析:

swap里的ds变量不在指向任何有效内存地址,程序其他地方不做任何修改。main方法调用了swap方法后,再次访问ds变量的a、b属性,依然可以输出结果。可见,main方法里的ds变量没有收到任何影响。

形参长度可变长的方法

从JDK1.5之后,Java允许定义形参长度可变的参数,从而允许为方法指定数量不确定的形参。

如果在定义方法时,再最后一个形参的类型后增加三点(...),则辨明该形参可接受多个参数值,多个参数值被当作数组传入。

限制:该中方式的参数,必须放在形参的末尾

总结一下:

Java中的参数传递,无论是基本类型,还是引用类型,他们传递的都是一份副本。基本类型传递的是值本身的副本,引用类型传递的是地址的副本。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-12-15 01:20:56

Java中的参数传递机制的相关文章

java中利用反射机制绕开编译器对泛型的类型限制

首先看下面这个例子 public static void main(String[] args) { ArrayList<Integer> al1 = new ArrayList<Integer>(); al1.add(1); ArrayList<String> al2 = new ArrayList<String>(); al2.add("hello"); //int型链表和string型链表,结果为true System.out.pr

Java中的异常处理机制

Java中的异常处理机制 示例 public class test { public static void main(String[] args) { // TODO Auto-generated method stub String s="hello"; int i=Integer.parseInt(s); } } 运行异常结果 在上述代码中Integer.parseInt表示把字符串类型转化成整数类型,同时Integer是Int的封装类:程序会报错,因为计算机没有办法把hello

图解JAVA中的类加载机制(详细版)

注:本文为作者整理和原创,如有转载,请注明出处. 上一篇博文,把JAVA中的Class文件格式用图形的方式画了一下,逻辑感觉清晰多了,同时,也为以后查阅的方便. Class文件只是一种静态格式的二进制流,它只有被虚拟机加载进内存解析之后才会生成真正的运行时的结构,因此,搞清楚类加载机制不但有助于我们加深理解Class文件中各个字段的含义,同时也有利于我们更深入的了解JAVA代码背后的暗流涌动.比如new关键字背后,虚拟机都做了什么?JAVA中的哪些操作会真正导致类被加载?哪些操作又会导致类被初始

浅说Java中的反射机制(二)

写过一篇Java中的反射机制,不算是写,应该是抄了,因为那是别人写的,这一篇也是别人写的,摘抄如下: 引自于Java基础--反射机制的知识点梳理,作者醉眼识朦胧.(()为我手记) 什么是反射? 正常编译执行java文件时,会生成一个.class文件,反射就是一个反编译的过程,它可以通过.class文件得到一个java对象.一个类会有很多组成部分,比如成员变量.成员方法.构造方法等,反射可以通过加载类(加载类是个什么东西?一直搞不清楚),解剖出类的各个组成部分. 为什么要用反射? 我们需要访问一个

关于JAVA中的反射机制的总结

JAVA中的反射机制是一种能够大大增强程序扩展性的技术.当一个程序封装完成后(生成exe文件),如果想要对该程序进行功能扩展,不用进行代码的从新修改(也不可能拿到源码),只要修改配置文件(一般是XML)就可以完成. 这样的程序为外部提供了一个接口,只要按照该接口的规则定义一个对象(功能),即可以为应用程序扩展该功能. 可以这样比喻,你买了一块电脑主板(封装好的程序),此时你想要添加一块声卡(要扩展的功能),此时只能够通过一个pci接口(应用程序提供的接口)来进行扩展.声卡的接口必须符合PCI的规

Java中的参数传递

Java中方法参数传递是:值传递(Pass By Value) public class ParameterDemo{     public void changeValue1(int a){         a = 8;     }     public void changeValue2(Employee e){         e.salary = 8000;     }     public void changeValue3(Employee e){         e = new E

java中的反射机制在Android开发中的用处

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制.      然而在android中Google很多的类的某些方法不让第三方应用去调用,通过java反射机制能把这些隐藏方法获取出来并调用,三方应用上我们就很方便的去用这些方法.      例如我们需要安全的杀死某个应用的服务和进程调用ActivityManager.forceStopPack

Java中的异常处理机制的简单原理和应用。

异常是指java程序运行时(非编译)所发生的非正常情况或错误,与现实生活中的事件很相似,现实生活中的事件可以包含事件发生的时间.地点.人物.情节等信息,可以用一个对象来表示,Java使用面向对象的方式来处理异常,它把程序中发生的每个异常也都分别封装到一个对象来表示的,该对象中包含有异常的信息. Java对异常进行了分类,不同类型的异常分别用不同的Java类表示,所有异常的根类为java.lang.Throwable,Throwable下面又派生了两个子类:Error和Exception,Erro

黑马程序员【java中的反射机制】

Java中的反射机制 ------- android培训.java培训.期待与您交流! ---------- java的反射机制是java的特性之一,反射机制是构建框架技术的基础所在,使用反射可以使程序更加灵活,避免将程序写死在代码里.相对于很多初学者只接触过java基础的人,反射还是一个很朦胧难懂的概念,下面我们就来说一下反射的一些应用. java反射机制是指在运行状态中,动态获取信息以及动态调用对象方法的功能.java反射有3个动态性质:1.运行时生成对象实例,2.运行期间调用发放,3.运行