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

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

 

 

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

 我们可以看一下microsoft的文档中对按引用传递参数的定义(如下截图):https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/ref#passing-an-argument-by-reference  

java 函数的引用 java中的引用参数_内存地址

 

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

1.基本数据类型的参数

java 函数的引用 java中的引用参数_存储单元_02

1 public class TransferTest {
 2     public static void main(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(int x) {
10         x = 2;
11     }
12 }

java 函数的引用 java中的引用参数_存储单元_02

运行结果:

java 函数的引用 java中的引用参数_内存地址_04

传递过程的示意图如下:

java 函数的引用 java中的引用参数_System_05

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

 

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

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

 

2.引用类型的参数

示例1:

java 函数的引用 java中的引用参数_存储单元_02

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

java 函数的引用 java中的引用参数_存储单元_02

 

 运行结果:

java 函数的引用 java中的引用参数_存储单元_08

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

 

传递过程的示意图如下:

java 函数的引用 java中的引用参数_System_09

分析:

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

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

 

示例2:

java 函数的引用 java中的引用参数_存储单元_02

1 public class P {
 2     String name = "P";
 3     public P(String name) {
 4         this.name = name;
 5     }
 6     @Override
 7     public String toString() {
 8         return name;
 9     }
10 }
11 
12 public class Test {
13     static P p1 = new P("p1");
14     public static void main(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 void changeObj(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 }

java 函数的引用 java中的引用参数_存储单元_02

运行结果:

java 函数的引用 java中的引用参数_System_12

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

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

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

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

java 函数的引用 java中的引用参数_内存地址_13

 

 

 执行22行后:

java 函数的引用 java中的引用参数_内存地址_14

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

总结:

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

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

 

 

示例3:

java 函数的引用 java中的引用参数_存储单元_02

1 package cn.canshu;
 2 class Person{
 3     private int id;
 4     private String name;
 5     public Person(int id,String name) {
 6         this.id = id;
 7         this.name = name;
 8     }
 9 }
10 public class Demo02 {
11     public static void main(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 void swap(Person a, Person b) {
19         Person temp = a;
20         a = b;
21         b = temp;
22     }
23 }

java 函数的引用 java中的引用参数_存储单元_02

运行结果:

java 函数的引用 java中的引用参数_存储单元_17

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

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

===================================我是分割线==========================================================================================

示例4

class Person{
     private int id;
     private String name;
     public Person(int id,String name) {
         this.id = id;
         this.name = name;
     }
    //省略get,set方法
  //省略toString方法
 }

public void demo(){
  Person person = new Person("张三",1);
 sout(person);
 demo2(person);
 sout(person);   
}

public void demo2(Person person1){
    person.setName ="李四"
    person.setId=2
  sout(person1)

}

运行结果:

改变前:person{name=‘张三’,age=1}

    person{name=‘李四’,age=2}

改变后:person{name='李四',age=2}

 

 

把person对象的地址的引用传给了person1 ,所以person和person1指向的是同一个内存地址。

 

总结一下:JAVA方法的传值,基本数据类型是值传递,引用数据类型(不包含String类型)是值传递【地址值的传递】,如果方法里有person1=new Person【new对象】,或者是给对象person1赋值了一个地址值。原来的(person)对象不改变,如果只是改变person1的属性,person的属性值也会跟着改变。

如果有疏漏,请指正(* ̄︶ ̄)!!!