Java 引用传递与值传递
【背景】
首先 Java 中没有值传递和引用传递的概念,所谓 pass by value 和 pass by reference 只是从其他语言引申的概念而已。所以你会听到有人说 ”Java 中没有引用传递,全部都是值传递“,然而在使用过程中却又总能遇到 ”方法调用确实改变了某些值“ 的情况。
一、基本类型与引用类型
基本类型:byte/char/int/long/float/double
引用类型:除了上述类型之外的类型,包括各种扩展类型(Integer/Character/Double等),String,实体类,List 等;
int num = 10;
String str = "hello";
如图所示,num是基本类型,值就直接保存在变量中。而str是引用类型,变量中保存的只是实际对象的地址。一般称这种变量为"引用",引用指向实际对象,实际对象中保存着内容。
二、赋值运算符 ”=“
当使用赋值运算符时,会发生什么?
num = 20;
str = "word";
对于基本类型,赋值直接改变了值。对于引用类型而言,new了一个新对象 word,并将str中保存的地址更新为了新的地址。由于原地址0x11不再持有任何引用,(0x11 -> hello) 会被垃圾回收掉。
三、方法调用时发生了什么
// 第一个例子:基本类型
void foo(int value) {
value = 100;
}
// num 没有被改变
foo(num);
// 第二个例子:没有提供改变自身方法的引用类型
void foo(String text) {
text = "windows";
}
// str 也没有被改变
foo(str);
// 第三个例子:提供了改变自身方法的引用类型
StringBuilder sb = new StringBuilder("iphone");
void foo(StringBuilder builder) {
builder.append("11pro");
}
// sb 被改变了,变成了"iphone11pro"
foo(sb);
// 第四个例子:提供了改变自身方法的引用类型,但是不使用,而是使用赋值运算符。
StringBuilder sb = new StringBuilder("iphone");
void foo(StringBuilder builder) {
builder = new StringBuilder("ipad");
}
// sb 没有被改变,还是 "iphone"。
foo(sb);
// 第五个例子:List
List<User> userList = new ArrayList<>();
void foo(List<User> list) {
// 有效
userList.add(new User("aaaaa", 2));
userList = new ArrayList<>();
// 无效
userList.add(new User("bbbbb",1));
}
/*
【总结】
1. 使用 = 对方法参数赋值的,都不能改变调用者的值
2. 使用引用类型提供的改变自身的方法 (如 List.add(); StringBuilder.append(); )
可以改变其值 (类似引用传递)
*/
四、都是这样的吗
int[][] arr3 = new int[3][3];
setArr(arr3);
private static void setArr(int[][] arr3) {
// 有效
arr3[2][1] = 1;
// 有效
arr3[1] = new int[] {1,2,3};
// 无效
arr3 = new int[5][6];
}
不能完全按照 ”=“ 操作来判断是否为 ”引用传递“ 。 因为在以上代码中,实例化的二维数组中,arr3 处保存了一个地址(栈区),指向了其实际值的位置(堆区)。