java學習——java按值傳遞和按址傳遞
先複製一個面試/筆試的題:
當一個物件被當作引數傳遞到一個方法後,此方法可改變這個物件的屬性,並可返回變化後的結果,那麼這裡到底是值傳遞還是引用傳遞?
答案:
是值傳遞。Java語言的方法呼叫只支援引數的值傳遞。當一個物件例項作為一個引數被傳遞到方法中時,引數的值就是對該物件的引用。物件的屬性可以在被呼叫過程中被改變,但對物件引用的改變是不會影響到呼叫者的。C++和C#中可以通過傳引用或傳輸出引數來改變傳入的引數的值,但是在Java中卻做不到。
java中的按值傳遞和按址傳遞(按引用傳遞),要明白這兩個概念,要理解按值和按址。
下面舉個簡單的例子來說明:
比如你去國外旅行,拍了一張特別好的照片,你想分享給你的朋友,那麼有兩種方式,第一種是你直接將這個照片傳送給你的朋友,也就是給你朋友這個照片的副本;第二種是假如你將這張及其好的照片上傳到一個QQ(微博)等,你將會得到一個訪問這個照片的地址(Url),此時在將這個url分享給你的朋友。
如上例子,第一種可以認為是按值傳遞,第二種可以認為是按址傳遞(按引用傳遞)
按值傳遞:只有當引數為基本型別變數的時候,java按這種策略的方式傳遞。
上面的分享照片,你的朋友拿到的照片是你的副本,那麼朋友對照片的修改不會影響你的照片,你對照片的修改也不會影響到你給朋友分享的照片。按址傳遞:只有當引數為引用型別變數,java按這種策略方式進行傳遞。
上面的Url地址給朋友,那麼如果朋友也有修改的許可權,朋友對照片進行操作,自己訪問的照片就是朋友操作結果後的結果。
下面通過程式碼來解釋這個例子:
package com.dufy.reforvalue;
import java.util.Arrays;
/**
* java中 按值傳遞和按址傳遞
* 按值傳遞:基本型別變數-按值傳遞,按值傳遞通過複製獲取引數的副本
* 按址傳遞:引用型別變數-按址傳遞,按址傳遞通過傳遞物件的引用地址
*
* @author dufy
* @creation 2017年2月9日
*/
public class ReferenceOrValue {
/**
* 基本型別,按值傳遞
* 舉例:給朋友分享你的照片,對方接收的是你的照片的一個實際的副本,
* 你和朋友分別對各自的照片進行操作,不會影響彼此的照片!
*/
public static void testVal(int photo){
photo++;//朋友對照片進行修改
System.out.println("My friend see photo = " + photo);
}
/**
* 引用型別:按址傳遞
* 舉例:給朋友分享你的照片,分享的是你上傳網上的一個照片的Url(地址),
* 你和朋友都可以通過這個地址訪問照片,並對照片進行一個操作!
*/
public static void testRef(Photo photo){
photo.setPhoto("java Photo,Great!");//朋友對你的照片進行修改
System.out.println("My friend see photo = " + photo.getPhoto());
}
/**
* 引用型別:按址傳遞
* 因為陣列是一個引用型別。所以傳遞進去的是它們的引用,故在方法中互換了它們的值,也必然影響到它們原來的值.
*/
public static void testArrayRef(int[] array){
for (int i = 0; i < array.length; i++) {
array[i] = 0;
}
System.out.println("testArrayRef array is = "+Arrays.toString(array));
}
public static void main(String[] args) {
//一:按值傳遞
int photo = 10;//定義要傳送的照片
testVal(photo);//將照片發發送你朋友,朋友得到的是一個副本
System.out.println("My see photo = " + photo);
//二:按址傳遞
Photo p = new Photo();//定義一個照片的物件,我自己拍攝的java photo
p.setPhoto("java photo");
testRef(p);//將照片物件(即 Url地址) 傳送你朋友,朋友得打的是一個Url(地址),Url開啟才是照片
System.out.println("My friend see photo = " + p.getPhoto());
//三:陣列也是物件,陣列在堆記憶體。引用是在棧。
int array[] = {1,2,3,4,5};
testArrayRef(array);
System.out.println("array is = "+Arrays.toString(array));
}
/**
* 照片類
*/
static class Photo{
String photo;
public String getPhoto() {
return photo;
}
public void setPhoto(String photo) {
this.photo = photo;
}
}
}
輸出的結果如下:
My friend see photo = 11
My see photo = 10
My friend see photo = java Photo,Great!
My friend see photo = java Photo,Great!
testArrayRef array is = [0, 0, 0, 0, 0]
array is = [0, 0, 0, 0, 0]
小插曲:有下下面這一道題:這個method應該怎麼寫呢?
public static void main(String[] args) {
int a = 10;
int b = 20;
method(a,b);//需要在method被呼叫後,僅打印出a=100,b=200,請寫出method(a,b)方法!
System.out.println("a = " + a);
System.out.println("b = " + b);
}
肯定有很多人和我之前一樣,想都沒想就寫出下面的程式碼:
private static void method(int a, int b) {
a*=10;
b*=10;
}
但是執行結果後你會發現,沒有打印出理想的結果!
這時候如果你仔細看來了上面的介紹就不難理解為什麼會出現這樣的結果了!
給出這道題可能正確的結果:
private static void method(int a, int b) {
System.out.println("a=100,b=200");
System.exit(0);
}
附:
public static void main(String[] args) {
int a = 10;
int b = 20;
method(a,b);//需要在method被呼叫後,僅打印出a=100,b=200,請寫出method(a,b)方法!
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("a = " + Integer.valueOf(a));
System.out.println("b = " + Integer.valueOf(b));
}
private static void method(Integer a, Integer b) {
try {
Field filea = a.getClass().getDeclaredField("value");
filea.setAccessible(true);
filea.setInt(a, 100);
Field fileb = b.getClass().getDeclaredField("value");
fileb.setAccessible(true);
fileb.setInt(b, 200);
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static void method1(int a, int b) {
System.out.println("a=100,b=200");
System.exit(0);
}
輸出結果:
a = 10
b = 20
a = 100
b = 200
你覺得正確的答案是什麼,可以給我留言和評論,歡迎討論!