Java 方法参数的作用

从一道题目看起


先来看一道题,请问以下最终输出的整型 a100 还是 200 ?字符串 strJack 还是 Rose ?字符数组的输出 [A, B, C] 还是说 [X, B, C]

public class Example {
    public static void main(String[] args) {
        int a = 100;
        String str = "abc";
        char[] ch = {'A', 'B', 'C'};
        change(a, str, ch);
        System.out.println("a = " + a + ", str = " + str + ", ch = " + Arrays.toString(ch));
    }

    public static void change(int a, String str, char[] ch) {
        a = 200;
        str = "xyz";
        ch[0] = 'X';
    }
}

实际上输出结果是:a = 100, str = abc, ch = [X, B, C],这可能跟有些人猜测的答案有所不一样,为什么呢?嫌麻烦的朋友可以直接拉到最后看总结。

按值调用和按引用调用


按值调用(Call by Value)表示方法接收的是调用者提供的变量值,按引用调用(Call by Reference)表示方法接收的是调用者提供的变量地址。

相信很多初学 Java 的人都搞不清楚,甚至很多人到现在还搞不清楚 Java 的方法参数是值传递还是引用传递,或者是两个都有?

一个方法是不能修改按值传递的变量值的,但是可以修改按引用传递的变量的值,熟悉 C++ 的朋友可以看以下例子(不熟悉 C++ 的朋友也可以跳过,不影响下面阅读)

    #include <iostream>
    using namespace std;
    
    void change(int a, int *b) {
        a = 200;
        *b = 200;
    }
    
    int main() {
        int a = 100;
        int b = 100;
        change(a, &b);
        cout << "a = " << a << endl;
        cout << "b = " << b << endl;
        return 0;
    }

在 Java 的方法中,只有按值调用,没有按引用调用,很多人以为 Java 方法参数是基本数据类时是按值调用,参数是对象时是引用调用,这是错误的。

Java方法参数的类型


Java 的数据类型分为基本数据类型和对象引用,很自然的方法的参数类型也就是这两种。

调用方法时,方法参数会初始化为传入实参的一个副本,基本数据类型传递的是值,对象引用则是初始化为指向实参的一个对象引用副本。

方法参数的作用


方法能不能改变基本数据类型的参数?

答案是不可以。本文开头的例子可能比较抽象,形参和实参起了相同的名称,我们来看这个例子,输出结果是 x = 100,实参 x 的在调用方法后并没有得到改变。

   public class Example {
       public static void main(String[] args) {
           int x = 100;
           change(x);
           System.out.println("x = " + x);
       }
       
       // 方法不能改变基本数据类型的参数
       public static void change(int y) {
           y = 200;
       }
   }

事实上形参 y 拿到的只是 x 的值的副本,并不是真的拿到了实参 x ,当 change 方法执行结束后参数变量 y 就不能再使用了,这就是为什么本文开头的代码里,a 的值依然是 100

方法能不能改变对象的参数状态呢?

答案是可以。先看一个例子,输出结果是 jack.name = Rose

public class Example {
    public static void main(String[] args) {
        Person jack = new Person("Jack");
        change(jack);
        System.out.println("jack.name = " + jack.name);
    }
    
    // 方法可以改变对象的参数状态
    public static void change(Person changePerson) {
        changePerson.name = "Rose";
    }
}
    
class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }
}

可以看到,jack 对象的参数从一开始的 Jack,调用方法后变为了 Rose, 调用方法时,changePerson 会初始化为 jack 值的一个副本,这里就是一个对象引用。changePerson 修改它指向的对象的 name 字段值,方法调用结束后 changePerson 不能再使用了,但是 jack 继续引用那个 name 字段值被修改过的对象。这就是为什么本文开头的例子当中,字符数组 ch 里的值能被改变的原因

方法能不能让一个对象引用一个新的对象?

答案是不可以,看一个反例

public class Example {
    public static void main(String[] args) {
        Person jack = new Person("Jack");
        Person rose = new Person("Rose");
        swap(jack, rose);
        System.out.println("jack.name = " + jack.name + ", rose.name = " + rose.name);
    }
        

    // 企图交换两个对象引用指向的对象是白费力气
    public static void swap(Person changePerson1, Person changePerson2) {
        Person temp = changePerson1;
        changePerson1 = changePerson2;
        changePerson2 = temp;
    }
}

class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }
}

调用 swap 方法时,changePerson1changePerson2 被初始化为 jackrose 两个对象引用的副本,所以交换的只是这两个副本,实参的对象引用指向对象不变。值得注意的是,在 Java 中,字符串类型 String 不是一个基本数据类型,它就是一个对象,所以任何企图改变字符串引用指向的方法都将会是徒劳。

总结

方法不能修改基本数据类型的参数(即数值型或布尔型)

方法可以改变对象参数的状态

方法不能让一个对象参数引用一个新的对象


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。