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

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

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

  1. public class Foo {
  2. String attribute;
  3. Foo(String s) {
  4. this.attribute = s;
  5. }
  6. void setAttribute(String s) {
  7. this.attribute = s;
  8. }
  9. String getAttribute() {
  10. return this.attribute;
  11. }
  12. }

下面在阐明观点时,可能会多次用到该类。

关于Java里值传递还是引用传递,至少从表现形式上来看,两种观点都有支撑的论据。下面我来一一分析:

观点1:引用传递

理由如下:先看一段代码

  1. public class Main {
  2. public static void modifyReference(Foo c){
  3. c.setAttribute("c"); // line DDD
  4. }
  5. public static void main(String[] args) {
  6. Foo fooRef = new Foo("a"); // line AAA
  7. modifyReference(fooRef); // line BBB
  8. System.out.println(fooRef.getAttribute()); // 输出 c
  9. }
  10. }

上述示例,输出结果为"c",而不是"c"。

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

先留着疑问,我们继续往下看。

观点2:值传递

继续看一段代码

  1. public class Main {
  2. public static void changeReference(Foo aRef){
  3. Foo bRef = new Foo("b");
  4. aRef = bRef;   // line EEE
  5. }
  6. public static void main(String[] args) {
  7. Foo fooRef = new Foo("a"); // line AAA
  8. changeReference(fooRef); // line BBB
  9. System.out.println(fooRef.getAttribute()); // 输出 a
  10. }
  11. }

上述示例,输出结果为"a", 而不是"b"。

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

观点1和观点2的输出结果多少会让人有些困惑,别急,我们继续往下看。

深入分析

为了详细分析这个问题,把上述两段代码合起来:

  1. public class Main {
  2. public static void modifyReference(Foo cRef){
  3. cRef.setAttribute("c"); // line DDD
  4. }
  5. public static void changeReference(Foo aRef){
  6. Foo bRef = new Foo("b"); // line FFF
  7. aRef = bRef;   // line EEE
  8. }
  9. public static void main(String[] args) {
  10. Foo fooRef = new Foo("a"); // line AAA
  11. changeReference(fooRef); // line BBB
  12. System.out.println(fooRef.getAttribute()); // 输出 a
  13. modifyReference(fooRef); // line CCC
  14. System.out.println(fooRef.getAttribute()); // 输出 c
  15. }
  16. }

下面来深入内部来详细分析一下引用和Object内部的变化。来看下面图示:

① Line AAA, 申明一个名叫fooRef,类型为Foo的引用,并见其分配给一个新的包含属性值为"f"的对象,该对象类型为Foo。

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

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

  1. void changeReference(Foo a);

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

  1. changeReference(fooRef);

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

  1. Foo bRef = new Foo("b");

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

  1. aRef = bRef;

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

  1. modifyReference(fooRef);

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

  1. cRef.setAttribute("c");

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

总结

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

作者:夏磊洲
来源:51CTO

Java深入学习系列之值传递Or引用传递?相关推荐

  1. JAVA基础学习之路(十一)引用传递

    引用传递: 不同栈内存可以指向同一块堆内存,不同栈内存可以对一块堆内存进行修改 范例一: class Message {private int num = 10;public Message(int ...

  2. 死磕面试系列,Java到底是值传递还是引用传递?

    Java到底是值传递还是引用传递? 这虽然是一个老生常谈的问题,但是对于没有深入研究过这块,或者Java基础不牢的同学,还是很难回答得让人满意. 可能很多同学能够很轻松的背出JVM.分布式事务.高并发 ...

  3. java对象引用出错_“Java有值传递和引用传递”为什么错了?

    前言 初学Java的时候,老师在课堂上说"Java有值传递和引用传递",但网上"Java只有值传递"的呼声很高. 本人在查找资料的过程中,在这两个说法之间反复横 ...

  4. 深入Java集合学习系列:LinkedHashSet的实现原理

    转载自  深入Java集合学习系列:LinkedHashSet的实现原理 1.    LinkedHashSet概述: LinkedHashSet是具有可预知迭代顺序的Set接口的哈希表和链接列表实现 ...

  5. [初级]Java命令学习系列(六)——jinfo

    转载自 [初级]Java命令学习系列(六)--jinfo jinfo可以输出java进程.core文件或远程debug服务器的配置信息.这些配置信息包括JAVA系统参数及命令行参数,如果进程运行在64 ...

  6. [中级]Java命令学习系列(五)——jhat

    转载自 [中级]Java命令学习系列(五)--jhat jhat(Java Heap Analysis Tool),是一个用来分析java的堆情况的命令.之前的文章讲到过,使用jmap可以生成Java ...

  7. Java命令学习系列(二)——Jstack

    转载自 Java命令学习系列(二)--Jstack jstack是java虚拟机自带的一种堆栈跟踪工具. 功能 jstack用于生成java虚拟机当前时刻的线程快照.线程快照是当前java虚拟机内每一 ...

  8. Java命令学习系列(三)——Jmap

    转载自 Java命令学习系列(三)--Jmap jmap是JDK自带的工具软件,主要用于打印指定Java进程(或核心文件.远程调试服务器)的共享对象内存映射或堆内存细节.可以使用jmap生成Heap ...

  9. Java命令学习系列(一)——Jps

    转载自 Java命令学习系列(一)--Jps jps位于jdk的bin目录下,其作用是显示当前系统的java进程情况,及其id号. jps相当于Solaris进程工具ps.不象"pgrep ...

最新文章

  1. POJ1001--Exponentiation(幂计算)翻译
  2. 等级考试文件服务器,内核级 Samba 文件共享服务器 CIFSD 正式开始测试
  3. 052、overlay如何实现跨主机通信?(2019-03-19 周二)
  4. LeetCode MySQL 1398. 购买了产品A和产品B却没有购买产品C的顾客
  5. 【Python】猜数小游戏
  6. python识别鼠标在excel中选中的区域_[Excel技巧] 提高你工作效率的9个Excel技巧,职场新人必备...
  7. 我月薪3W,却不用熬夜加班做报表,这个养老工具,你一定要知道
  8. select, poll, epoll的实现分析
  9. 爆赞,java后端开发路线。
  10. C++无法打开库文件/无法打开源文件
  11. Industry工业软件开发平台设计A-1
  12. ubuntu挂载移动硬盘时提示Unable to mount
  13. salt returner mysql_saltstack实战2--远程执行之返回(returner)
  14. Jenkins构建项目
  15. html下拉加载原理,GitHub - gavinjzx/wxPull: 原生JS实现微信公众号或网页使用下拉加载和上拉刷新...
  16. 洛谷 P3817 小A的糖果
  17. java批量生成二维码图片,并打包成zip
  18. 这些秋季儿童养生小常识,你要知道!
  19. 微信小程序——video视频全屏展示
  20. 学习成绩 =90分的同学用A表示,60-89分之间的用B表示,60分以下的用C表示。(C语言版)

热门文章

  1. JS对象的两种 in 操作符
  2. win7上安装php的扩展vld
  3. lezhin漫画账号注册登录教程
  4. 安装tesseract时Status of chi_tra: sendrequest error什么意思
  5. 老毛桃重启计算机没反应,老毛桃怎么用
  6. c语言在尾部添加新节点,在单链表最后插入节点
  7. 画出含有四个节点的所有二叉树形态
  8. 章节九:cookies
  9. C++ 利用 windbg + dump + map + cod 文件分析 crash 原因
  10. 华为云CDN,是怎样拯救你的等待焦虑症