形参全称为“形式参数”是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传递的参数。
实参是全称为"实际参数"是在调用时传递给函数的参数,即传递给被调用函数的值。实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。 因此应预先用赋值,输入等办法使实参获得确定值。
引用调用是函数传递参数的一种方式,使用引用调用,可以在子函数中对形参所做的更改对主函数中的实参有效。
传值调用是指方法在调用参数时,不是对原参数进行操作,而是创建参数的拷贝并对进行操作,这种调用有利于保护数据。
我们开始分析下下面示例:
测试代码
int mint5 = 5;
System.out.println("mint5 == "+mint5);
setIntValue9(mint5);
System.out.println("mint5 == "+mint5);
Integer mInteger3 = 3;
// 测试将Integer对象传入之后是否可以重新复制引用
System.out.println("mInteger3 == "+mInteger3);
setValue12(mInteger3);
// 在方法内参数的引用,不会改变外部实参的引用
System.out.println("mInteger3 == "+mInteger3);
// 在方法内参数的引用,不会改变外部实参的引用
String mString3 = "3";
System.out.println("mString3 == " + mString3);
setStr4(mString3);
System.out.println("mString3 == " + mString3);
// 由于String的不可变性,从而String在方法体内可以理解为改变了外部引用,StringBuilder没有改变外部引用
StringBuilder mStringBuilder5 = new StringBuilder("5");
System.out.println("mStringBuilder5 =="+mStringBuilder5);
setStrBuilder(mStringBuilder5);
System.out.println("mStringBuilder5 =="+mStringBuilder5);
MTest mTest = new MTest();
// 在方法内参数的引用,不会改变外部实参的引用
System.out.println("mTest =="+mTest);
resetMTest(mTest);
System.out.println("mTest =="+mTest);
对应的函数与类
private static void setIntValue9(int mint5) {
mint5 = 9;
}
private static void setValue12(Integer i) {
i = new Integer(12);
}
static void resetMTest(MTest e) {
e = new MTest();
}
static void setStr4(String mString) {
mString = new String("4");
}
static void setStrBuilder(StringBuilder mString) {
mString.append("append");
}
public static class MTest {
public int i = 0;
public Integer mInteger;
public MTest() {
mInteger = new Integer(0);
}
}
static void setMTest(MTest e) {
e.i = 1;
}
以上函数中可以理解,函数外为实参,函数体中传入的参数为形参, ,以下为运行结果
mint5 == 5 //运行前mint == 5
mint5 == 5 //运行结束后未能改变,由于是值传递,所以外部实参无变化
mInteger3 == 3
mInteger3 == 3 //结束后mInteger3未变 ,由于在函数中赋值,重新变更了引用,未同步到外部实参
mString3 == 3
mString3 == 3 //运行结束后由于String的不可变性以及在函数中赋值,重新变更了引用,未同步到外部实参
mStringBuilder5 ==5
mStringBuilder5 ==5append //传入引用未变化,但对象内容发生变化
mTest ==Test$MTest@677327b6mTest.i0
mTest ==Test$MTest@677327b6mTest.i0 //由于在函数中赋值,重新变更了引用,未同步到外部实参
mTest ==Test$MTest@677327b6mTest.i1 //未能重现改变引用,但是改变了引用对象的数值,则对象内容发生变化
也可以通过以下文字理解:外部实参就是a,实参的地址是0x1,但是指向的堆中地址是0x9,0x9才是实际对象地址,然后函数内形参拷贝了a,对象b地址0x2,但是也指向了堆中0x9。对b这个指向的对象操作,就是可以理解对于a的操作,因为a与b指向的是同一个地址,但是如果在函数内对b重新赋值,相当于b指向的不在是a指向的对象地址,那么就不会对a指向的对象造成影响。
通过上面我们可以总结如下:
1.在Java中基本数据类型对象传入函数是传值调用,而引用数据类型调用函数是引用调用,函数传参数实质上是一个内容拷贝的过程,把实参拷贝成形参,这也解释了在Java中无法在函数中改变引用了。
2.在函数调用是传值调用时,函数内改变的是形参,并没有改变函数外实参的值,实参的值可以传递给形参,但是形参不能影响实参。
3.在函数调用是引用调用时,如果传入函数的参数是对象,无论对对象做了何种操作,都不会改变实参对象的引用(即为对象指向的地址在函数调用后不会发生任何改变),但是如果改变了对象的内容,就会改变实参对象的内容。
延伸c\c++:
函数调用其实就是内容拷贝的过程,
来源于 http://blog.chinaunix.net/uid-27645052-id-3337748.html
先来解释下实参和形参,所谓实参,就是通过主函数传递给子函数的变量。而子函数中用来接收变量的参数就称形参。如下:
点击(此处)折叠或打开
- #include<stdio.h>
- void fun(int p,int q)
- {
- int temp;
-
- temp = p;
- p = q;
- q = temp;
-
- return ;
- }
-
- int main()
- {
- int x = 1,y = 2;
-
- fun(x,y);
-
- printf("%d\t%d\n",x,y);
-
- return 0;
- }
以上代码中,x,y是main函数要传递给子函数fun的实参,而p,q就是用来接收x和y的值的形参,所以,形参和实参的类型必须相同。 函数在传参过程中,是一个内容的拷贝过程,而每个函数都会有自己的栈空间来存放局部变量,所以,形参和实参是不同的内存单元。通俗点讲,就是,你改变他们其中一个的值,另一个的值不会改变。
如上程序,main函数将x和y传给了fun函数的p和q,相当于p和q是对x和y的拷贝,p和q是存放于fun函数的栈空间的,x和y是存放于main函数的栈空间,它们是不同的内存单元,所以在fun函数中交换p和q的值并不影响x和y的值,x和y的值仍然分别为1和2。而且,随着fun函数的结束,p和q这两个局部变量会被释放。
但是,当你传递的参数是个指针的时候,就可以改变实参的值,以下通过程序来详细分析。
点击(此处)折叠或打开
- #include<stdio.h>
- void fun(int *p,int *q)
- {
- int temp;
-
- temp = *p;
- *p = *q;
- *q = temp;
-
- return ;
- }
-
- int main()
- {
- int x = 1,y = 2;
-
- fun(&x,&y);
-
- printf("%d\t%d\n",x,y);
-
- return 0;
- }
这段程序的输出结果为2 1。这是为什么呢?
main函数在给fun函数传递参数时,传递的是x和y的地址,而fun函数用p和q接收了这两个地址,也就是,p中存放的是x的地址,q中存放的是y的地址,即*p和*q就分别是x和y了,然后再将*p和*q进行交换,也就是将x和y进行了交换,所以最终x的值为2,y的值为1。
但是如下程序传了指针,却没有改变x和y的值:
点击(此处)折叠或打开
- #include<stdio.h>
- void fun(int *p,int *q)
- {
- int *temp;
-
- temp = p;
- p = q;
- q = temp;
-
- return ;
- }
-
- int main()
- {
- int x = 1,y = 2;
-
- fun(&x,&y);
-
- printf("%d\t%d\n",x,y);
-
- return 0;
- }
这段程序输出结果为1 2。为什么呢?
虽然main函数将x和y的地址传给了fun函数,但是fun函数交换的是p和q的值,也就是,只对两个地址进行了交换,此时p中存放y的地址,q中存放x的地址,但是,这并没有影响x和y的值,所以x仍为1,y仍为2。
由上面3个程序,我们可以知道,对子函数形参的改变,并不影响main函数实参的值;但是,当main函数实参传递的是变量的地址,子函数形参作为指针p的时候,对*p进行操作,会改变main函数中变量的值;但是如果单纯对指针p进行操作,也一样不会影响main函数中实参的值。