Java中是引用傳遞還是值傳遞?
前言
在學習Java程式語言過程中最容易讓你產生誤解的問題之一就是 java是值傳遞還是引用傳遞。今天就來圍繞這個話題揭開迷霧。
概念
首先先來認識一下什麼是值傳遞什麼是引用傳遞。
值傳遞: 將方法實際引數值複製到另一個變數,然後複製的物件被傳遞,這就是為什麼它被稱為“值傳遞”
引用傳遞:將實際引數的引用傳遞給該方法,這就是為什麼它被引用稱為“傳遞”的原因。
例子分析1
問題:如果java是使用引用傳遞的話,為什麼在函式中 變數的交換會沒有卵用呢? 答案:java通過引用來操作物件,並且所有Object型別的變數都是引用一個地址。但是,java傳遞方法引數並不是引用傳遞,而是值傳遞。
來個例子,大家感受一下:
public void badSwap(int var1, int var2)
{
int temp = var1;
var1 = var2;
var2 = temp;
}
當badSwap()方法結束,原先作為實際引數傳遞進來的變數仍然是它們原來的值,也就是這個方法然無卵用。如果把這個方法的引數由int改變成Object類,結果依然一樣。因為java是通過值傳遞來傳遞物件引用的。這麼說可能不太清晰,再來一個例子。
public void tricky(Point arg1, Point arg2)
{
arg1.x = 100;
arg1.y = 100;
Point temp = arg1;
arg1 = arg2;
arg2 = temp;
}
public static void main(String [] args)
{
Point pnt1 = new Point(0,0);
Point pnt2 = new Point(0,0);
System.out.println("X: " + pnt1.x + " Y: " +pnt1.y);
System.out.println("X: " + pnt2.x + " Y: " +pnt2.y);
System.out.println(" ");
tricky(pnt1,pnt2);
System.out.println("X: " + pnt1.x + " Y:" + pnt1.y);
System.out.println("X: " + pnt2.x + " Y: " +pnt2.y);
}
構造了兩個物件pnt1和pnt2,並且初始的x,y值都是0。之後傳入tricky()方
法,在方法中修改行參args1的x,y的值,之後交換行參的指向。
執行main方法 ,輸入如下
X: 0 Y: 0
X: 0 Y: 0
X: 100 Y: 100
X: 0 Y: 0
根據輸出結果,可以發現實際引數pnt1的值被修改了,pnt2的屬性沒有變,也就是pnt1盒pnt2的交換失敗了!這裡最容易被疑惑。
pnt1和pnt2肯定是物件引用,當把pnt1和pnt2傳入到tricky()方法當中,
由於java是值傳遞,也就類似於複製,相當於複製和一個和pnt1一樣的行參變數arg1,它也擁有了一個和pnt1變數同一指向的引用。圖一展示了通過值傳遞之後兩個引用指向同一個地址。
圖1。方法中被傳入物件引用引數之後,一個物件至少會有兩個引用
上面的例子由於兩個變數的引用都指向了同一個物件,所以在方法中修改物件的值會生效。但是由於arg1是copy的一個變數,所以交換的話只是交換了arg1的指向,這就是為什麼交換會失敗的原因。
圖2展示了僅僅是方法引數裡的引用交換了,而並不是原始的引數交換。
如果需要成功交換pnt1和pnt2的引用,只能在外部直接修改他們的引用即可。
圖2:java通過值傳遞,copy了一個和pnt1一樣的變數,他們擁有同樣的引用。在方法呼叫之後,僅僅是交換了arg1和arg2的引用。
在國內可能有大部分的人清楚傳遞的規律,但是他們依然習慣是基本型別是值傳遞,引用型別變數就是引用傳遞,因為方法中的引數的確有了外部變數的引用。這個看個人理解。我更偏向於是值傳遞:通過值傳遞之後方法裡的引數擁有了和實際引數一樣的值(基礎型別為值,物件型別為引用),所以才擁有了引用。而如果是引用傳遞的話,那就是是直接傳遞一個存放於堆區的物件給(也就是直是複製了一個物件)。當然這只是我個人的認識。
例子分析2
public class Test {
private static int a;
private int b;
public static void main(String[] args) {
System.out.println(a);
modify(a);
System.out.println(a);
return;
}
private static void modify(int a) {
a++;
}
}
知道了java是值傳遞的,結果很清楚了
輸出a的值肯定不變。
java總是通過值傳遞而不是引用傳遞,再來一個例子
例子分析3
public class Balloon {
private String color;
public Balloon(){}
public Balloon(String c){
this.color=c;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
Balloon類擁有一個color屬性。
public class Test {
public static void main(String[] args) {
Balloon red = new Balloon("Red"); //memory reference 50
Balloon blue = new Balloon("Blue"); //memory reference 100
swap(red, blue);
System.out.println("red color="+red.getColor());
System.out.println("blue color="+blue.getColor());
foo(blue);
System.out.println("blue color="+blue.getColor());
}
private static void foo(Balloon balloon) { //baloon=100
balloon.setColor("Red"); //baloon=100
balloon = new Balloon("Green"); //baloon=200
balloon.setColor("Blue"); //baloon = 200
}
//Generic swap method
public static void swap(Object o1, Object o2){
Object temp = o1;
o1=o2;
o2=temp;
}
}
由於之前分析過一個例子,這裡我就不詳細分析具體的每一個步驟。
大家可以去看該例子的原文出處,有每一步的分析過程,而且還有視訊詳細講解(應該是要翻牆)
關於引用的知識,大家可以看我的文章 堆和棧
結語
最近太忙人也有點懶散所以很久沒更新部落格了,得趕快恢復過來。有問題請留言。點贊只需一秒,動力卻是永恆,給我點個贊吧!期待你的關注!