深入理解--Java按值傳遞和按引用傳遞
引言
最近刷牛客網上的題目時碰到不少有關Java按值傳遞和按引用傳遞的問題,這種題目就是坑呀,在做錯了n次之後,查找了多方資料進行總結既可以讓自己在總結中得到提高,又可以讓其他人少走彎路。何樂而不為?
Java按值傳遞和按引用傳遞
首先問一句:Is Java “pass-by-reference” or “pass-by-value”? Java到底是按值傳遞還是按引用傳遞的呢?國外的網站上關於這個問題的討論非常之多。官方答案:The Java Spec says that everything in Java is pass-by-value. There is no such thing as “pass-by-reference” in Java.
基本資料型別的按值傳遞
java資料型別可以分為兩大類:基本型別(primitive types)和引用型別(reference types)。primitive types 包括boolean型別以及數值型別(numeric types)。numeric types又分為整型(integer types)和浮點型(floating-point type)。整型有5種:byte short int long char(char本質上是一種特殊的int)。浮點型別有float和double。關係整理一下如下圖:
例1
public class Swap {
public static void main(String[] args) {
int x = 10;
int y = 20;
swap(x, y);
System.out.println("x(2) = " + x);
System.out.println("y(2) = " + y);
}
public static void swap(int x, int y) {
int temp = x;
x = y;
y = temp;
System.out .println("x(1) = " + x);
System.out.println("y(1) = " + y);
}
}
/*輸出
x(1) = 20
y(1) = 10
x(2) = 10
y(2) = 20
*/
上面程式main函式呼叫swap函式來交換 x,y的值,然而呼叫函式之後發現main中x,y的值並未交換。包括在Java api中找不到一個可以交換兩個變數的方法。這與Java語言的特性有關。通過一個圖就可以知道上面程式的執行結果了。
由上圖可知,main函式中的x,y和swap函式中的x,y分別存放在不同的區域,在main中呼叫swap函式的時候,會將main中的x,y的值賦給swap中的x,y。當swap函式中對x,y交換時只是對swap幀中的x,y做交換,並不會改變main中的x,y。所以當函式返回時main中的x,y並不會改變。swap執行過程圖如下:
對於基本資料型別 short int long float double char byte boolean這八種按值傳遞呼叫函式並不會改變在原函式中的值。
引用資料型別的按值傳遞
引用資料資料型別分為三種:①介面 ②類 ③陣列對於引用資料型別的按值傳遞先給出一個例項對比例項進行分析。
例2
public static void main(String[] args) {
int []a={10,20};
System.out.println("a[0] :"a[0]+"a[1] : "+a[1]);//a[0]=10,a[1]=20;
swap(a, 0, 1);
System.out.println("a[0] :"a[0]+"a[1] : "+a[1]);//a[0]=20,a[1]=10;
}
public static void swap(int []a,int i,int j){
int temp=a[i];
a[i]=a[j];
a[j]=temp;
System.out.println("a[0] :"a[0]+"a[1] : "+a[1]);//a[0]=20,a[1]=10;
}
//輸出
/*a[0]=10 a[1]=20
a[0]=20 a[1]=10
a[0]=20 a[1]=10
*/
執行程式後發現,swap函式對a[0] ,a[1]的操作竟然影響到了main函式中的a[0] ,a[1]的值,真是不可思議。為什麼會產生如此之結果。原來引用型別的按值傳遞,傳遞的是物件的地址。還是用圖來解釋一下。
由圖可以看出在swap中僅僅是得到了陣列的地址,並沒有對陣列的元素進行復制,在swap中對陣列的操作是直接對main函式中陣列的操作,因此swap函式返回後main函式中的a[0] ,a[1]的值發生交換。
陣列、類、介面按值傳遞的時候都是傳遞物件的地址。再來看一個比較複雜的例項。
例3
public class Main{
public static void main(String[] args){
Foo f = new Foo("f");
changeReference(f); // It won't change the reference!
modifyReference(f); // It will modify the object that the reference variable "f" refers to!
}
public static void changeReference(Foo a){
Foo b = new Foo("b");
a = b;
}
public static void modifyReference(Foo c){
c.setAttribute("c");
}
}
①Foo f = new Foo(“f”);
②public static void changeReference(Foo a)
③changeReference(f);
④Foo b = new Foo(“b”);
⑤a = b
⑥c.setAttribute(“c”);
經典筆試題目
試題1
public class Demo {
public static void main(String[] args) {
//demo1
String str=new String("hello");
char []chs={'w','o','r','l','d'};
change(str, chs);
System.out.println(str+" "+new String(chs));
//-------------------------------------------------
//demo2
StringBuffer sb=new StringBuffer("hello");
change(sb);
System.out.println(sb);
}
public static void change(StringBuffer sb)
{
sb.append(" world");
// sb.deleteCharAt(0);
}
public static void change(String str,char[]chs)
{
str.replace('h', 'H');
chs[0]='W';
}
}
上面程式段demo1和demo2分別輸出什麼,這裡涉及到String特性,這道題目會做了,Java按值傳遞和按引用傳遞就徹底懂了,答案和解析先匿了。
試題2
public class foo {
public static void main(String sgf[]) {
StringBuffer a=new StringBuffer(“A”);
StringBuffer b=new StringBuffer(“B”);
operate(a,b);
System.out.println(a+”.”+b);
}
static void operate(StringBuffer x,StringBuffer y) {
x.append(y);
y=x;
}
}