關於Java 值傳遞 深度分析

關於Java 值傳遞 深度分析
首先說觀點:java只有值傳遞沒有引用傳遞

然後再來看看值傳遞與引用傳遞兩者的定義

值傳遞(pass by value)是指在調用函數時將實際參數複製一份傳遞到函數中,這樣在函數中如果對參數進行修改,將不會影響到實際參數。

引用傳遞(pass by reference)是指在調用函數時將實際參數的地址直接傳遞到函數中,那麼在函數中對參數所進行的修改,將影響到實際參數。

這裏牢記值傳遞中將實際參數複製一份。

然後就是對於參數類型:值類型 和 引用類型。

結合起來理解就是:值類型傳遞,java是將其值內容複製一份給形參;對於引用類型傳遞,java是將其地址複製一份給形參。

下面結合實例深入理解爲什麼java只有值傳遞

複製代碼
package 字符串;

public class 值傳遞 {

public static void main(String[] args)
{
    String str1="abc";
    updateStr1(str1);
    System.out.println("main函數中"+str1);
}
public static void updateStr1(String str1)
{
    str1="cba";  //<註解>
    System.out.println("調用函數中"+str1);
}

}
複製代碼
結果:

在這裏我們能夠清晰看到我們傳遞的是String類型的對象即(引用類型),並且在調用函數中我們修改了str1爲cba,如果是引用傳遞那麼我們在主函數打印則應該是cba,

但是很遺憾我們在主函數中仍然打印出來的是abc。所以我們可以說java是值傳遞類型了嗎,答案是不完全的。

接下來再看這一段代碼:

