博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【Java深入学习系列】之值传递Or引用传递?
阅读量:5966 次
发布时间:2019-06-19

本文共 3030 字,大约阅读时间需要 10 分钟。

我们来看一个新手甚至写了多年Java的朋友都可能不是十分确定的问题:

在Java方法传参时,究竟是引用传递还是值传递?

为了说明问题, 我给出一个非常简单的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并将其引用fooRefline 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并将其引用fooRefline EEE处传给了方法changeReference()的参数aRef, 该方法内部引用aRefline DDD处被重新赋值。如果是引用传递,那么引用aRefline 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

clipboard.png

② Line DDD, 方法内部,申明了一个
Foo类型的名为
aRef的引用,且
aRef被初始化为
null
void changeReference(Foo a);

clipboard.png

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

clipboard.png

④ Line FFF, 申明一个名叫
bRef,类型为
Foo的引用,并见其分配给一个新的包含属性值为
"b"的对象,该对象类型为
Foo
Foo bRef = new Foo("b");

clipboard.png

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

clipboard.png

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

clipboard.png

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

clipboard.png

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

总结

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

转载地址:http://mmtax.baihongyu.com/

你可能感兴趣的文章
重建控制文件--Rebuild controlfile
查看>>
PhotoShop的神奇(重新发表)
查看>>
集群节点列表编辑程序
查看>>
Nsrp实现juniper防火墙的高可用性【HA】!
查看>>
Linux下磁盘阵列raid
查看>>
Android 动态移动控件实现
查看>>
C#内置数据类型
查看>>
Lock应用之 读写锁
查看>>
oracle11g 安装在rhel5.0笔记
查看>>
PosgreSQL快速参数调优和sysbench压测
查看>>
网路游侠:铱迅软件版WEB应用防火墙试用
查看>>
MD5Init-MD5Update-MD5Final
查看>>
总结之:CentOS 6.5基于DHCP的PXE自动化安装系统详解
查看>>
Glusterfs(distribute) + DRBD + heartbeat + mon 实现分布式文件系统1
查看>>
RedHat 5.4+ Postfix +Extmail实现基于虚拟用户的邮件系统(三)
查看>>
UITableView基本用法
查看>>
Windows Server 2008域中组的简析
查看>>
保护你的聊天隐私---“外挂式”加密软件设计思路
查看>>
nginx 反向代理
查看>>
Excel复制粘贴——跳过空单元格案例
查看>>