Java深入学习系列之值传递Or引用传递?

开发 后端
在Java方法传参时,究竟是引用传递还是值传递?关于Java里值传递还是引用传递,至少从表现形式上来看,两种观点都有支撑的论据。Java内部方法传参不是引用传递,而是引用本身的"值"的传递,归根结底还是值传递。

[[171642]]

 我们来看一个新手甚至写了多年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; 
  } 
 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

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

关于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 
  } 
 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

上述示例,输出结果为"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 
  } 
 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

上述示例,输出结果为"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 
     
 
  } 
 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

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

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

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

 

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

void changeReference(Foo a); 
  • 1.

 

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

changeReference(fooRef); 
  • 1.

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

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

 

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

aRef = bRef; 
  • 1.

 

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

modifyReference(fooRef);  
  • 1.

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

cRef.setAttribute("c"); 
  • 1.

 

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

总结

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

责任编辑:庞桂玉 来源: segmentfault
相关推荐

2022-07-29 08:05:31

Java值传递

2015-09-08 10:16:41

Java参数按值传递

2022-11-02 15:00:03

Java值传递引用传递

2023-11-15 09:14:27

Java值传递

2024-09-04 01:36:51

Java对象传递

2020-09-02 08:00:51

Java引用传递值传递

2009-08-17 14:48:44

Java参数传递机制

2020-03-23 14:15:51

RadonDB安装数据库

2012-02-21 14:04:15

Java

2011-03-25 13:44:28

Java值传递

2010-09-25 14:38:18

Java内存分配

2009-11-17 14:13:34

PHP配置

2015-09-29 08:57:46

javascript对象

2017-12-05 08:53:20

Golang参数传递

2021-10-18 15:50:49

Android强引用软引用

2009-09-04 11:00:13

通过C#引用传递

2015-09-29 09:27:04

JavaScript对象

2010-06-29 15:29:22

UML建模流程

2010-09-28 09:22:34

DOM模型Html

2010-08-31 13:06:45

CSS
点赞
收藏

51CTO技术栈公众号