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()方法里面,a和b的值已经交换过来了。
但是请注意,Java调用参数的方法是pass-by-copy,也就是说,虽然在swap()方法里,参数a和b(所谓的形参)获取了 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,尽管完全没有人会来看。。

原文地址:https://www.cnblogs.com/limuyuan/p/is-java-pass-by-value-or-reference.html

时间: 2024-10-11 07:47:08

Java调用方法参数究竟是传值还是传址?的相关文章

五分钟学Java:可变参数究竟是怎么一回事?

在逛 programcreek 的时候,我发现了一些专注基础但不容忽视的主题.比如说:Java 的可变参数究竟是怎么一回事?像这类灵魂拷问的主题,非常值得深入地研究一下. 我以前很不重视基础,觉得不就那么回事嘛,会用就行了.就比如说今天这个主题,管它可变不可变呢,不就是个参数嘛,还能有多大学问--抱着这种态度,我一直横行江湖近十载(苦笑).可等到读者找我提一些基础的问题时,我几乎回答不上来,感觉知识是散的,或者是浮于表面的.幸好最近一段时间,我开始幡然醒悟,开始不放过任何一个细节,渐渐地,有点"

Java的方法参数-想想挺有趣的问题

一直认为Java的方法参数都是传递值,调用后对本身不影响. Java不存在C/C++中的指针,在快速排序中,传入的数组,却发生了值的改变.由此引发的思考: //为方便举例,以下为部分快速排序伪代码 传入的数组,在递归中,数组值被操作. void quickSort(int s[], int l, int r){ if (l < r){ quickSort(s, l, i - 1); quickSort(s, i + 1, r); } } 结论:Java方法参数中传递的是值,在参数是引用类型(如数

JAVA获取方法参数名的分析(一)

关于题目 首先解释一下题目. 我们知道, Java通过反射,可以从一个类得知它有哪些方法,有哪些变量,也可以知道每个方法中有哪几个什么类型的传入参数.但有一个东西反射取不到,那就是我们对方法传入参数的命名. 取得传入参数的名字有什么意义? 对这个问题的探究,源于在写一个测试类时候的需求.假设我们有一个类需要测试,这个类中有数十个方法.为每个方法编写测试类,将耗费大量的时间和精力.因此我有一种想法,就是通过java的反射,获得这个类所有的方法,再通过传入参数的名字和参数类型,来生成一些符合要求的数

Java传值和传址

调用函数时,传的参数过去可能是传值,也可能是传址.如果是传值,函数内部的操作对参数的值没有影响:如果是传址,函数内部的操作是对参数指向的内存进行操作,会影响参数的值. Java到底是传值还是传址?用下面这个例子可以实验一下: package test; import java.util.ArrayList; import java.util.List; public class ValueAndAddress { public static void f1(int x){ x = x + 5;

javascript的变量,传值和传址,参数之间关系

先把收获晾一下: 1.javascrip变量包含两种类型的值,一种为引用类型的值,一种是基本类型的值.引用类型包括:Array,Object,Function(可以这么理解,非基本类型的都是引用类型);5种基本类型包括:undefined,null,string,boolean,number 2.函数的参数的传递的机制是复制变量值. 书上说:”把函数外部的值复制给函数内部的参数,就和把值从一个变量复制给另一个变量一样.基本类型的传递如同基本类型变量的复制一样,而引用类型的则如同引用类型变量的复制

JS 基础知识2 传值和传址

要知道传址跟传址,首先我们先了解下数据值的操作. 1复制:可以把它赋值给一个新的变量 2传递:可以将他传递给一个函数或者方法 3比较:可以与另外一个值比较,判断是否相等 简要介绍下传值: 当一个数据是通过值被操作的,那么关系到的是数据的值,在赋值的过程中,是对实际值进行了COPY,储存在一个变量或者属性或数组中,copy的值与原数据是相互独立的. 当数据通过值传递给一个函数时,数据的一份传递给这个函数,如果函数体修改了这个值,只在函数体受影响,函数外的原数据不受影响. 当一个数据通过值和另外一个

javascript . 05 json的组成、for...in 遍历对象、简单数据类型与复杂数据类型的传值与传址、内置对象

对象字面量  JSON var obj = { aaa :999}; var json={"aaa":999,"bbb":888}; "kay":value 对象字面2??定义方法和json很像,只有一点不同,json的key 必须加"" : 对象,数组也可以作为键值 JSON的组成 Javascript Object Notation 对象字面量的一种表现形式 : 键名必须有双引号""包围 : 为什么用J

php自定义函数的“传值”与“传址”学习笔记

参数的传递,可以分为 传值 和 传址 两种,究竟有什么区别呢? 1 2 3 4 5 6 7 8 9 10 function fun($num1,$num2) { $num1=100; $num2=1000; } $n1=1; $n2=10; fun($n1,$n2); echo “n1={$n1} n2={$n2}”; ?> 得到的结果是:n1=1 n2=10 这一段代码是传值的过程,自定义函数fun的参数传递具体$n1,$n2的值,这个很容易理解,我之前说的自定义函数都是传值,那什么是传址呢?

C# 函数的传值与传址(转)

http://www.cnblogs.com/mdnx/archive/2012/09/04/2671060.html using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { class Program { /* 先来了解一下什么是实参, 什么是形参. 所谓形参其实也就是说函数定义的时候所带的参数. 比如 static