複製代碼
package 字符串;
  
  public class person {
  private int age;
  public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class 值傳遞2 {

public static void main(String[] args)
{
    person p1=new person();
    p1.setAge(10);
    System.out.println("我在主函數裏對p1的年齡屬性賦值爲"+p1.getAge());
    setage(p1);
    System.out.println("我再從主函數裏獲取P1的年齡屬性"+p1.getAge());
}

public static void setage(person p1)
{
    p1.setAge(18);  //不是我們對它的地址進行了操作,而是我們對它地址的內容進行了操作
    System.out.println("我在調用函數裏對p1的年齡屬性重新賦值爲"+p1.getAge());
}

}
複製代碼
結果:

咦,怎麼回事這次也是傳遞的對象(引用類型),爲什麼這次我們對年齡這個字段的修改在主函數同步了呢?

別急,下面我們先來分析這兩個例子。

首先第一個類型的例子中,我們傳遞的是String類型的變量,它是一個特殊的類型的引用變量。

(不可變字符串:編譯器可讓字符串共享,即將各種字符串存放於公共存儲池中,字符串變量是指向其中相應位置 --出自《Java核心技術 卷1》)

出於這句話的理解就是每個字符串都對應一個地址:我們例一中是將str1的地址複製給了我們的形參str1,並且形參中str1的地址進行了改變指向了“cba”的地址。所以說在主函數中的str1的地址仍然指向的是“abc”所對應的地址。

所以說對於String類型的變量,我們對於給它重新賦值不是改變了它的內容,而是改變了它指向字符串的位置。這也就解釋了爲什麼java中String類型是不可變類型。

而在我們例二中,我們將p1的地址複製給了我們形參中的p1,此時他們都指向的內存中一塊相同的地址這裏存放着相同內容,所以我們在調用函數對這個地址中的內容進行修改時就會同步到我們主函數中的p1。所以這個並不意味着這個是引用傳遞。

好吧,那怎麼才能解釋好Java確實是值傳遞呢(上面String類型例子是特殊的引用類型不方便解釋)

下面我們通過這個例子說明:

複製代碼
package 字符串;
public class person {

private int age;
public int getAge() {
    return age;
}
public void setAge(int age) {
    this.age = age;
}

}
public class 值傳遞3 {

public static void main(String[] args) {
    person p1=new person();
    person p2=new person();
    p1.setAge(10);
    p2.setAge(18);
    System.out.println("我在主函數裏對p1的年齡屬性賦值爲"+p1.getAge());
    System.out.println("我在主函數裏對p2的年齡屬性賦值爲"+p2.getAge());
    swap(p1,p2); 
    System.out.println("************我是主函數裏的分割線***************");
    //我再在主函數裏分別對p1,p2獲取他們的年齡,若爲引用傳遞則p1的年齡應該爲18,p2爲10.
    System.out.println("我在主函數裏獲取p1的年齡"+p1.getAge());
    System.out.println("我在主函數裏獲取p1的年齡"+p2.getAge());
}
public static void swap(person p1,person p2)
{
    System.out.println("************我是調用函數裏的分割線***************");
    person temp=new person();
    temp=p1;
    p1=p2;
    p2=temp;
    System.out.println("我在調用函數裏交換了p1和p2指向的地址");
    System.out.println("我在調用函數裏對p1的年齡屬性賦值爲"+p1.getAge());
    System.out.println("我在調用函數裏對p2的年齡屬性賦值爲"+p2.getAge());
    
}

}
複製代碼
結果:

看到沒,這就是充分說明Java是值傳遞的例子。在這個例子中我們依然傳遞的是person類的對象p1,p2(引用類型),他們將各自的地址複製一份到了形參p1、p2。

然後我們在調用函數中交換了他們的地址,確實在調用函數中他們的age屬性發生交換。但是再當我們在主函數獲取他們的age時,如果是引用傳遞則應該p1的age爲18,p2的age爲10,

和我們在調用函數中打印結果一致。但是,很遺憾在主函數中他們的值仍然是p1(10),p2(18)。所以這也充分印證了java是值傳遞。

那麼什麼是引用傳遞呢?我們把代碼放入C#看看。

複製代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 值傳遞or引用傳遞
{

public class person
{
    private int age;
    public int getAge()
    {
        return age;
    }
    public void setAge(int age)
    {
        this.age = age;
    }

}
class Program
{
    static void Main(string[] args)
    {
        person p1 = new person();
        person p2 = new person();
        person p3 = new person();
        p1.setAge(10);
        p2.setAge(18);
        p3.setAge(15);
        Console.WriteLine("我在主函數裏對p1的年齡屬性賦值爲" + p1.getAge());
        Console.WriteLine("我在主函數裏對p2的年齡屬性賦值爲" + p2.getAge());
        Console.WriteLine("我在主函數裏對p3的年齡屬性賦值爲" + p3.getAge());
        swap(ref p1,ref p2,p3);
        Console.WriteLine("************我是主函數裏的分割線***************");
        //我再在主函數裏分別對p1,p2獲取他們的年齡,若爲引用傳遞則p1的年齡應該爲18,p2爲10.
        Console.WriteLine("我在主函數裏獲取p1的年齡" + p1.getAge());
        Console.WriteLine("我在主函數裏獲取p2的年齡" + p2.getAge());
        Console.WriteLine("我在主函數裏獲取p3的年齡" + p3.getAge());
    }
    public static void swap(ref person p1,ref person p2, person p3)
    {
        Console.WriteLine("************我是調用函數裏的分割線***************");
        person temp = new person();
        temp = p1;
        p1 = p2;
        p2 = temp;
        p3.setAge(20);
        Console.WriteLine("我在調用函數裏交換了p1和p2指向的地址");
        Console.WriteLine("我在調用函數裏對p1交換地址後年齡爲" + p1.getAge());
        Console.WriteLine("我在調用函數裏對p2交換地址後年齡爲" + p2.getAge());
        Console.WriteLine("我在調用函數裏修改p3年齡爲" + p3.getAge());

    }
}

}
複製代碼
結果:

請注意在C#中如果我們要實現引用傳遞,請加上關鍵字ref,否則,它執行的原理仍然與我們java中執行的機制一樣,即拷貝一份地址給形參。

如果你還有點暈,不妨我們來看看下面兩張圖。

爲了方便大家理解把圖畫成這樣,然後關於java的值傳遞深度分析就到這裏。歡迎大家一起討論。(可以打臉/哈哈)
原文地址https://www.cnblogs.com/kunming97/p/10665287.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章