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

来源:互联网 发布:ubuntu debian 编辑:程序博客网 时间:2024/05/29 19:06

一. 先看看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 是值类型”的人,都是本本主义者。"

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



原创粉丝点击