zoukankan      html  css  js  c++  java
  • Java调用方法参数究竟是传值还是传址?

    之前阅读《Head First Java》的时候,记得里面有提到过,Java在调用方法,传递参数的时候,采用的是pass-by-copy的方法,传递一份内容的拷贝,即传值。
    举一个最简单的例子:

     1 public class Test {
     2     public static void main(String[] args) {
     3         int numberA = 1;
     4         int numberB = 2;
     5         swap(numberA, numberB);
     6         System.out.println(numberA);
     7         System.out.println(numberB);
     8     }
     9     
    10     public static void swap(int a, int b) {
    11         int c = a;
    12         a = b;
    13         b = c;
    14     }
    15 }

    这里,swap(int a, int b)方法的目的是交换参数a, b 的值,不过这是不会实现的。

    虽然在方法里面将变量a的值赋给了一个临时变量temp,再将变量b的值赋给了a,最后将temp的值赋给了b。这个时候,b中保存的是之前a中的值,a中保存的也是b中的值,起码在swap()方法里面,ab的值已经交换过来了。
    但是请注意,Java调用参数的方法是pass-by-copy,也就是说,虽然在swap()方法里,参数ab(所谓的形参)获取了 numberA 和 numberB 的值(所谓的实参),但是获取值的方法是拷贝了实参的值赋给形参,并不是让形参直接指向实参在内存中的地址(所谓的指针)。
    所以,这段代码输出的结果是:

    1
    2
    点击查看

    本例中用的是原始类型(Primitive Type)int,那么对于引用类型,是不是也是这样的呢?让我们来看下面这段代码:

     1 import java.util.ArrayList;
     2 import java.util.List;
     3 
     4 public class Test {
     5     public static void main(String[] args) {
     6         List<Integer> aList = new ArrayList<Integer>();
     7         aList.add(1);
     8         addToList(aList);
     9         System.out.println(aList);
    10     }
    11 
    12     public static void addToList(List<Integer> list) {
    13         list.add(2);
    14     }
    15 }

    这段代码里,我们首先新建了一个ArrayList aList,并向里面添加了一个数字“1”。然后我们尝试调用 addToList(List<Integer> list) 方法来向aList里面添加数字“2”。这样做是否会成功呢?

    答案是,会成功的。输出结果为:

    [1, 2]
    点击查看

    纳尼?刚刚不是还说,Java不是pass-by-copy传值的吗?
    难道不是应该这样:list只是aList的一个复制品而已,不论在addToList()方法里面对list进行任何操作,最后都不会影响到aList()吗?

    前一阵子我一直是这么想的,还和同事为了一个类似的问题争执了好久。他坚持说这里是传址的,可我清清楚楚地记得《Head First Java》里告诉我们,Java是pass-by-value的。。
    但是现在来看,被调用的方法确确实实影响了主调方法参数的值。所以问题究竟出在哪里呢?

    对于这个问题,我认真思考了一下,外加最近学习的OCA里也有提到这个,整理一下我自己的理解。
    首先,Java确确实实是传值(pass-by-value)的,在上面的例子里,传过去的确确实实也是一个copy,但是不要忘了,引用型(Reference Type)变量里面存放的值究竟是什么。
    我们这里的引用型变量aList被声明为 List<Integer>类型,也就是说,aList变量里面只可以接收对 List<Integer> 对象的引用
    这里所说的“引用”,其实也就是地址,也就是指针。
    也就是说,当我们调用 addToList(List<Integer> list) 方法的时候,传给参数list的值,实际上是对相同对象的一个引用。用《Head First Java》里遥控器和家电的比喻来说的话,我们这里只有一台电视和一个遥控器。然后我们复制了一个一模一样的遥控器出来,两个遥控器拥有一模一样的功能,比如开关,选台,调音量等。。而我们的电视只有一台,所以,用另外一个遥控器,是确确实实可以对这一台电视进行操作的。
    所以到这里就很清晰了,Java仍然是传值(pass-by-value)的语言,关键在于,你传的是什么样的一个值。

    最后让我们来看看OCA上面关于这部分知识点的一个小练习,有几个小陷阱,自己好好分析:

     1 public class ReturningValues {
     2     public static void main(String[] args) {
     3         int number = 1;
     4         String letters = "abc";
     5         number(number);
     6         letters = letters(letters);
     7         System.out.println(number + letters);
     8     }
     9     
    10     public static int number(int number) {
    11         number++;
    12         return number;
    13     }
    14     
    15     public static String letters(String letters){
    16         letters += "d";
    17         return letters;
    18     }
    19 }

    先自己做一下,做完之后再看答案:

    1abcd
    点击查看

    你做对了吗?如果做错了,最可能的原因是你没有注意到第5行只是调用了那个方法,而并没有获取到方法的返回值。以后自己写代码的时候一定要注意避免犯这个错误!

    PS:为了把答案折叠起来,本来已经用Markdown写好了,硬是新开了一篇用TinyMCE编辑器改HTML,尽管完全没有人会来看。。

  • 相关阅读:
    序列化流与反序列化流
    io流之Properties类
    io流之转换流与缓冲流
    Io流之File概述(递归/字节流/字符流)
    基本数据类型包装/System类/Math类/Arrays类
    Date类概述与Calendar类概念
    String类型概述
    20180926 小小插件 (弹窗)
    20180901 文件加载 错误处理 错误的触发
    20180828 优化留言板 功能(增加 删除 修改 查看)!
  • 原文地址:https://www.cnblogs.com/limuyuan/p/is-java-pass-by-value-or-reference.html
Copyright © 2011-2022 走看看