从一道题目看起
先来看一道题,请问以下最终输出的整型 a
是 100
还是 200
?字符串 str
是 Jack
还是 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
方法时,changePerson1
和 changePerson2
被初始化为 jack
和 rose
两个对象引用的副本,所以交换的只是这两个副本,实参的对象引用指向对象不变。值得注意的是,在 Java 中,字符串类型 String
不是一个基本数据类型,它就是一个对象,所以任何企图改变字符串引用指向的方法都将会是徒劳。
总结
方法不能修改基本数据类型的参数(即数值型或布尔型)
方法可以改变对象参数的状态
方法不能让一个对象参数引用一个新的对象
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。