Java值传递和引用传递及形参实参的分析(实例对比c++)

时间:2022-03-15 19:25:04

一. 先看看C++的一个实例(方便理解Java)

1. 例子argtest.cpp中的swap1函数是最常见的一种,形参copy了一份实参,也即main中的a1_1和swap1中的a1_1在内存中是两份不同的数据,但传入时它们保存的值是相等的,仅此而已,所以你在swap1函数中怎么捣鼓a1_1,都不会影响到main中的a1_1;

Java基本类型的参数传入,即int、boolean、char等,可以理解成这种方法,也就是所谓的“值传递”;


2. 例子argtest.cpp中的swap2函数,其实int换成某个class也同样适用,你可以swap2中的“int *a1_2” 脑补成 "User *user1"也是完全没问题的;

     swap2是用指针传递参数,尽管如此,和swap1本质上也是一样的:“指针”也是数据,所以swap2的a1_2即形参,也是copy了一份实参,只不过这次实参是指针,但传入时它们保存的值是相等的,也就是指向的内存是同一份。也仅此而已,实参和形参依旧是2份不同的东东,所以在swap2那样“你等于我我等于你”是起不到作用的;要想起作用就得用到C/C++的取值符“ * ”(星号);

Java引用类型的参数擦混入,可以理解成这种方式,也就是所谓的“引用传递”,其中Java的引用直接看做“指针”;(其实我感觉本质依旧是值传递)


3. 例子argtest.cpp中的swap3函数,用到了c++的引用方法,本质其实也是传入了指针;

要明确的是:Java没有swap3这种方式,还需要注意的是,Java的引用和c++的引用是两回事,不要搞混了;本文针对的是Java,所以此方式不赘述;


argtest.cpp:

#include <iostream>
using namespace std;

void swap1(int a1_1, int a2_1) {
int temp = 0;
temp = a1_1;
a1_1 = a2_1;
a2_1 = temp;
}

void swap2(int *a1_2, int *a2_2) {
int *temp = 0;
temp = a1_2;
a1_2 = a2_2;
a2_2 = temp;
}

void swap3(int &a1_3, int &a2_3) {
int temp = 0;
temp = a1_3;
a1_3 = a2_3;
a2_3 = temp;
}


int main()
{
int a1_1 = 1;
int a2_1 = 2;
int a1_2 = 1;
int a2_2 = 2;
int a1_3 = 1;
int a2_3 = 2;

swap1(a1_1, a2_1);
cout<<a1_1<<" "<<a2_1; //输出结果: 1 2
cout<<"\n";

swap2(&a1_2, &a2_2);
cout<<a1_2<<" "<<a2_2; //输出结果: 1 2
cout<<"\n";

swap3(a1_3, a2_3);
cout<<a1_3<<" "<<a2_3; //输出结果: 2 1
cout<<"\n";
}



二. Java的形参和实参

1. 对比前后2个例子的前2个方法,你或许可以找到很多共同点,我认为前者的swap1对应后者的swap1,前者的swap2对应后者的swapUser;


2. 不管传入方法的是基本类型还是引用类型,形参和实参都是两份数据,只不过“存的内容”一样而已;


3. 传入引用其实可以理解为传入的是"指针",引用形参和引用实参是两份“指针”,只不过它们指向同一份数据;


4. ArgTest.java中的swapUserAge方法之所以能够成功交换数据,因为“u1.age”表示的是形参指向的数据,而不是形参本身,形参和实参虽然是两份数据,但是它们指向的数据是同一份的,所以“看起来是修改了”。其实swapUserAge中依旧无法改变main中栈里面的那个实参user1,改变的只是user1指向的那份堆内存


ArgTest.java:

public class ArgTest {
public static void main(String[] args) {
int a1 = 1;
int a2 = 2;
swap1(a1, a2);
System.out.println(a1 + " " + a2); //输出结果: 1 2

User user1 = new User(1);
User user2 = new User(2);
swapUser(user1, user2);
System.out.println(user1.age); //输出结果: 1

swapUserAge(user1, user2);
System.out.println(user1.age); //输出结果: 2
}

private static void swap1(int a1, int a2) {
int temp;
temp = a1;
a1 = a2;
a2 = temp;
}

private static void swapUser(User u1, User u2) {
User temp;
temp = u1;
u1 = u2;
u2 = temp;
}

private static void swapUserAge(User u1, User u2) {
int tempAge = u1.age;
u1.age = u2.age;
u2.age = tempAge;
}
}

class User {
public int age;
public String name;

public User(int age) {
this.age = age;
}

public void showUser() {
System.out.println("I am User");
}
}


三. Java的值类型数据和引用类型数据

引用王垠的一句话: 

"实际上,所有的数据都是引用类型就是 Scheme 和 Java 最初的设计原理。原始类型用值来传递数据只是一种性能优化(叫做 inlining),它对于程序员应该是透明(看不见)的。那些在面试时喜欢问“Java 是否所有数据都是引用”,然后当你回答“是”的时候纠正你说“int,boolean 是值类型”的人,都是本本主义者。"

我认为他说的很有道理,感兴趣的可以点进去看一下他那篇博文,写得非常不错。