欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

從一道面試題看你對(duì)java的理解程度

 更新時(shí)間:2018年09月05日 10:24:58   投稿:daisy  
這篇文章主要給大家介紹了關(guān)于如何從一道面試題看你對(duì)java的理解程度的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起看看吧

簡(jiǎn)介

最近有點(diǎn)忙,很久沒(méi)更新文章了,后面會(huì)慢慢恢復(fù)...回顧正題

最近看到一篇文章,關(guān)于一道面試題,先看一下題目,如下:

public static void main(String[] args) {
 Integer a = 1;
 Integer b = 2;
 System.out.printf("a = %s, b = %s\n", a, b);
 swap(a, b);
 System.out.printf("a = %s, b = %s\n", a, b);
 }

public static void swap(Integer a, Integer b) {
 // TODO 實(shí)現(xiàn)
}

有人可能在沒(méi)經(jīng)過(guò)仔細(xì)考慮的情況下,給出以下的答案

// 特別提醒,這是錯(cuò)誤的方式
// 特別提醒,這是錯(cuò)誤的方式
// 特別提醒,這是錯(cuò)誤的方式
public static void swap(Integer a, Integer b) {
 // TODO 實(shí)現(xiàn)
 Integer temp = a;
 a = b;
 b = temp;
}

很遺憾,這是錯(cuò)誤的。重要的事注釋三遍

那么為什么錯(cuò)誤,原因是什么?

想要搞清楚具體的原因,在這里你需要搞清楚以下幾個(gè)概念,如果這個(gè)概念搞清楚了,你也不會(huì)把上面的實(shí)現(xiàn)方法寫錯(cuò)

  • 形參和實(shí)參
  • 參數(shù)值傳遞
  • 自動(dòng)裝箱

所以,上面的問(wèn)題先放一邊,先看一下這幾個(gè)概念

形參和實(shí)參

什么是形參?什么是實(shí)參?概念上的東西,參考教科書(shū)或者google去吧,下面直接代碼說(shuō)明更加明顯

public void test() {
 int shi_can = 0;

 testA(shi_can);
}

public void testA(int xing_can) {

}

注:為了清楚的表達(dá)意思,我命名的時(shí)候并沒(méi)有按照java的駝峰規(guī)則命名,這里只是為了演示

通過(guò)上面的代碼很清楚的表達(dá)形參和實(shí)參的概念,在調(diào)用testA時(shí),傳遞的就是實(shí)參,而在testA方法簽名中的參數(shù)為形參

從作用域上看,形參只會(huì)在方法內(nèi)部生效,方法結(jié)束后,形參也會(huì)被釋放掉,所以形參是不會(huì)影響方法外的

值傳遞和引用傳遞

值傳遞:傳遞的是實(shí)際值,像基本數(shù)據(jù)類型

引用傳遞:將對(duì)象的引用作為實(shí)參進(jìn)行傳遞

java基本類型數(shù)據(jù)作為參數(shù)是值傳遞,對(duì)象類型是引用傳遞

實(shí)參是可以傳遞給形參的,但是形參卻不能影響實(shí)參,所以,當(dāng)進(jìn)行值傳遞的情況下,改變的是形參的值,并沒(méi)有改變實(shí)參,所以無(wú)論是引用傳遞還是值傳遞,只要更改的是形參本身,那么都無(wú)法影響到實(shí)參的。對(duì)于引用傳遞而言,不同的引用可以指向相同的地址,通過(guò)形參的引用地址,找到了實(shí)際對(duì)象分配的空間,然后進(jìn)行更改就會(huì)對(duì)實(shí)參指向的對(duì)象產(chǎn)生影響

額,上面表述,可能有點(diǎn)繞,看代碼

// 僅僅是一個(gè)java對(duì)象
public class IntType {

 private int value;

 public int getValue() {
 return value;
 }

 public void setValue(int value) {
 this.value = value;
 }
}

// main方法
public class IntTypeSwap {
 public static void main(String[] args) {

 // CODE_1
 IntType type1 = new IntType();
 type1.setValue(1);

 IntType type2 = new IntType();
 type2.setValue(2);
 // CODE_1

 swap1(type1, type2);
 System.out.printf("type1.value = %s, type2.value = %s", type1.getValue(), type2.getValue());
 swap2(type1, type2);
 System.out.println();
 System.out.printf("type1.value = %s, type2.value = %s", type1.getValue(), type2.getValue());
 }

 public static void swap2(IntType type1, IntType type2) {
 int temp = type1.getValue();
 type1.setValue(type2.getValue());
 type2.setValue(temp);
 }

 public static void swap1(IntType type1, IntType type2) {
 IntType type = type1;
 type1 = type2;
 type2 = type;
 }
}

