Java中方法参数的值传递机制
首先,我们要知道在调用方法时,传入的实参不会直接在方法内使用,而是重新在栈中申请一个副本(形参)来使用。这种传递方法称作值传递。
基本数据类型
如果参数是基本数据类型,此时实参给形参的是实参真实存储的数据值。
public class ValueTransferTest1 {
public static void main(String[] args) {
//交换两个变量的值
int m = 10;
int n = 20;
System.out.println("m = " + m + ",n = " + n);
swap(m, n);
System.out.println("m = " + m + ",n = " + n);
}
public static void swap(int m, int n){
int temp = m;
m = n;
n = temp;
}
}
/*
result :
m = 10,n = 20
m = 10,n = 20
*/
内存结构解析 :
在调用 swap 方法时,方法的形参会新生成两个 int 类型变量 m 、n。在方法内,形参的 m 、n 得到交换,与main方法中的 m 、n 无关,只是将其值传给了形参而已。
如果仍然想要使用方法来交换main方法中 m 、n的值,那么就将其封装在类中,通过对象 . 属性来交换两个变量的值 :
public class ValueTransferTest2 {
public static void main(String[] args) {
Data data = new Data();
data.m = 10;
data.n = 20;
System.out.println("m = " + data.m + ",n = " + data.n);
//交换 m, n的值
swap(data);
System.out.println("m = " + data.m + ",n = " + data.n);
}
public static void swap(Data data){
int temp = data.m;
data.m = data.n;
data.n = temp;
}
}
class Data{
int m;
int n;
}
/*
result :
m = 10,n = 20
m = 20,n = 10
*/
引用数据类型
-
如果参数是引用数据类型,此时实参赋给形参的是实参存储数据的地址值。
public class ValueTransferTest3 { public static void main(String[] args) { int[] arr1 = new int[]{0, 0, 0, 0, 0}; int[] arr2 = new int[]{1, 1, 1, 1, 1}; System.out.println(Arrays.toString(arr1)); System.out.println(Arrays.toString(arr2)); swap(arr1, arr2); System.out.println(Arrays.toString(arr1)); System.out.println(Arrays.toString(arr2)); } public static void swap(int[] arr1, int[] arr2){ int[] temp = arr1; arr1 = arr2; arr2 = temp; } } /* result : [0, 0, 0, 0, 0] [1, 1, 1, 1, 1] [0, 0, 0, 0, 0] [1, 1, 1, 1, 1] */
内存结构解析 :
在调用 swap 方法时,方法的形参会新生成两个 int[] 类型变量 arr1 、arr2。在方法内,形参的 arr1 、arr2 的值交换,与main方法中的 arr1、arr2 无关,只是将地址值赋值给形参而已。
同样,如果仍然想要使用方法来交换main方法中 arr1 、arr2,那么就将其封装在类中,通过对象 . 属性来交换两个变量的值 :
public class ValueTransferTest4 {
public static void main(String[] args) {
int[] arr1 = new int[]{0, 0, 0, 0, 0};
int[] arr2 = new int[]{1, 1, 1, 1, 1};
Arr arr = new Arr();
arr.arr1 = arr1;
arr.arr2 = arr2;
System.out.println(Arrays.toString(arr.arr1));
System.out.println(Arrays.toString(arr.arr2));
swap(arr);
System.out.println(Arrays.toString(arr.arr1));
System.out.println(Arrays.toString(arr.arr2));
}
public static void swap(Arr arr){
int[] temp = arr.arr1;
arr.arr1 = arr.arr2;
arr.arr2 = temp;
}
}
class Arr{
int[] arr1;
int[] arr2;
}
/*
result :
[0, 0, 0, 0, 0]
[1, 1, 1, 1, 1]
[1, 1, 1, 1, 1]
[0, 0, 0, 0, 0]
*/
C++中函数参数的传递机制
和 Java 一样,C++的函数在调用的时候,也会在栈中申请一个副本(形参)来存实参的值。与 Java 不同当传入的实参是指针时,我们称作址传递,当传入的实参不是指针,则称为值传递。
值传递
如果参数不是指针,那么情况和 Java 相同 :
#include <iostream>
using namespace std;
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
int main()
{
int a = 10;
int b = 20;
cout << "a = " << a << ",b = " << b << endl;
swap(a, b);
cout << "a = " << a << ",b = " << b << endl;
return 0;
}
/*
result :
a = 10,b = 20
a = 10,b = 20
*/
内存结构解析也和 java 相同, 此处不再赘述。
址传递
如果参数是指针的话,与 Java 的引用传递所不同的是,C++中的指针参数可以解引用,从而实现交换 :
#include <iostream>
using namespace std;
void swap(int * a, int * b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int main()
{
int a = 10;
int b = 20;
cout << "a = " << a << ",b = " << b << endl;
swap(&a, &b);
cout << "a = " << a << ",b = " << b << endl;
return 0;
}
/*
result :
a = 10,b = 20
a = 20,b = 10
*/
解引用 : 将指针变量中存的地址上保存的内容解析出来。此操作不改变指针变量的指向,且可以修改指向的地址上的内容,所以可以实现交换。
下面是一段错误的代码,和 Java 中引用数据类型交换的错误一样 :
#include <iostream>
using namespace std;
void swap(int * a, int * b)
{
int * temp = a;
a = b;
b = temp;
}
int main()
{
int a = 10;
int b = 20;
cout << "a = " << a << ",b = " << b << endl;
swap(&a, &b);
cout << "a = " << a << ",b = " << b << endl;
return 0;
}
/*
result :
a = 10,b = 20
a = 10,b = 20
*/
内存结构解析和 java 相同, 此处不再赘述。
在 C++ 中,当传入的值为对象时,那么在函数中会通过拷贝构造函数生成一个新的对象用来操作。当传入的值为对象引用时,则可类比 Java 中的引用传递,当传入的值为对象指针时,那么就可以通过解引用的操作来改变对象本身。
#include <iostream>
#include <string>
using namespace std;
class Person
{
public :
string name;
int age;
Person(string name = "Null", int age = 0) : name(name), age(age) {}
};
ostream & operator<<(ostream & cout, Person & p)
{
cout << "name = " << p.name << ",age = " << p.age << endl;
return cout;
}
//方法一 : 传入对象引用 将Person中的参数通过 对象.属性 的方法全部交换一遍
void swapFiled(Person & p1, Person & p2)
{
int tempAge = p1.age;
p1.age = p2.age;
p2.age = tempAge;
string tempName = p1.name;
p1.name = p2.name;
p2.name = tempName;
}
//方法二 : 传入对象指针 解引用直接交换两个对象
void swapPerson(Person * p1, Person *p2)
{
Person temp = *p1;
*p1 = *p2;
*p2 = temp;
}
//直接传入对象
void swap(Person p1, Person p2)
{
//此时无论是直接交换对象还是使用 对象.属性的方法都不行
//因为此函数内的 p1 p2 是通过拷贝构造函数新生成的一份
//与main函数的 p1 p2 没有一点关系
/*
Person temp = p1;
p1 = p2;
p2 = temp;
*/
int tempAge = p1.age;
p1.age = p2.age;
p2.age = tempAge;
string tempName = p1.name;
p1.name = p2.name;
p2.name = tempName;
}
int main()
{
Person p1("Tom", 18);
Person p2("John", 20);
cout << p1 << p2 << endl;
swapPerson(&p1, &p2);
cout << p1 << p2 << endl;
swapFiled(p1, p2);
cout << p1 << p2 << endl;
return 0;
}
/*
result :
name = Tom,age = 18
name = John,age = 20
name = John,age = 20
name = Tom,age = 18
name = Tom,age = 18
name = John,age = 20
*/