Java中的参数传递

为了便于理解,会将参数传递分为按值传递和按引用传递。

其实核心理解:实参和形参是两个变量,名称相同而已。如果都指向同一对象,而方法又改变了对象内容,则原值才发生变化。

形参、实参

  • 形参: 定义方法使用的参数。

形参只会在方法被调用的时候,虚拟机才会分配内存单元,方法调用结束后便会释放所分配的内存单元。因此,形参只在方法内部有效。

基本数据类型作为参数传递

基本数据类型作为参数传递时都是传递值的拷贝。 指的是方法调用中,传递的是值的拷贝,无论怎么改变这个拷贝,原值是不会改变的。

也即是说实参把它的值传递给形参,对形参的改变不会影响实参的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Test {
public static void main(String[] args) {
Test test1 = new Test();
int i = 5;
System.out.println("调用前的i=" + i);
test1.testPassParameter(i);
//传递后,testPassParameter方法中对形参i的改变不会影响这里的i
System.out.println("调用后的i=" + i);
}
public void testPassParameter(int i) {
i = 10;//这里只是对形参的改变
System.out.println("tpp方法中的i=" + i);
}
}

输出结果

1
2
3
调用前的i=5
tpp方法中的i=10
调用后的i=5

对象作为参数传递

对象作为参数传递时,在方法内改变对象的值,有时原对象跟着改变,而有时又没有改变。就会让人对“传值”和“传引用”产生疑惑。

先看看两种情况的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Test {
public static void main(String[] args) {
StringBuffer s1 = new StringBuffer("hello");
StringBuffer s2 = new StringBuffer("hello");
changeStringBuffer(s1, s2);
System.out.println("s1=" + s1);
System.out.println("s2=" + s2);
}
public static void changeStringBuffer(StringBuffer ss1, StringBuffer ss2) {
ss1.append("world");
ss2 = ss1;
}
}

输出结果:

1
2
s1=helloworld  
s2=hello

分析如下:

1
2
3
//(1)s1,s2指向字符串的地址不同,假设为地址1,地址2
StringBuffer s1 = new StringBuffer("hello");
StringBuffer s2 = new StringBuffer("hello");
1
2
3
//(2)调用changeStringBuffer方法
changeStringBuffer(s1, s2);
//调用后会将s1,s2的地址(地址1,地址2)传给ss1,ss2,即现在ss1也指向地址1,ss2也指向地址2
1
2
3
4
//(3)ss1所指向字符串的值变为helloworld,调用者s1的值相应变化。
ss1.append("world");
//ss2将指向ss1指向的地址(地址1),但此时s2依旧指向地址2s2的值在调用前后不变。
ss2 = ss1;

所以,s1的输出为helloworld,s2的输出结果为hello。

(注意,这里为了区分,s1,s2和ss1,ss2用了不同的名称,但有时候形参与实参的名字相同,其实两者变量是完全不同的,一个是main方法中的变量,一个是changeStringBuffer()中的局部变量。)

可以看出,在java中对象作为参数传递时,传递的是引用的地址,是把对象在内存中的地址拷贝了一份传给了参数

拓展:

String是final类型,是个特殊的类,对它的一些操作符是重载的。比如:

1
2
3
4
5
6
7
String str = "hello";//可能创建一个或者不创建对象。
//如果"hello"这个字符串在常量池里不存在,会在java常量池里创建一个String对象,str指向这个内存地址。
//无论以后用这种方式创建多少个值为"hello"的字符串对象,始终只有一个内存地址被分配
//java中称为“字符串驻留”,所有的字符串常量都会在编译后自动地驻留

String str = "hello";
str = str + "world";//等价于str = new String(new StringBuffer(str).append("world"));

从以上分析,现在可以更易理解下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Test {
public static void fun(String str, char[] ch) {
str = "world";
ch[0] = 'd';
}
public static void main(String[] args) {
String str = new String("hello");
char[] ch = {'a', 'b', 'c'};
fun(str, ch);
System.out.println(str);
System.out.println(ch);
}
}

输出结果

1
2
hello  
dbc

方法调用时,名称相同的实参和形参并不一样,一个是main()中的str ,指向存放”hello”的内存地址。一个是fun()中的str,str=”world”,str指向常量池中的”world”内存地址。ch[0]=’d’,对象的内容发生改变。

所以main()中str变量存放的对象内容依然是”hello”。ch从”abc”变为”dbc”。