java中的值傳遞和引用傳遞

Java中沒有指針,所以也沒有引用傳遞了,僅僅有值傳遞 不過可以通過對象的方式來實現引用傳遞 類似java沒有多繼承 但可以用多次implements 接口實現多繼承的功能
值傳遞:方法調用時,實際參數把它的值傳遞給對應的形式參數,方法執行中形式參數值的改變不影響實際參 

數的值。
引用傳遞:也稱爲傳地址。方法調用時,實際參數的引用(地址,而不是參數的值)被傳遞給方法中相對應的形式參數,在方法執行中,對形式參數的操作實際上就是對實際參數的操作,方法執行中形式參數值的改變將會影響實際參數的值。

Java參數按值傳遞

面試題:當一個對象被當作參數傳遞到一個方法後,此方法可改變這個對象的屬性,並可返回變化後的結果,那麼這裏到底是值傳遞還是引用傳遞?
    答:是值傳遞。Java 編程語言只有值傳遞參數。當一個對象實例作爲一個參數被傳遞到方法中時,參數的值就是該對象的引用一個副本。指向同一個對象,對象的內容可以在被調用的方法中改變,但對象的引用(不是引用的副本)是永遠不會改變的。

-------------------------------------------------------------

在 Java 應用程序中永遠不會傳遞對象,而只傳遞對象引用。因此是按引用傳遞對象。但重要的是要區分參數是如何傳遞的,這纔是該節選的意圖。Java 應用程序按引用傳遞對象這一事實並不意味着 Java 應用程序按引用傳遞參數。參數可以是對象引用,而 Java 應用程序是按值傳遞對象引用的。
  
Java 應用程序中的變量可以爲以下兩種類型之一:引用類型或基本類型。當作爲參數傳遞給一個方法時,處理這兩種類型的方式是相同的。兩種類型都是按值傳遞的;沒有一種按引用傳遞。
   
按 值傳遞意味着當將一個參數傳遞給一個函數時,函數接收的是原始值的一個副本。因此,如果函數修改了該參數,僅改變副本,而原始值保持不變。按引用傳遞意味 着當將一個參數傳遞給一個函數時,函數接收的是原始值的內存地址,而不是值的副本。因此,如果函數修改了該參數的值,調用代碼中的原始值也隨之改變。如果 函數修改了該參數的地址,調用代碼中的原始值不會改變.
   
當傳遞給函數的參數不是引用時,傳遞的都是該值的一個副本(按值傳遞)。區別在於引用。在 C++ 中當傳遞給函數的參數是引用時,您傳遞的就是這個引用,或者內存地址(按引用傳遞)。在 Java 應用程序中,當對象引用是傳遞給方法的一個參數時,您傳遞的是該引用的一個副本(按值傳遞),而不是引用本身。
   
Java 應用程序按值傳遞參數(引用類型或基本類型),其實都是傳遞他們的一份拷貝.而不是數據本身.(不是像 C++ 中那樣對原始值進行操作。)



例1:

Java代碼 複製代碼
  1. //在函數中傳遞基本數據類型,   
  2. public   class  Test {   
  3.             
  4.      public   static   void  change( int  i,  int  j) {   
  5.          int  temp = i;   
  6.         i = j;   
  7.         j = temp;   
  8.     }   
  9.   
  10.      public   static   void  main(String[] args) {   
  11.          int  a =  3 ;   
  12.          int  b =  4 ;   
  13.         change(a, b);   
  14.   
  15.         System.out.println( "a="  + a);   
  16.         System.out.println( "b="  + b);   
  17.     }   
  18. }   
  19.   
  20. 結果爲:   
  21. a= 3   
  22. b= 4   
  23. 原因就是 參數中傳遞的是 基本類型 a 和 b 的拷貝,在函數中交換的也是那份拷貝的值 而不是數據本身;  

 

例2:

Java代碼 複製代碼
  1. //傳的是引用數據類型   
  2. public   class  Test {   
  3.   
  4.      public   static   void  change( int [] counts) {   
  5.         counts[ 0 ] =  6 ;   
  6.         System.out.println(counts[ 0 ]);   
  7.     }   
  8.   
  9.      public   static   void  main(String[] args) {   
  10.          int [] count = {  1 2 3 4 5  };   
  11.         change(count);   
  12.     }   
  13. }   
  14.   
  15. 在方法中 傳遞引用數據類型 int 數組,實際上傳遞的是其引用count的拷貝,他們都指向數組對象,在方法中可以改變數組對象的內容。即:對複製的引用所調用的方法更改的是同一個對象。  


例3:
Java代碼 複製代碼
  1. //對象的引用(不是引用的副本)是永遠不會改變的   
  2. class  A {   
  3.      int  i =  0 ;   
  4. }   
  5.   
  6.   
  7. public   class  Test {   
  8.   
  9.      public   static   void  add(A a) {   
  10.         a =  new  A();   
  11.         a.i++;   
  12.     }   
  13.        
  14.      public   static   void  main(String args[]) {   
  15.         A a =  new  A();   
  16.         add(a);   
  17.         System.out.println(a.i);   
  18.     }   
  19. }   
  20.   
  21. 輸出結果是 0   
  22. 在該程序中,對象的引用指向的是A ,而在change方法中,傳遞的引用的一份副本則指向了一個新的OBJECT,並對其進行操作。   
  23. 而原來的A對象並沒有發生任何變化。 引用指向的是還是原來的A對象。  




