当一个对象被当作参数传递到一个方法后,在此方法内可以改变这个对象的属性,那么这里到底是值传递还是引用传递?

答:是值传递。Java 语言的参数传递只有值传递。当一个实例对象作为参数被传递到方法中时,参数的值就是该对象的引用的一个副本。指向同一个对象,对象的内容可以在被调用的方法内改变,但对象的引用(不是引用的副本) 是永远不会改变的。

Java的参数传递,不管是基本数据类型还是引用类型的参数,都是按值传递,没有按引用传递!

binary传入的文件在java中以请求参数形式展示 java传参数的三种形式_内存地址

对于参数的传递可以分为两种情况:

1.基本数据类型的参数

1 public classTransferTest {2 public static voidmain(String[] args) {3 int num = 1;4 System.out.println("changeNum()方法调用之前:num = " +num);5 changeNum(num);6 System.out.println("changeNum()方法调用之后:num = " +num);7 }8
9 public static void changeNum(intx) {10 x = 2;11 }12 }

运行结果:

binary传入的文件在java中以请求参数形式展示 java传参数的三种形式_System_02

传递过程的示意图如下:

binary传入的文件在java中以请求参数形式展示 java传参数的三种形式_java传参_03

分析:num作为参数传递给changeNum()方法时,是将内存空间中num所指向的那个存储单元中存放的值1复制了一份传递给了changeNum()方法中的x变量,而这个x变量也在内存空间中分配的一个存储单元。这时就把num对的值1传递给了x变量所指向的存储单元中。此后在changeNum()方法中对x变量的一切操作都是针对于x所指向的这个存储单元,与num所指向的存储单元无关。

所以,在changeNum()方法被调用后,num所指向的存储单元的值还是没有发生变化,这就是所谓的“值传递”。

值传递的精髓是:传递的是存储单元中的内容,而不是存储单元的引用。

2.引用类型的参数

示例1:

1 public classTransferTest2 {2 public static voidmain(String[] args) {3 Person person = newPerson();4System.out.println(person);5change(person);6System.out.println(person);7}8
9 public static voidchange(Person p) {10 p = newPerson();11}12}13
14 /**15 * Person类
16*/
17 classPerson {18
19 }

运行结果:

binary传入的文件在java中以请求参数形式展示 java传参数的三种形式_内存地址_04

可以看出两次打印结果一致。即调用change()方法后,person变量并没发生改变。

传递过程的示意图如下:

binary传入的文件在java中以请求参数形式展示 java传参数的三种形式_内存地址_05

分析:

01.当程序执行到第3行 Person person = new Person()时,程序在堆内存(heap)中开辟了一块内存空间用来存储Person类实例对象,同时在栈内存(stack)中开辟了一个存储单元来存储该实例对象的引用,即上图中person指向的存储单元。

02.当程序执行到第5行 change(person)时,person作为参数(实参)传递给饿了change()方法。这里是person将自己的存储单元的内容传递给了change()方法的p变量。此后在change()方法中对p变量的一切操作都是针对于p变量所指向的存储单元,与perosn所指向的存储单元就没有关系了。

示例2:

1 public classP {2 String name = "P";3 publicP(String name) {4 this.name =name;5 }6 @Override7 publicString toString() {8 returnname;9 }10 }11
12 public classTest {13 static P p1 = new P("p1");14 public static voidmain(String[] args) {15 P p = new P("P");16      System.out.println("before change p:" +p.toString);17 changeObj(p);18 System.out.println("after change p:" +p.toString());19 }20
21 static voidchangeObj(P p) {22 p = new P("pp");23 System.out.println("change p:" +p.toString());24 //p = p1;25 //System.out.println(p.toString());
26 }27 }

运行结果:

binary传入的文件在java中以请求参数形式展示 java传参数的三种形式_System_06

首先要理解 “=” 赋值操作的意义:

对于基本数据类型来说 “=”赋值操作是直接改变内存地址(存储单元)上的值。

对于引用类型来说 “=” 赋值操作是改变引用变量所指向的内存地址(上文中存储单元)。

调用changeObj()方法进入第21行:

binary传入的文件在java中以请求参数形式展示 java传参数的三种形式_java传参_07

执行22行后:

binary传入的文件在java中以请求参数形式展示 java传参数的三种形式_java传参_08

所以对外部的p变量是没有影响的。

总结:

函数参数传递其实是一个赋值的过程,基本类型传递的是数值,引用类型传递的引用对象的内存地址。

另外一点要特别注意,函数的参数其实是函数内部的局部变量。不要跟外部变量混淆。

示例3:

1 packagecn.canshu;2 classPerson{3 private intid;4 privateString name;5 public Person(intid,String name) {6 this.id =id;7 this.name =name;8 }9 }10 public classDemo02 {11 public static voidmain(String[] args) {12 Person a = new Person(23, "a");13 Person b = new Person(22,"b");14 System.out.println("交换前\na:"+a+"\nb:"+b);15 swap(a, b);16 System.out.println("交换后\na:"+a+"\nb:"+b);17 }18 private static voidswap(Person a, Person b) {19 Person temp =a;20 a =b;21 b =temp;22 }23 }

运行结果:

binary传入的文件在java中以请求参数形式展示 java传参数的三种形式_内存地址_09

同理,外部的ab引用不是内部的ad引用,故不会发生改变。

我们可以发现,Java方法的传值,实际上是把实参的值—-对象引用(对象的内存地址)传递给了形参,从而形参和实参的值(即变量里存储的内存地址,非变量本身的内存地址)是相同的,指向了同一个对象/内存地址。