Java相关的面试经常会遇见这样的题目:
private void function(int a) {
a = 2;
}
@Test
public void test() {
int a = 1;
function(a);
System.out.println(a);// result == 1
}
问最后的输入结果是什么,大家都知道这个结果是1.
仔细想想其实这个问题考察的是对于Java中引用传递和值传递的问题。
这里要引入一个概念,实际参数和形式参数,对于上面的代码,test方法中的int a = 1;最后作为实际参数传递到方法function()中,function方法的参数int a就是形式参数。
查资料得知:方法调用时,实际参数把它的值传递给形式参数,方法接收到的是原始参数的一个copy,此时内存中存在两个相等的基本数据类型,即实际参数和形式参数,后面方法中参数的修改都是对形参的修改,不影响实际参数。
引用传递,也成为地址传递,方法调用时实际参数的引用被传递给方法的对应的形式参数,方法接受的是原始对象的内存地址引用。在方法执行的时候,形参和实参内容相同,指向同一块内存地址,方法的执行对引用的操作将会影响到实际对象。
基本数据类型值传递:
private void f1(int a) {
a = 2;
}
@Test
public void main1() {
int a = 1;
f1(a);
System.out.println(a);// result == 1
// 对于基本数据类型是值传递,形参的值不会影响实参,也就是f1方法的参数a赋值了 a= 2,
// 但是不会影响main1方法的实参a = 1,最后输入依然是1
}
数组的传递:
private void f2(int[] c) {
c[0] = 2;
}
@Test
public void main2() {
int[] a = new int[1];
a[0] = 1;
f2(a);
System.out.println(a[0]);// result == 2 ???
}
数组的数据保存和Java其他对象类型一样也是引用传递 ,main2方法中的数组a和f2方法的参数a传递的时引用指向的时同一片堆内存new int[1]的地址。 因此 方法修改了a[0] = 2也就是修改了堆中的对象,所以输入也变了(这里的引用传递也就是说f2方法的参数是把main2方法中对象的引用a传递了过来,实际上和f2方法的参数没有关系不管他是int[]a或者是int[]b)。所以f2方法最后修改了引用的值,结果输入2。
对于数组的操作对比如下代码:
private void f3(int[] a) {
a = new int[1];
a[0] = 2;
}
@Test
public void main3() {
int[] a = new int[1];
a[0] = 1;
f3(a);
System.out.println(a[0]);// 结果是 1 result ==1
}
new int[1] 在堆内存中分配了空间,int a[]指向了这个地址空间,只有调用数组的对象的属性或者对象本身是访问的是堆中的
对象,a[0] = 1;
f3方法中的参数 int []a 被赋值了一个新创建的数组 new int[1]以后的修改都在这个对象中进行,不会修改原来main3中定义的的参数,所以不变。
字符串的传递:
@Test
public void testStr() {
String ss = "hello";
change(ss);
System.out.println(ss);// 结果:hello
}
private void change(String value) {
value = value + "--world";
System.out.println(value); // 局部结果:hello--world
}
字符串的操作结果和值传递的结果一样。
对于Integer等包装类型的传递:
@Test
public void testInteger() {
Integer integer = 10;
change(integer);
System.out.println("原始值:"+integer);//结果:
}
private void change(Integer value) {
value = +value;
System.out.println("修改的value:" + value);
}
包装类型数据的结果:
对于对象类型的操作:
@Test
public void testStringBuilderStr() {
StringBuilder ss = new StringBuilder("StringBuilder-hello");
change(ss);
System.out.println(ss);//结果:StringBuilder-hello--world
// java对于对象的传递时引用传递,对于基本数据类型的传递是值传递
}
private void change(StringBuilder sb) {
sb = sb.append("--world");
System.out.println(sb.toString());
}
使用StringBuilder创建一个对象传递到方法中,最后输入的结果是:StringBuilder-hello--world
我们能看到传递对象时最后把对象的值做了修改。
所以从上面的代码我们基本可以知道:
对于数组和其他对象类型的传递是引用传递,方法中会修改原始参数的值。
对于基本数据类型,以及基本数据类型的包装类型和String在java中采用的是值传递,不会修改原始值。