在main方法中,CODE_1中間的代碼為聲明了兩個(gè)對(duì)象,分別設(shè)置value為1和2,而swap1和swap2兩個(gè)方法的目的是為了交互這兩個(gè)對(duì)象的value值

先思考一下,應(yīng)該輸出的結(jié)果是什么

...

...

type1.value = 1, type2.value = 2
type1.value = 2, type2.value = 1

從輸出結(jié)果來(lái)看swap1并沒(méi)有達(dá)到目的,回頭看一下

swap1public static void swap1(IntType type1, IntType type2) {
 IntType type = type1;
 type1 = type2;
 type2 = type;
 }

從值傳遞的角度來(lái)看,對(duì)象參數(shù)傳遞采用的是引用傳遞,那么type1和type2傳遞過(guò)來(lái)的是指向?qū)ο蟮囊?,在方法?nèi)部,直接操作形參,交換了形參的內(nèi)容,這樣形參改變,都是并沒(méi)有對(duì)實(shí)參產(chǎn)生任何影響,也沒(méi)有改變對(duì)象實(shí)際的值,所以,結(jié)果是無(wú)法交換

而對(duì)于swap2,對(duì)象引用作為形參傳遞過(guò)來(lái)后,并沒(méi)有對(duì)形參做任何的改變,而是直接操作了形參所指向的對(duì)象實(shí)際地址,那這樣,無(wú)論是實(shí)參還是其他地方,只要是指向該對(duì)象的所有的引用地址對(duì)應(yīng)的值都會(huì)改變

自動(dòng)裝箱

看我上面的那個(gè)例子的swap1,是不是頓時(shí)覺(jué)得與上面的面試題的錯(cuò)誤做法非常相似了,是的,錯(cuò)誤的原因是一模一樣的,就是稍微有一點(diǎn)區(qū)別,就是Integer不是new出來(lái)的,而是自動(dòng)裝箱的一個(gè)對(duì)象,那么什么是自動(dòng)裝箱呢?jdk到底做了什么事?

如果你不想知道為什么,只想知道結(jié)果,那么我就直說(shuō),自動(dòng)裝箱就是jdk調(diào)用了Integer的valueOf(int)的方法,很簡(jiǎn)單,看源碼

public static Integer valueOf(int i) {
 if (i >= IntegerCache.low && i <= IntegerCache.high)
 return IntegerCache.cache[i + (-IntegerCache.low)];
 return new Integer(i);
 }

上面那些如果不想深究可以忽略,就看最后一句,是不是明白了什么呢。沒(méi)錯(cuò),也是new出來(lái)一個(gè)對(duì)象,如果想知道上面的代碼做了什么處理,可以參考 Long==Long有趣的現(xiàn)象 這篇文章,里面有介紹類似的

好了,有人可能會(huì)問(wèn),為什么會(huì)知道自動(dòng)裝箱調(diào)用的是valueOf方法,這里其他人怎么知道的我不清楚,我是通過(guò)查看反編譯的字節(jié)碼指令知道的

public static void main(String[] args) {
 Integer a = 1;
 Integer b = 2;
 System.out.printf("a = %s, b = %s\n", a, b);
 swap(a, b);
 System.out.printf("a = %s, b = %s\n", a, b);
 }

 public static void swap(Integer a, Integer b) {
 Integer temp = a;
 a = b;
 b = temp;
 }

反編譯出來(lái)的結(jié)果為

對(duì)比一下可以很清楚的看到valueOf(int)方法被調(diào)用

回歸

好,現(xiàn)在回歸正題了,直接操作形參無(wú)法改變實(shí)際值,而Integer又沒(méi)有提供set方法,那是不是無(wú)解了呢?我很好奇如果有人以下這樣寫,面試官會(huì)有什么反應(yīng)

public static void swap(Integer a, Integer b) {
 // TODO 實(shí)現(xiàn)
 // 無(wú)解,
 }

既然出了肯定是有解的,可以實(shí)現(xiàn),回頭看看,在上面swap2的那個(gè)例子中是通過(guò)set方法來(lái)改變值的,那么Integer有沒(méi)有提供呢?答案沒(méi)有(我沒(méi)找到)

那就先看看源碼

private final int value;
...
public Integer(int value) {
 this.value = value;
 }

這是Integer的構(gòu)造函數(shù),可以看到Integer對(duì)象實(shí)際值是用value屬性來(lái)存儲(chǔ)的,但是這個(gè)value是被final修飾的,沒(méi)辦法繼續(xù)找,value沒(méi)有提供任何的set方法。既然在萬(wàn)法皆不通的情況下,那就只能動(dòng)用反射來(lái)解決問(wèn)題

