值传递和引用传递

值传递

我们可以直接看一个值传递的例子

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

当调用 foo 函数的时候,实参 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=p2 这个语句,把栈中 p1 指向 p2 在堆中指向的位置,即第二个 People 对象,此时 p1 和 p2 都指向了堆中第二个 People 对象

执行 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"

当使用方法时,形参会先产生一个 StringBuffer 引用 stringBuffer,然后指向堆中的对象

然后调用方法,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"

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容