java方法中,傳參是傳值還是傳址問題,java

來源:互聯網
上載者:User

java方法中,傳參是傳值還是傳址問題,java

問題引出:

 

編寫一個簡單的交換值的小程式,如果我們只是簡單地定義一個交換函數接收兩個數,在函數內部定義一個中間變數完成交換。那麼當我們把a,b兩個實參傳給這個函數時,往往得不到預期的結果。這是為什麼呢?

在C語言和C#中:

 

在C語言中,如果我們運行下列代碼:

#include<stdio.h>  void swap(int,int);    void main(){      int a=5;      int b=7;     printf("a=%d b=%d\n",a,b);//a=5 b=7     swap(a,b);     printf("a=%d b=%d\n",a,b);//a=5 b=7    }    void swap(int c,int d){     int temp;     temp=c;     c=d;     d=temp;    }

以及在C#中運行下列代碼並試著傳進去a,b:

 static void swap(int c, int d)  //這個函數將以傳值的方式運行      {    int temp;    temp=c;    c=d;    d=temp;     }  

得到的結果都不是我們預期的那樣實現a和b的數值交換。

這就是因為在C和C#中存在傳值和傳址兩個不同的概念。

以上代碼實現的是“傳值”的概念。也就是說,在程式運行時,實參將自己的值複製給形參,而形參也是分配記憶體空間的。因此在這個值傳遞的過程過後,實參和形參實際上就指向了兩個不同的儲存空間,不存在關聯關係了。在我們所謂的執行交換值的函數中,交換的實際上是形參而已(不是a,b而是c,d),並且形參作為局部變數的生命週期也就是只在函數內部,函數運行結束,形參也就不複存在了。故代碼達不到我們交換值的目的。

對於這個問題的解決,C語言和C#分別提供的解決方案之一就是址傳遞

在C語言中,我們可以很簡單地利用別名就可以完成兩個數的交換:

#include<stdio.h>  void swap(int &,int &);    void main(){      int a=5;      int b=7;     printf("a=%d b=%d\n",a,b);//a=5 b=7     swap(a,b);        printf("a=%d b=%d\n",a,b);//a=7 b=5    }         void swap(int &a,int &b){ //此處利用取地址符,傳入地址 
int temp; temp=a; a=b; b=temp; }

 

在C#中,提供了專門的關鍵字ref可以表示是傳值還是傳地址。

static void swap(ref int i, ref int t) // 這個函數將會以傳址的方式進行運算.         {    int temp;      temp=a;      a=b;      b=temp;   }

以上兩段代碼實現的就是傳址的方式。可以實現我們的預期代碼實現功能。

在C語言中還可以使用操作指標的方式(但是屬於值傳遞)達到預期

#include<stdio.h>  void swap(int *,int *);    void main(){      inta=5;      intb=7;     printf("a=%d b=%d\n",a,b);//a=5 b=7     swap(&a,&b);//此處傳出a,b的地址,利用取地址符          printf("a=%d b=%d\n",a,b);//a=7 b=5    }         void swap(int *a,int *b){//傳入地址,就可以完成兩個說     inttemp;     temp=*a;     *a=*b;     *b=temp;    } 

 

無論是C語言中的別名,還是C#中的關鍵字ref。傳遞的都是實參的地址,也就是說程式運行時,實參將自己的地址傳遞給函數。這時的實參和形參所指向的地址是一致的,也就是說是一個記憶體空間上的對象,那麼在函數運行期間對形參的操作也就順理成章的改變了實參的值。

那麼對於java呢?

 

先貼個例子:

package com.test;public class Test1 {            public static void Reverse(int R[],int l,int r){        int i,j;        int temp;        for(i=l,j=r;i<j;++i,--j){            temp=R[i];            R[i]=R[j];            R[j]=temp;        }    }    public static void Add(int a,int b){        a=a+100;        b=b+1000;    }            public static void main(String[] args) {        int[] array=new int[]{1,2,3,4,5,6};        Reverse(array,0,5);        for(int i=0;i<array.length;++i)        System.out.print(array[i]);
     System.out.println(); System.out.println("——————————————"); int i=1,j=2; Add(i,j); System.out.println(i+"####"+j); }}

輸出結果:

654321——————————————1####2

由於Java中程式員無法直接操作指標,對於傳值還是傳地址很模糊,但我們知道java中變數分為兩類,一類是基本變數,一類是引用。(數組屬於引用)

其實當我們把實參a,b傳進方法後,就相當於把a,b的值分別傳給了方法用於接收a,b的局部變數,那麼不管是傳進去引用還是值,實參a,b的值都不會被改變,因為操作的時函數中接收a,b傳遞的值的局部變數,函數執行完畢,這兩個局部變數也就不存在了。