例4:
String 不改變,數組改變

Java代碼 複製代碼
  1.   
  2. public   class  Example {   
  3.     String str =  new  String( "good" );   
  4.   
  5.      char [] ch = {  'a' 'b' 'c'  };   
  6.   
  7.      public   static   void  main(String args[]) {   
  8.         Example ex =  new  Example();   
  9.         ex.change(ex.str, ex.ch);   
  10.         System.out.print(ex.str +  " and " );   
  11.         System.out.println(ex.ch);   
  12.     }   
  13.   
  14.      public   void  change(String str,  char  ch[]) {   
  15.         str =  "test ok" ;   
  16.         ch[ 0 ] =  'g' ;   
  17.     }   
  18. }    
  19. 程序 3 輸出的是 good and gbc.   
  20. String 比較特別,看過String 代碼的都知道, String 是  final 的。所以值是不變的。 函數中String對象引用的副本指向了另外一個新String對象,而數組對象引用的副本沒有改變,而是改變對象中數據的內容.   
  21. 對於對象類型,也就是Object的子類,如果你在方法中修改了它的成員的值,那個修改是生效的,方法調用結束後,它的成員是新的值,但是如果你把它指向一個其它的對象,方法調用結束後,原來對它的引用並沒用指向新的對象。 
Java參數,不管是原始類型還是引用類型,傳遞的都是副本(有另外一種說法是傳值,但是說傳副本更好理解吧,傳值通常是相對傳址而言)。
    如果參數類型是原始類型,那麼傳過來的就是這個參數的一個副本,也就是這個原始參數的值,這個跟之前所談的傳值是一樣的。如果在函數中改變了副本的 值不會改變原始的值.
    如果參數類型是引用類型,那麼傳過來的就是這個引用參數的副本,這個副本存放的是參數的地址。如果在函數中沒有改變這個副本的地址,而是改變了地址中的 值,那麼在函數內的改變會影響到傳入的參數。如果在函數中改變了副本的地址,如new一個,那麼副本就指向了一個新的地址,此時傳入的參數還是指向原來的 地址,所以不會改變參數的值。
 ( 對象包括對象引用即地址和對象的內容)
 
a.傳遞值的數據類型:八種基本數據類型和String(這樣理解可以,但是事實上String也是傳遞的地址,只是string對象和其他對 象是不同的,string對象是不能被改變的,內容改變就會產生新對象。那麼StringBuffer就可以了,但只是改變其內容。不能改變外部變量所指 向的內存地址)。
b.傳遞地址值的數據類型:除String以外的所有複合數據類型,包括數組、類和接口

下面舉例說明:
 Java  應用程序中永遠不會傳遞對象,而只傳遞對象引用。因此是按引用傳遞對象。但重要的是要區分參數是如何傳遞的,這纔是該節選的意圖。 Java  應用程序按引用傳遞對象這一事實並不意味着  Java  應用程序按引用傳遞參數。參數可以是對象引用,而  Java  應用程序是按值傳遞對象引用的。
Java  應用程序中的變量可以爲以下兩種類型之一:引用類型或基本類型。當作爲參數傳遞給一個方法時,處理這兩種類型的方式是相同的。兩種類型都是按值傳遞的;沒有一種按引用傳遞。
按 值傳遞意味着當將一個參數傳遞給一個函數時,函數接收的是原始值的一個副本。因此,如果函數修改了該參數,僅改變副本,而原始值保持不變。按引用傳遞意味 着當將一個參數傳遞給一個函數時,函數接收的是原始值的內存地址,而不是值的副本。因此,如果函數修改了該參數,調用代碼中的原始值也隨之改變。
當傳遞給函數的參數不是引用時,傳遞的都是該值的一個副本(按值傳遞)。區別在於引用。在  C++  中當傳遞給函數的參數是引用時,您傳遞的就是這個引用,或者內存地址(按引用傳遞)。  Java  應用程序中,當對象引用是傳遞給方法的一個參數時,您傳遞的是該引用的一個副本(按值傳遞),而不是引用本身。
Java  應用程序按值傳遞所有參數,這樣就製作所有參數的副本,而不管它們的類型。
  1. class  Test
  2. {
  3.   public  static  void  main(String  args[])
  4.   {
  5.     int  val;
  6.     StringBuffer  sb1, sb2;
  7.  
  8.     val = 10;
  9.     sb1 = new  StringBuffer ("apples" );
  10.     sb2 = new  StringBuffer ("pears" );
  11.     System .out.println("val is "  + val);
  12.     System .out.println("sb1 is "  + sb1);
  13.     System .out.println("sb2 is "  + sb2);
  14.     System .out.println("" );
  15.  
  16.     System .out.println("calling modify" );
  17.     // 按值傳遞所有參數
  18.     modify(val, sb1, sb2);
  19.     System .out.println("returned from modify" );
  20.     System .out.println("" );
  21.  
  22.     System .out.println("val is "  + val);
  23.     System .out.println("sb1 is "  + sb1);
  24.     System .out.println("sb2 is "  + sb2);
  25.   }
  26.  
  27.   public  static  void  modify(int  a, StringBuffer  r1,
  28.                             StringBuffer  r2)
  29.   {
  30.       System .out.println("in modify..." );
  31.       a = 0;
  32.       r1 = null ;  //1
  33.       r2.append(" taste good" );
  34.       System .out.println("a is "  + a);
  35.       System .out.println("r1 is "  + r1);
  36.       System .out.println("r2 is "  + r2);
  37.   }
  38. }
  39.  
