幾個月前
developerWorks 發布了我的書 Practical Java 中的一些節選
該書是由 Addison
Wesley 出版的
首先我將利用 developerWorks 上的此欄目回答讀者提出的一些問題
然後對有關這些節選的各種評論作一答復
節選理解參數是按值而不是按引用傳遞的說明 Java 應用程序有且僅有的一種參數傳遞機制
即按值傳遞
寫它是為了揭穿普遍存在的一種神話
即認為 Java 應用程序按引用傳遞參數
以避免因依賴
按引用傳遞
這一行為而導致的常見編程錯誤
對此節選的某些反饋意見認為
我把這一問題搞糊塗了
或者將它完全搞錯了
許多不同意我的讀者用 C++ 語言作為例子
因此
在此欄目中我將使用 C++ 和 Java 應用程序進一步闡明一些事實
要點
讀完所有的評論以後
問題終於明白了
至少在一個主要問題上產生了混淆
某些評論認為我的節選是錯的
因為對象是按引用傳遞的
對象確實是按引用傳遞的
節選與這沒有沖突
節選中說所有參數都是按值
另一個參數
傳遞的
下面的說法是正確的
在 Java 應用程序中永遠不會傳遞對象
而只傳遞對象引用
因此是按引用傳遞對象
但重要的是要區分參數是如何傳遞的
這才是該節選的意圖
Java 應用程序按引用傳遞對象這一事實並不意味著 Java 應用程序按引用傳遞參數
參數可以是對象引用
而 Java 應用程序是按值傳遞對象引用的
C++ 和 Java 應用程序中的參數傳遞
Java 應用程序中的變量可以為以下兩種類型之一
引用類型或基本類型
當作為參數傳遞給一個方法時
處理這兩種類型的方式是相同的
兩種類型都是按值傳遞的
沒有一種按引用傳遞
這是一個重要特性
正如隨後的代碼示例所示的那樣
在繼續討論之前
定義按值傳遞和按引用傳遞這兩個術語是重要的
按值傳遞意味著當將一個參數傳遞給一個函數時
函數接收的是原始值的一個副本
因此
如果函數修改了該參數
僅改變副本
而原始值保持不變
按引用傳遞意味著當將一個參數傳遞給一個函數時
函數接收的是原始值的內存地址
而不是值的副本
因此
如果函數修改了該參數
調用代碼中的原始值也隨之改變
關於 Java 應用程序中參數傳遞的某些混淆源於這樣一個事實
許多程序員都是從 C++ 編程轉向 Java 編程的
C++ 既包含非引用類型
又包含引用類型
並分別按值和按引用傳遞它們
Java 編程語言有基本類型和對象引用
因此
認為 Java 應用程序像 C++ 那樣對基本類型使用按值傳遞
而對引用使用按引用傳遞是符合邏輯的
畢竟您會這麼想
如果正在傳遞一個引用
則它一定是按引用傳遞的
很容易就會相信這一點
實際上有一段時間我也相信是這樣
但這不正確
在 C++ 和 Java 應用程序中
當傳遞給函數的參數不是引用時
傳遞的都是該值的一個副本(按值傳遞)
區別在於引用
在 C++ 中當傳遞給函數的參數是引用時
您傳遞的就是這個引用
或者內存地址(按引用傳遞)
在 Java 應用程序中
當對象引用是傳遞給方法的一個參數時
您傳遞的是該引用的一個副本(按值傳遞)
而不是引用本身
請注意
調用方法的對象引用和副本都指向同一個對象
這是一個重要區別
Java 應用程序在傳遞不同類型的參數時
其作法與 C++ 並無不同
Java 應用程序按值傳遞所有參數
這樣就制作所有參數的副本
而不管它們的類型
示例
我們將使用前面的定義和討論分析一些示例
首先考慮一段 C++ 代碼
C++ 語言同時使用按值傳遞和按引用傳遞的參數傳遞機制
清單
C++ 示例 #include
#include
void modify(int a
int *P
int &r);
int main (int argc
char** argv)
{
int val
ref;
int *pint;
val =
;
ref =
;
pint = (int*)malloc(sizeof(int));
*pint =
;
printf(
val is %d\n
val);
printf(
pint is %d\n
pint);
printf(
*pint is %d\n
*pint);
printf(
ref is %d\n\n
ref);
printf(
calling modify\n
);
//按值傳遞 val 和 pint
按引用傳遞 ref
modify(val
pint
ref);
printf(
returned from modify\n\n
);
printf(
val is %d\n
val);
printf(
pint is %d\n
pint);
printf(
*pint is %d\n
*pint);
printf(
ref is %d\n
ref);
return
;
}
void modify(int a
int *p
int &r)
{
printf(
in modify
\n
);
a =
;
*p =
;
p =
;
r =
;
printf(
a is %d\n
a);
printf(
p is %d\n
p);
printf(
r is %d\n
r);
}
這段代碼的輸出為
清單
C++ 代碼的輸出 val is
pint is
*pint is
ref is
calling modify
in modify
a is
p is
r is
returned from modify
val is
pint is
*pint is
ref is
這段代碼聲明了三個變量
兩個整型變量和一個指針變量
設置了每個變量的初始值並將其打印出來
同時打印出了指針值及其所指向的值
然後將所有三個變量作為參數傳遞給 modify 函數
前兩個參數是按值傳遞的
最後一個參數是按引用傳遞的
modify 函數的函數原型表明最後一個參數要作為引用傳遞
回想一下
C++ 按值傳遞所有參數
引用除外
後者是按引用傳遞的
modify 函數更改了所有三個參數的值
將第一個參數設置為
將第二個參數所指向的值設置為
然後將第二個參數設置為
將第三個參數設置為
將新值打印出來
然後函數返回
當執行返回到 main 時
再次打印出這三個參數的值以及指針所指向的值
作為第一個和第二個參數傳遞的變量不受 modify 函數的影響
因為它們是按值傳遞的
但指針所指向的值改變了
請注意
與前兩個參數不同
作為最後一個參數傳遞的變量被 modify 函數改變了
因為它是按引用傳遞的
現在考慮用 Java 語言編寫的類似代碼
清單
Java 應用程序 class Test
{
public static void main(String args[])
{
int val;
StringBuffer sb
sb
;
val =
;
sb
= new StringBuffer(
apples
);
sb
= new StringBuffer(
pears
);
System
out
println(
val is
+ val);
System
out
println(
sb
is
+ sb
);
System
out
println(
sb
is
+ sb
);
System
out
println(
);
System
out
println(
calling modify
);
//按值傳遞所有參數
modify(val
sb
sb
);
System
out
println(
returned from modify
);
System
out
println(
);
System
out
println(
val is
+ val);
System
out
println(
sb
is
+ sb
);
System
out
println(
sb
is
+ sb
);
}
public static void modify(int a
StringBuffer r
StringBuffer r
)
{
System
out
println(
in modify
);
a =
;
r
= null; //
r
append(
taste good
);
System
out
println(
a is
+ a);
System
out
println(
r
is
+ r
);
System
out
println(
r
is
+ r
);
}
}
這段代碼的輸出為
清單
Java 應用程序的輸出 val is
sb
is apples
sb
is pears
calling modify
in modify
a is
r
is null
r
is pears taste good
returned from modify
val is
sb
is apples
sb
is pears taste good
From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19648.html