Java之值传递Or引用传递?

为了说明问题, 我给出一个非常简单的class定义:

public class Foo {
 String attribute;
 Foo(String s) {
 this.attribute = s;
 }
 void setAttribute(String s) {
 this.attribute = s;
 }
 String getAttribute() {
 return this.attribute;
 }
}

下面在阐明观点时,可能会多次用到该类。
关于Java里值传递还是引用传递,至少从表现形式上来看,两种观点都有支撑的论据。下面我来一一分析:
观点1:引用传递
理由如下:
先看一段代码

public class Main {
 public static void modifyReference(Foo c){
 c.setAttribute("c"); // line DDD
 }

 public static void main(String[] args) {
 Foo fooRef = new Foo("a"); // line AAA
 modifyReference(fooRef); // line BBB
 System.out.println(fooRef.getAttribute()); // 输出 c
 }
}

上述示例,输出结果为”c”,而不是”a”, 也就是传入的fooRef里的属性被修改了,发生了side-effect。

我们在 line AAA 处新创建了一个Object Foo并将其引用 fooRef 在 line BBB 处传给了方法 modifyReference() 的参数 cRef , 该方法内部处理后, fooRef 指向的Object中的值从”a”变成了”c”, 而引用 fooRef 还是那个引用, 因此,我们是否可以认为,在 line BBB 处发生了引用传递?
先留着疑问,我们继续往下看。

观点2:值传递
继续看一段代码

public class Main {
 public static void changeReference(Foo aRef){
 Foo bRef = new Foo("b");
 aRef = bRef; // line EEE
 }

 public static void main(String[] args) {
 Foo fooRef = new Foo("a"); // line AAA
 changeReference(fooRef); // line BBB
 System.out.println(fooRef.getAttribute()); // 输出 a
 }
}

上述示例,输出结果为”a”, 而不是”b”, 即对传入的fooRef内部的change并没有影响外部的传入前的值。

我们在 line AAA 处新创建了一个Object Foo并将其引用 fooRef 在 line EEE 处传给了方法 changeReference() 的参数 aRef , 该方法内部引用 aRef 在 line DDD 处被重新赋值。如果是引用传递,那么引用 aRef 在 line EEE 处已经被指向了新的Object, 输出应该为 “b” 才对,事实上是怎样的呢?事实上输出了 “a” ,也就是说 changeReference() 方法改变了传入引用所指对象的值。

观点1和观点2的输出结果多少会让人有些困惑,别急,我们继续往下看。
深入分析
为了详细分析这个问题,把上述两段代码合起来:

public class Main {
 public static void modifyReference(Foo cRef){
 cRef.setAttribute("c"); // line DDD
 }
 public static void changeReference(Foo aRef){
 Foo bRef = new Foo("b"); // line FFF
 aRef = bRef; // line EEE
 }

 public static void main(String[] args) {
 Foo fooRef = new Foo("a"); // line AAA
 changeReference(fooRef); // line BBB
 System.out.println(fooRef.getAttribute()); // 输出 a

 modifyReference(fooRef); // line CCC
 System.out.println(fooRef.getAttribute()); // 输出 c
 }
}

下面来深入内部来详细分析一下引用和Object内部的变化。
来看下面图示:
① Line AAA, 申明一个名叫 fooRef ,类型为 Foo 的引用,并见其分配给一个新的包含属性值为 “f” 的对象,该对象类型为 Foo 。

Foo fooRef = new Foo("a"); // line AAA

图片
② Line DDD, 方法内部,申明了一个 Foo 类型的名为 aRef 的引用,且 aRef 被初始化为 null 。

void changeReference(Foo a);

图片
③ Line CCC, changeReference() 方法被调用后,引用 aRef 被分配给 fooRef 指向的对象。

changeReference(fooRef);

图片
④ Line FFF, 申明一个名叫 bRef ,类型为 Foo 的引用,并见其分配给一个新的包含属性值为 “b” 的对象,该对象类型为 Foo

Foo bRef = new Foo("b");

图片
⑤ Line EEE, 将引用 aRef 重新分配给了包含属性 “b” 的对象。此处注意,并非将 fooRef 重新分配,而是 aRef

aRef = bRef;

图片
⑥ Line CCC, 调用方法 modifyReference(Foo cRef) 后,新建了一个引用 cRef 并将之分配到包含该属性 “f” 的对象上,该对象同时被两个引用 fooRef 和 cRef 指向着。

modifyReference(fooRef);

图片
⑦ Line DDD, cRef.setAttribute(“c”); 将会改变 cRef 引用指向的包含属性 “f” 的对象,而该对象同时被引用 fooRef 指向着。

cRef.setAttribute("c");

图片
此时引用 fooRef 指向的对象内部属性值 “f” 也被重新设置为 “c” 。

总结
Java内部方法传参不是引用传递,而是引用本身的”值”的传递,归根结底还是值传递 。将一个对象的引用 fooRef 传给方法的形参 newRef ,将给该对象新增了一个引用,相当于多了一个 alias 。我们可以通过这个原引用 fooRef ,或这是方法参数里的新引用 newRef 去访问、操作原对象,也可以改变参数里的引用 newRef 本身的值,却无法改变原引用 fooRef 的值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值