Java  應用程序的輸出
  1.  
  2. val is 10
  3. sb1 is apples
  4. sb2 is pears
  5.  
  6. calling modify
  7. in modify...
  8. a is 0
  9. r1 is null
  10. r2 is pears taste good
  11. returned from modify
  12.  
  13. val is 10
  14. sb1 is apples
  15. sb2 is pears taste good
  16.  


這段代碼聲明瞭三個變量:一個整型變量和兩個對象引用。設置了每個變量的初始值並將它們打印出來。然後將所有三個變量作爲參數傳遞給  modify  方法。

modify 
方法更改了所有三個參數的值:  

將第一個參數(整數)設置爲  0  
將第一個對象引用  r1  設置爲  null  
保留第二個引用  r2  的值,但通過調用  append  方法更改它所引用的對象(這與前面的  C++  示例中對指針  p  的處理類似)。

當執行返回到  main  時,再次打印出這三個參數的值。正如預期的那樣,整型的  val  沒有改變。對象引用  sb1  也沒有改變。如果  sb1  是按引用傳遞的,正如許多人聲稱的那樣,它將爲  null 。但是,因爲  Java  編程語言按值傳遞所有參數,所以是將  sb1  的引用的一個副本傳遞給了  modify  方法。  modify  方法在  //1  位置將  r1  設置爲  null  時,它只是對  sb1  的引用的一個副本進行了該操作,而不是像  C++  中那樣對原始值進行操作。

另外請注意,第二個對象引用  sb2  打印出的是在  modify  方法中設置的新字符串。即使  modify  中的變量  r2  只是引用  sb2  的一個副本,但它們指向同一個對象。因此,對複製的引用所調用的方法更改的是同一個對象。
傳值---傳遞基本數據類型參數
public    class           PassValue{
    static void exchange(int a, int b){//靜態方法,交換a,b的值
        int temp;
        temp = a;
        a = b;
        b = temp;
    }
    public static void main(String[] args){
       int i = 10;
       int j = 100;
       System.out.println("before call: " + "i=" + i + "/t" + "j = " + j);//調用前
        exchange(i, j);                                                                    //值傳遞,main方法只能調用靜態方法
        System.out.println("after call: " + "i=" + i + "/t" + "j = " + j);//調用後
    }
}
運行結果:
        before call: i = 10        j = 100
        after    call: i = 10        j = 100
說明:調用exchange(i, j)時,實際參數i,j分別把值傳遞給相應的形式參數a,b,在執行方法exchange()時,形式參數a,b的值的改變不影響實際參數i和j的值,i和j的值在調用前後並沒改變。
引用傳遞---對象作爲參數
如果在方法中把對象(或數組)作爲參數,方法調用時,參數傳遞的是對象的引用(地址),即在方法調用時,實際參數把對對象的 引用(地址)傳遞給形式參數。這是實際參數與形式參數指向同一個地址,即同一個對象(數組),方法執行時,對形式參數的改變實際上就是對實際參數的改變, 這個結果在調用結束後被保留了下來。
class Book{
    String name;
    private folat price;
    Book(String n,    float ){                //構造方法
        name = n;
        price = p;
    }
    static  void  change(Book a_book,    String n,    float p){    //靜態方法,對象作爲參數
            a_book.name = n;
            a_book.price = p;
    }
    public void output(){        //實例方法,輸出對象信息
        System.out.println("name: " + name + "/t" + "price: " + price);
    }
}
 public class PassAddr{
    public static void main(String [] args){
        Book b = new Book("java2",    32.5f);
        System.out.print("before call:/t");        //調用前
        b.output();
        b.change(b,    "c++",    45.5f);            //引用傳遞,傳遞對象b的引用,修改對象b的值
        System.out.print("after call:/t");            //調用後
        b.output();
    }
}
運行結果:
        before    call:    name:java2        price:32.5
        after       call:    name:c++          price:45.5
說明:調用change(b,"c++",45.5f)時,對象b作爲實際參數,把引用傳遞給相應的形式參數a_book,實際上a_book也指向同一 個對象,即該對象有兩個引用名:b和a_book。在執行方法change()時,對形式參數a_book操作就是對實際參數b的操作。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章