那為什麼傳進去引用可以修改a,b的值呢,這是不違反上面說的原理了嗎?

其實,Java中無論傳值還是傳引用都是傳的參數的副本,即將實參進行複製。副本只在函數內部有效。而當傳引用時,傳進去的是實參自己的地址的複製版,地址被複製了,但是指向的值的指向沒變(即對於“地址”這個東西有實參和形參不指向同一儲存空間這回事,但是對於“地址指向的值”只有一個),同時地址無法被改變但是地址指向的值可以被改變。所以可以說java中都是在傳值。

在C++中:

 

在C++中,允許操作指標。也存在著明確的值傳遞和址傳遞。(其實下面兩條在概念上對於所有這幾種語言來說是普適的)

值傳遞(按值傳遞):  1.實參值傳遞給相應形參 2.實參地址傳遞給相應形參 比如:數組、指標。(類似Java)

址傳遞(引用傳遞):   使用別名,共用儲存空間(直接存取) 形參為引用參數時,才為按址傳遞,此時對應實參一般為一個變數。

強調:除了使用別名外,即使是使用指標也是值傳遞。即在函數的形參列表中,只要存在形參儲存空間的開闢就屬於值傳遞。

貼兩個例子:

例1:

#include<iostream>using namespace std;void Reverse(int R[],int l,int r){    int i,j;    int temp;    for(i=l,j=r;i<j;++i,--j){    temp=R[i];    R[i]=R[j];    R[j]=temp;    }}void Add(int c,int d){c=c+100;d=d+1000;}int main(){    int array[]={1,2,3,4,5,6};    Reverse(array,0,5);    for(int i=0;i<6;++i){    cout<<array[i];    }    cout<<endl;    int a=1,b=2;    Add(a,b);    cout<<a<<","<<b;    cout<<endl;    return 0;}

運行結果:

例2(代碼來自於參考部落格):

#include<iostream>  using namespace std;  void mySwap(int *p1,int *p2);  int main(){        int a=12;      int b=44;        int *pa=&a;      int *pb=&b;          if(a<b){                mySwap1(a,b);          //mySwap2(pa,pb);          //mySwap3(pa,pb);          //mySwap4(a,b);      }          return 0;  }    /*  int類型作為形參--值傳遞:形參a ,b 也要分配記憶體空間,實參的值複製給形參  */  void mySwap1(int a,int b){      int temp;      temp=a;      a=b;      b=temp;  }  /*  int * 類型作為形參--值傳遞:形參p1,p2 也要分配記憶體空間,實參的值複製給形參(地址)  */  void mySwap2(int *p1,int *p2){        //改變形參指標的指向,不會影響到實參      int *temp;      temp=p1;      p1=p2;      p2=temp;    }  /*  int * 類型作為形參--值傳遞:形參p1,p2 也要分配記憶體空間,實參的值複製給形參(地址)  */  void mySwap3(int *p1,int *p2){        //改變形參指標指向的記憶體空間值,也就改變了實參指標指向的記憶體空間值      int temp2;      temp2=*p1;      *p1=*p2;      *p2=temp2;  }    /*  參考型別作為形參--址傳遞:形參a,b是不分配記憶體空間的,形參是實參的“別名”  */  void mySwap4(int &a,int &b){        int temp;      temp=a;      a=b;      b=temp;  

 

總結一下:

 

C語言、C#、C++中有明確的傳值和傳址方式。程式員可以主動區分。

在C語言和C++中,除了使用別名外,其他都屬於值傳遞。

在Java中,由於不能直接操作指標,只存在值傳遞,但是當傳遞的值是引用時,那麼方法中得到的是引用的地址的複製版,地址被複製了,但是指向的值的指向沒變(即對於“地址”這個東西有實參和形參不指向同一儲存空間這回事,但是對於“地址指向的值”只有一個),同時地址無法被改變但是地址指向的值可以被改變。所以可以說java中都是在傳值。

那麼,回到最初的問題上面來,我們要怎麼做才能實現兩個數值的交換呢?

 

小編的答案是操作地址,無論是值傳遞還是址傳遞,只要操作的是地址,那麼形參和實參指向的就是同一個儲存空間,也就是說形參的改變可以帶來實參的改變。

那麼,請各位想一想怎樣實現Java中基本變數的數值交換呢?小編還沒想好,歡迎大家賜教。

參考部落格:

http://blog.csdn.net/single0515/article/details/51584536

http://blog.csdn.net/cxc19890214/article/details/44617685

http://blog.csdn.net/u010126792/article/details/63771475

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.