??自己的第一篇博客,各位看官多多指教母赵。這里講的是一道面試題逸爵,題目如下:
//要求寫一個(gè)swap方法交換ab值輸出打印a=2 b=1
public static void main(String[] args) {
Integer a=1,b=2;
swap(a,b);
System.out.println("a="+a+" b="+b);
}
??這道題目所涉及到的知識(shí)點(diǎn)包括:
- Integer的自動(dòng)裝箱
- java的值傳遞和引用傳遞
- 對(duì)Integer類的源碼分析
- java的反射機(jī)制
1. Integer的自動(dòng)裝箱
??java的自動(dòng)裝箱就是自動(dòng)將原始數(shù)據(jù)類型(byte,short,char,int,long,float,double,boolean)轉(zhuǎn)換成對(duì)應(yīng)的基本數(shù)據(jù)類型包裝類對(duì)象(Byte,Short,Character,Integer,Long,Float,Double,Boolean)具滴,比如將int的變量轉(zhuǎn)換成Integer對(duì)象
這里的Integer a=1 經(jīng)過自動(dòng)裝箱以后變?yōu)?br>
Integer a=Integer.valueOf(1);
那么我們接著查看Integer類中的valueOf()方法如下:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
??接著查看IntegerCache這個(gè)內(nèi)部類可以發(fā)現(xiàn)java虛擬機(jī)在我們首次使用Integer的時(shí)候會(huì)初始化數(shù)值在-128-127之間的整數(shù)凹嘲,而且這個(gè)區(qū)間范圍可以在虛擬機(jī)啟動(dòng)的時(shí)候自己設(shè)定。換句話說构韵,數(shù)值在-128-127之間的相同整數(shù)都會(huì)取自相同的內(nèi)存具體值周蹭。下面例子中a和b都是指向相同的內(nèi)存地址,而x和y指向兩塊不同的內(nèi)存地址疲恢。
Integer x=-129;
Integer y=-129;
System.out.println(x==y); //輸出false
Integer a=-128;
Integer b=-128;
System.out.println(a==b); //輸出true
Integer xx=new Integer(1);
Integer yy=new Integer(1);
System.out.println(xx==yy); //輸出false
??說到這里必須說一下valueOf(int i)方法最終返回的是new Integer(i);而Integer類的構(gòu)造函數(shù)如下凶朗,這里的value是解題的關(guān)鍵,value在Integer類中是一個(gè)final的私有成員變量显拳。
public Integer(int value) {
this.value = value;
}
說出正確答案之前還得看看Integer的源碼中的toString方法
public String toString() { return toString(value); }
可見其返回的真實(shí)值就是value棚愤,并且value就是在構(gòu)造函數(shù)中初始化的。在swap方法中我們拿到了Integer的引用杂数,那么我們就可以通過反射來改變對(duì)應(yīng)實(shí)例的局部變量值宛畦。具體代碼如下:
private static void swap1(Integer aa, Integer bb) {
try {
Field aaValue = aa.getClass().getDeclaredField("value");
aaValue.setAccessible(true);
aaValue.set(aa,2);
Field bbValue = bb.getClass().getDeclaredField("value");
bbValue.setAccessible(true);
bbValue.set(bb,1);
System.out.println(Integer.valueOf(1));//輸出2
} catch (Exception e) {
e.printStackTrace();
}
}
但是運(yùn)行之后我們發(fā)現(xiàn)其結(jié)果是:
a=2 b=2
??看到這里是不是感覺很不可思議,但是我們距離真相已經(jīng)很近了揍移。我們疑惑的是為什么b的值沒有發(fā)生改變次和,更疑惑的是為什么Integer.valueOf(1)輸出的竟然是2。這是因?yàn)榛緮?shù)據(jù)類型在-128-127之間的數(shù)都會(huì)自動(dòng)裝箱并且從緩存中去取那伐,我們反射改變的只是緩存中的1對(duì)應(yīng)的實(shí)例的實(shí)例變量value值踏施,我們?cè)赽bValue.set(bb,1);的時(shí)候1會(huì)自動(dòng)裝箱去緩存中去找,而這時(shí)緩存中的Integer.valueOf(1)已經(jīng)是2罕邀,所以返回的還是2畅形。解決的方法就是不讓其去緩存中找,參考代碼如下:
//正解
private static void swap1(Integer aa, Integer bb) {
try {
Field aaValue = aa.getClass().getDeclaredField("value");
aaValue.setAccessible(true);
aaValue.set(aa,2);
Field bbValue = bb.getClass().getDeclaredField("value");
bbValue.setAccessible(true);
//這里的1不會(huì)自動(dòng)裝箱诉探,當(dāng)然就不會(huì)從緩存中去取值
bbValue.set(bb,new Integer(1));
} catch (Exception e) {
e.printStackTrace();
}
}
??這里還是要多說一句日熬,如果a,b變量的值是-128-127之外的整數(shù)的話雖然會(huì)自動(dòng)裝箱阵具,但是不會(huì)從緩存中去取值碍遍,那么bbValue.set(bb,-128-127之外的整數(shù));就可以了,是不用專門new Integer()的阳液,參考代碼如下:
public static void main(String[] args) {
Integer a=999;
Integer b=888;
swap1(a,b);
System.out.println("a="+a+" b="+b);
}
private static void swap1(Integer aa, Integer bb) {
try {
Field aaValue = aa.getClass().getDeclaredField("value");
aaValue.setAccessible(true);
aaValue.set(aa,888);
Field bbValue = bb.getClass().getDeclaredField("value");
bbValue.setAccessible(true);
bbValue.set(bb,999);//這里沒有new Integer(999)
} catch (Exception e) {
e.printStackTrace();
}
}
輸出結(jié)果為a=888 b=999
2. 錯(cuò)誤答案示例
??對(duì)于這道題目的解法有些同學(xué)可能會(huì)給出下面的答案怕敬,明顯是錯(cuò)誤的,因?yàn)樗鼈兙唧w交換的還是aa和bb所指向的內(nèi)存地址值帘皿,而這一塊內(nèi)存地址值所指向的內(nèi)存空間的內(nèi)容并沒有改變东跪,所以a和b變量是沒有變化的(參考圖1)。
private static void swap(Integer aa,Integer bb){
Integer temp;
temp=aa;
bb=temp;
aa=bb;
}
??按照我自己的理解,java的值傳遞和引用傳遞實(shí)際上都是值傳遞虽填,因?yàn)橐脗鬟f最終傳遞的還是引用對(duì)象的內(nèi)存具體值(下圖紅色線條代表交換后的狀態(tài)丁恭,可見a和b并沒有發(fā)生變化)
3. 題目變種
//對(duì)于這道題是不能用反射的,因?yàn)槭腔緮?shù)據(jù)類型的值傳遞
public static void main(String[] args) {
int a=1;
int b=2;
swap(a,b);
System.out.println("a="+a+" b="+b);
}
投機(jī)取巧的做法如下:
public static void main(String[] args) {
int a=1;
int b=2;
swap(a,b);
System.out.println("a="+a+" b="+b);
}
private static void swap(Integer aa, Integer bb) {
System.out.println("a="+2+" b="+1);
System.exit(0);
}
??題目雖小斋日,卻包含很多知識(shí)點(diǎn)牲览,希望大家能夠有所收獲,歡迎留言討論恶守!