• Java参数传递是值传递还是引用传递?


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

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

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

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

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

    1.基本数据类型的参数

     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 }

    运行结果:

    传递过程的示意图如下:

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

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

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

    2.引用类型的参数

    示例1:

     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 }

     运行结果:

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

    传递过程的示意图如下:

    分析:

    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 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 }

    运行结果:

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

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

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

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

     执行22行后:

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

    总结:

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

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

    示例3:

     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("交换前
    a:"+a+"
    b:"+b);
    15         swap(a, b);
    16         System.out.println("交换后
    a:"+a+"
    b:"+b);
    17     }
    18     private static void swap(Person a, Person b) {
    19         Person temp = a;
    20         a = b;
    21         b = temp;
    22     }
    23 }

    运行结果:

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

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

  • 相关阅读:
    三类设计模式UML图
    有一种面试叫-----别人的面试
    reference to 'map' is ambiguous|
    多个if语句和else if区别
    n&m位运算
    边缘填充算法
    C# 换行
    优秀程序员必须知道的32个算法,提高你的开发效率
    作业四(不算寒假了吧)
    寒假作业三
  • 原文地址:https://www.cnblogs.com/9513-/p/8484071.html
Copyright © 2020-2023  润新知