public static void swap(Integer a, Integer b) {
 int temp = a.intValue();
 try {
 Field value = Integer.class.getDeclaredField("value");
 value.setAccessible(true);
 value.set(a, b);
 value.set(b, temp);

 } catch (NoSuchFieldException e) {
 e.printStackTrace();
 } catch (IllegalAccessException e) {
 e.printStackTrace();
 }
 }

現(xiàn)在感覺(jué)很開(kāi)心,終于找到解決方案,可是當(dāng)你執(zhí)行的時(shí)候,從輸出結(jié)果你會(huì)發(fā)現(xiàn),jdk在跟我開(kāi)玩笑嗎

a = 1, b = 2
a = 2, b = 2

為什么會(huì)出現(xiàn)這種情況,無(wú)奈,調(diào)試會(huì)發(fā)現(xiàn)是在value.set的時(shí)候?qū)nteger的緩存值改變了,因?yàn)?code>value.set(Object v1, Object v2)兩個(gè)參數(shù)都是對(duì)象類型,所以temp會(huì)進(jìn)行自動(dòng)裝箱操作,會(huì)調(diào)用valueOf方法,這樣會(huì)獲取到錯(cuò)誤的緩存值,所以,為了避免這種情況,就只能不需要調(diào)用緩存值,直接new Integer就可以跳過(guò)緩存,所以代碼改成如下即可

public static void swap(Integer a, Integer b) {
 int temp = a.intValue();
 try {
 Field value = Integer.class.getDeclaredField("value");
 value.setAccessible(true);
 value.set(a, b);
 value.set(b, new Integer(temp));

 } catch (NoSuchFieldException e) {
 e.printStackTrace();
 } catch (IllegalAccessException e) {
 e.printStackTrace();
 }
 }

至此,這道題完美結(jié)束

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • Java List雙擊事件實(shí)現(xiàn)方法

    Java List雙擊事件實(shí)現(xiàn)方法

    這篇文章主要介紹了Java List雙擊事件實(shí)現(xiàn)方法,需要的朋友可以參考下
    2014-09-09
  • 解決ObjectMapper序列換Map時(shí)候的坑

    解決ObjectMapper序列換Map時(shí)候的坑

    這篇文章主要介紹了解決ObjectMapper序列換Map時(shí)候的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Spring Boot配置過(guò)濾器的2種方式示例

    Spring Boot配置過(guò)濾器的2種方式示例

    這篇文章主要給大家介紹了關(guān)于Spring Boot配置過(guò)濾器的2種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Spring Boot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • Java-IO流實(shí)驗(yàn)

    Java-IO流實(shí)驗(yàn)

    流是一種抽象概念,它代表了數(shù)據(jù)的無(wú)結(jié)構(gòu)化傳遞。。用來(lái)進(jìn)行輸入輸出操作的流就稱為IO流。換句話說(shuō),IO流就是以流的方式進(jìn)行輸入輸出,希望能給您帶來(lái)幫助
    2021-06-06
  • java基本教程之Thread中start()和run()的區(qū)別 java多線程教程

    java基本教程之Thread中start()和run()的區(qū)別 java多線程教程

    這篇文章主要介紹了Thread中start()和run()的區(qū)別,Thread類包含start()和run()方法,它們的區(qū)別是什么?下面將對(duì)此作出解答
    2014-01-01
  • idea如何通過(guò)maven指定JDK版本

    idea如何通過(guò)maven指定JDK版本

    這篇文章主要介紹了idea如何通過(guò)maven指定JDK版本問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • mybatis批量插入,批量更新以及null值問(wèn)題的解決

    mybatis批量插入,批量更新以及null值問(wèn)題的解決

    這篇文章主要介紹了mybatis批量插入,批量更新以及null值問(wèn)題的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • java音樂(lè)播放器編寫源碼

    java音樂(lè)播放器編寫源碼

    這篇文章主要為大家詳細(xì)介紹了java音樂(lè)播放器的編寫源碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • 詳解Java中Method的Invoke方法

    詳解Java中Method的Invoke方法

    這篇文章主要介紹了詳解Java中Method的Invoke方法,需要的朋友可以參考下
    2017-10-10
  • Java簡(jiǎn)化復(fù)雜系統(tǒng)調(diào)用的門面設(shè)計(jì)模式

    Java簡(jiǎn)化復(fù)雜系統(tǒng)調(diào)用的門面設(shè)計(jì)模式

    Java門面模式是一種結(jié)構(gòu)性設(shè)計(jì)模式,它為復(fù)雜系統(tǒng)提供了一個(gè)簡(jiǎn)單的接口,使得系統(tǒng)的客戶端能夠更加方便地使用系統(tǒng)功能。門面模式通過(guò)封裝復(fù)雜的子系統(tǒng),隱藏系統(tǒng)的實(shí)現(xiàn)細(xì)節(jié),提高了系統(tǒng)的易用性和靈活性
    2023-04-04

最新評(píng)論