Java的传递很多人都知道是值传递。但不少人说不出这个值到底指的是什么?有的时候如基本类型int,long表现的像值。有的时候如List又表现的像传递引用。这到底是怎么一回事呢?
值传递还是引用传递?
先看两个例子:
public static void updateBasicInt(int a1) {
a1 = 5;
}
public static void updateListByAddElement(List<Integer> nums1) {
nums1.add(5);
}
@Test
public void should_not_change_basic_int() {
int a = 10;
updateBasicInt(a);
assertThat(a).isEqualTo(10);
}
@Test
public void should_update_list_when_add_element() {
List<Integer> nums = new ArrayList<>();
nums.add(1);
nums.add(2);
assertThat(nums.size()).isEqualTo(2);
PassValueOrRef.updateListByAddElement(nums);
assertThat(nums.size()).isEqualTo(3);
}
看到这里有的人可能会说:可变对象传引用,不可变对象传值。然而真的是这样吗?
再看一个例子:
public static void updateListByNewList(List<Integer> nums1) {
nums1 = new ArrayList<>();
nums1.add(5);
}
@Test
public void should_not_update_list_when_new_list() {
List<Integer> nums = new ArrayList<>();
nums.add(1);
nums.add(2);
assertThat(nums.size()).isEqualTo(2);
PassValueOrRef.updateListByNewList(nums);
assertThat(nums.size()).isEqualTo(2);
}
如果真的是可变对象List传引用,为什么nums还是看起来没有被改变?nums的引用明明在updateListByNewList方法中被更改了呀。
实际上,Java只有值传递一种方式。对于基本类型,基本类型被直接存储在栈中。当基本类型的参数被传递给方法时,拷贝一份实参并存储在新开辟的栈空间中。 而对于对象引用,引用变量被存储在栈空间中,而引用变量实际指向的对象被存储在堆空间中。当一个对象被传递给方法时,会拷贝一份引用变量并存储在新开辟的栈空间中,但引用变量仍只指向之前已经创建的存储在堆中的对象。
valueOrRef.png
所以updateListByAddElement方法会更改它们共同指向的堆空间中的对象。而updateListByNewList方法将在方法内部创建的引用变量指向了一份新开辟的堆空间,故对方法外的引用变量所指向的引用变量指向的堆空间中的对象没有修改。
参考资料: