值传递
我们可以直接看一个值传递的例子
public void swap(int x, int y) {
int temp = x;
x = y;
y = temp;
}
@Test
void testObject() {
int a = 100;
int b = 200;
swap(100, 200);
System.out.println("a: " + a);
System.out.println("b: " + b);
}
//输出
a: 100
b: 200
可见,a 和 b 的值在经过“所谓的”交换函数之后,并没能把值交互。这是为什么呢?
这就是因为java是值传递的。也就是说,我们在调用一个需要传递参数的函数时,传递给函数的参数并不是我们传进去的参数本身,而是它的副本。
以这个例子来说,当我们调用 swap 这个函数时,并不是传入的真正的 a 和 b 这两个参数,而是他们俩的复制品,比如我们定义这两个复制品为 x 和 y,此时 x 会指向另一个 100,y 会指向另一个 200。之后在方法 swap 中的所有操作,其实都是基于这两个复制出来的变量 x 和 y 进行着的。尽管 x 和 y 的值确实交换了,但是他们值的改变并不能影响到 a 和 b。
再来看一个例子:
void foo(int val) {
val = 100;
}
void foo1(String text) {
text = "win";
}
@Test
public void print() {
int val = 200;
foo(val);
System.out.println(val);
String text = "hello";
foo1(text);
System.out.println(text);
}
无可厚非,输出分别是 200 和 hello。可以用图片来描述一下流程
首先,在调用方法之前,会先创建一个形参 val

当执行函数中的语句时,其实都是对拷贝的那个参数,即形参 val 进行赋值,可以看到,我们将他直接变为了 100,而实际的参数 val,还是原来的 200

引用传递
顾名思义,就是方法的参数是一个类的引用
class People {
int age;
}
@Test
void testObject() {
People p1 = new People();
People p2 = new People();
p1.age = 10;
p2.age = 15;
System.out.println("p1.age: " + p1.age + " p2.age: " + p2.age);
p1 = p2;
System.out.println("p1.age: " + p1.age + " p2.age: " + p2.age);
p1.age = 30;
System.out.println("p1.age: " + p1.age + " p2.age: " + p2.age);
}
//输出
p1.age: 10 p2.age: 15
p1.age: 15 p2.age: 15
p1.age: 30 p2.age: 30
通过图示我们可以分析一下
首先在栈中建立两个引用 p1 和 p2,分别指向堆中 new 出来的对象

执行 p1.age=30 这个语句,即把第二个 People 对象的 age 属性变为30,由于 p1 和 p2 都指向这个对象,因此 p1 和 p2 的 age 属性都是15

我们可以再来看一个例子
void foo(StringBuffer stringBuffer) {
stringBuffer.append("world");
}
@Test
void bufferTest2() {
StringBuffer sb = new StringBuffer("hello");
foo(sb);
System.out.println(sb);
}
输出结果是 helloworld,表明原来的值已被改变。照例,我们画个图
一开始,栈中的 StringBuffer 引用指向堆中的 StringBuffer 对象,该对象里面的值为 "hello"

然后调用方法,append 方法直接改变的就是原来 String 的值。可以看到,此时堆中 StringBuffer 对象中的值已经改变,此时两个引用 sb 和 stringBuffer 都指向同一个对象

我们再来看另一个例子
void foo1(StringBuffer stringBuffer) {
stringBuffer = new StringBuffer("world");
}
@Test
void bufferTest2() {
StringBuffer sb = new StringBuffer("hello");
foo1(sb);
System.out.println(sb);
}
此时输出的值没有改变,还是 "hello"。这是为什么呢?
当调用 fool 方法的时候,实际在栈中又新创建了一个 StringBuffer 的引用 stringBuffer,这个引用重新指向了一个新的 StringBuffer 对象。
所以即使当执行方法之后,输出 sb,依旧还是原来的 sb 指向的对象中的值,即 "hello"
网友评论