1. 程式人生 > >JAVA中值傳遞和引用傳遞的三種情況

JAVA中值傳遞和引用傳遞的三種情況

前言

我們都知道,java中資料型別分為基本資料型別和引用資料型別。

  • 基本資料型別
    • 整型:byte,short,int,long
    • 浮點型:float,double
    • 字元型:char
    • 布林型:boolean
  • 引用資料型別
    • 陣列
    • 介面

方法的引數分為實際引數,和形式引數。

  • 形式引數:定義方法時寫的引數。
  • 實際引數:呼叫方法時寫的具體數值。

一般情況下,在資料做為引數傳遞的時候,基本資料型別是值傳遞,引用資料型別是引用傳遞(地址傳遞)。

值傳遞

public static void main(String[] args) {
    int num1 = 10;
    int
num2 = 20; swap(num1, num2); System.out.println("num1 = " + num1); System.out.println("num2 = " + num2); } public static void swap(int a, int b) { int temp = a; a = b; b = temp; System.out.println("a = " + a); System.out.println("b = " + b); }

執行的結果是:

a = 20
b = 10
num1 = 10 num2 = 20

原因:

流程:

  1. 主函式進棧,num1、num2初始化。
  2. 呼叫swap方法,swap( )進棧,將num1和num2的,複製一份給a和b。
  3. swap方法中對a、b的值進行交換。
  4. swap方法執行完畢,a、b的值已經交換。
  5. swap方法彈棧。
  6. 主函式彈棧。

解析:

在swap方法中,a、b的值進行交換,並不會影響到num1、num2。因為,a、b中的值,只是從num1、num2的複製過來的。
也就是說,a、b相當於num1、num2的副本,副本的內容無論怎麼修改,都不會影響到原件本身。

引用傳遞

public static void
main(String[] args) { int[] arr = {1,2,3,4,5}; change(arr); System.out.println(arr[0]); } //將陣列的第一個元素變為0 public static void change(int[] array) { int len = array.length; array[0] = 0; }

執行的結果是:

0

原因:

流程:

  1. 主函式進棧,int[] arr初始化。
  2. 呼叫change方法,change( )進棧,將arr的地址值,複製一份給array。
  3. change方法中,根據地址值,找到堆中的陣列,並將第一個元素的值改為0。
  4. change方法執行完畢,陣列中第一個元素的值已經改變。
  5. change方法彈棧。
  6. 主函式彈棧。

解析:

呼叫change()的時候,形參array接收的是arr地址值的副本。並在change方法中,通過地址值,對陣列進行操作。change方法彈棧以後,陣列中的值已經改變。main方法中,打印出來的arr[0]也就從原來的1變成了0.

無論是主函式,還是change方法,操作的都是同一個地址值對應的陣列。
就像你把自己家的鑰匙給了另一個人,這個人拿著鑰匙在你家一頓瞎折騰,然後走了。等你拿著鑰匙回到家以後,家裡已經變成了被折騰過後,慘不忍睹的樣子。。
這裡的鑰匙就相當於地址值,家就相當於陣列本身。

String型別傳遞

public static void main(String[] args) {
    String str = "AAA";

    change(str);

    System.out.println(str);
}   
public static void change(String s) {
    s = "abc";
}

執行的結果是:

AAA

這就神奇了!!!
String是一個類,類是引用資料型別,做為引數傳遞的時候,應該是引用傳遞。但是從結果看起來卻是值傳遞。

原因:

String的API中有這麼一句話:“their values cannot be changed after they are created”,
意思是:String的值在建立之後不能被更改。
API中還有一段:
String str = "abc";
等效於:
char data[] = {'a', 'b', 'c'};
String str = new String(data);
也就是說:對String物件str的任何修改 等同於 重新建立一個物件,並將新的地址值賦值給str。

這樣的話,上面的程式碼就可以寫成:

public static void main(String[] args) {
    String str1 = "AAA";

    change(str1);

    System.out.println(str1);
}   
public static void change(String s) {
    char data[] = {'a', 'b', 'c'}
    String str = new String(data);
    s = str;
}

流程:

  1. 主函式進棧,str1初始化。
  2. 呼叫change方法,change( )進棧,將str1的地址值,複製一份給s。
  3. change方法中,重現建立了一個String物件”abc”,並將s指向了新的地址值。
  4. change方法執行完畢,s所指向的地址值已經改變。
  5. change方法彈棧。
  6. 主函式彈棧。

解析:

String物件做為引數傳遞時,走的依然是引用傳遞,只不過String這個類比較特殊。
String物件一旦建立,內容不可更改。每一次內容的更改都是重現創建出來的新物件
當change方法執行完畢時,s所指向的地址值已經改變。而s本來的地址值就是copy過來的副本,所以並不能改變str1的值。

String型別類似情況:

class Person {
    String name;

    public Person(String name) {
        this.name = name;
    }
}
public class Test {
    public static void main(String[] args) {
        Person p = new Person("張三");

        change(p);

        System.out.println(p.name);
    }

    public static void change(Person p) {
        Person person = new Person("李四");
        p = person; 
    }
}

執行的結果是:

張三

總結

  • 值傳遞的時候,將實參的,copy一份給形參。
  • 引用傳遞的時候,將實參的地址值,copy一份給形參。

也就是說,不管是值傳遞還是引用傳遞,形參拿到的僅僅是實參的副本,而不是實參本身。