Java深入学习系列之值传递Or引用传递?
我们来看一个新手甚至写了多年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",而不是"c"。
我们在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"。
我们在line AAA处新创建了一个Object Foo并将其引用fooRef在line EEE处传给了方法changeReference()的参数aRef, 该方法内部引用aRef在line DDD处被重新赋值。如果是引用传递,那么引用aRef在line EEE处已经被指向了新的Object, 输出应该为"b"才对,事实上是怎样的呢?事实上输出了"b",也就是说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的值。
作者:夏磊洲
来源:51CTO
Java深入学习系列之值传递Or引用传递?相关推荐
- JAVA基础学习之路(十一)引用传递
引用传递: 不同栈内存可以指向同一块堆内存,不同栈内存可以对一块堆内存进行修改 范例一: class Message {private int num = 10;public Message(int ...
- 死磕面试系列,Java到底是值传递还是引用传递?
Java到底是值传递还是引用传递? 这虽然是一个老生常谈的问题,但是对于没有深入研究过这块,或者Java基础不牢的同学,还是很难回答得让人满意. 可能很多同学能够很轻松的背出JVM.分布式事务.高并发 ...
- java对象引用出错_“Java有值传递和引用传递”为什么错了?
前言 初学Java的时候,老师在课堂上说"Java有值传递和引用传递",但网上"Java只有值传递"的呼声很高. 本人在查找资料的过程中,在这两个说法之间反复横 ...
- 深入Java集合学习系列:LinkedHashSet的实现原理
转载自 深入Java集合学习系列:LinkedHashSet的实现原理 1. LinkedHashSet概述: LinkedHashSet是具有可预知迭代顺序的Set接口的哈希表和链接列表实现 ...
- [初级]Java命令学习系列(六)——jinfo
转载自 [初级]Java命令学习系列(六)--jinfo jinfo可以输出java进程.core文件或远程debug服务器的配置信息.这些配置信息包括JAVA系统参数及命令行参数,如果进程运行在64 ...
- [中级]Java命令学习系列(五)——jhat
转载自 [中级]Java命令学习系列(五)--jhat jhat(Java Heap Analysis Tool),是一个用来分析java的堆情况的命令.之前的文章讲到过,使用jmap可以生成Java ...
- Java命令学习系列(二)——Jstack
转载自 Java命令学习系列(二)--Jstack jstack是java虚拟机自带的一种堆栈跟踪工具. 功能 jstack用于生成java虚拟机当前时刻的线程快照.线程快照是当前java虚拟机内每一 ...
- Java命令学习系列(三)——Jmap
转载自 Java命令学习系列(三)--Jmap jmap是JDK自带的工具软件,主要用于打印指定Java进程(或核心文件.远程调试服务器)的共享对象内存映射或堆内存细节.可以使用jmap生成Heap ...
- Java命令学习系列(一)——Jps
转载自 Java命令学习系列(一)--Jps jps位于jdk的bin目录下,其作用是显示当前系统的java进程情况,及其id号. jps相当于Solaris进程工具ps.不象"pgrep ...
最新文章
- POJ1001--Exponentiation(幂计算)翻译
- 等级考试文件服务器,内核级 Samba 文件共享服务器 CIFSD 正式开始测试
- 052、overlay如何实现跨主机通信?(2019-03-19 周二)
- LeetCode MySQL 1398. 购买了产品A和产品B却没有购买产品C的顾客
- 【Python】猜数小游戏
- python识别鼠标在excel中选中的区域_[Excel技巧] 提高你工作效率的9个Excel技巧,职场新人必备...
- 我月薪3W,却不用熬夜加班做报表,这个养老工具,你一定要知道
- select, poll, epoll的实现分析
- 爆赞,java后端开发路线。
- C++无法打开库文件/无法打开源文件
- Industry工业软件开发平台设计A-1
- ubuntu挂载移动硬盘时提示Unable to mount
- salt returner mysql_saltstack实战2--远程执行之返回(returner)
- Jenkins构建项目
- html下拉加载原理,GitHub - gavinjzx/wxPull: 原生JS实现微信公众号或网页使用下拉加载和上拉刷新...
- 洛谷 P3817 小A的糖果
- java批量生成二维码图片,并打包成zip
- 这些秋季儿童养生小常识,你要知道!
- 微信小程序——video视频全屏展示
- 学习成绩 =90分的同学用A表示,60-89分之间的用B表示,60分以下的用C表示。(C语言版)