本系列面試筆記所有問題,源自面試過程中的記錄總結,主要涉及Java和Android,但是由於面試過程中任何問題都有可能被問到,所以也會涉及一些其他方向的知識。部分答案參考網上朋友的文章,如有錯誤之處,歡迎批評指正。
在面試的過程中,經常被問到一些平時編程忽略的細節問題,比如強制類型轉換什麼時候可以強制轉換,什麼可以編譯通過卻報運行錯誤。這些細節如果不是平時加以實踐並注意,在真正被問到時還真有點蒙 - -
以下是我對父類子類之間類型轉換,以及動態鏈接等問題的總結。
強制類型轉換規則(個人總結):
1)在同一個繼承樹上的類都可以相互進行強制類型轉換,這些轉換在編譯是都可以通過,但是在運行時,部分情況會報ClassCastException異常(規則第五條後續講解)。比如:A extends B, B extends C; 則A B C 三個類的對象都可以相互強轉。
2)不在同一個繼承樹上的類型不可以進行強制類型轉換(編譯不會通過)。
3)兩個子類不可以進行強制類型轉換。
4)一個子類的對象可以強制轉換成父類的對象。
5)一個父類的對象在強制轉換成子類對象編譯可以通過,但是運行時會報ClassCastException異常。
注意:一個對象進行強制類型轉換之後,對象的類型就發生了改變,當使用這個對象調用屬性和方法時,調用的是轉換之後類的屬性和方法。(注意動態鏈接問題,後文介紹)
從內存角度理解強制轉換:
假設現在有一個父類father,他裏面的變量需要佔用1M的內存。這個father類有一個子類Son,需要佔用0.5M的內存。
結合實例來分析強制類型轉換:
Father f = new Father(); //系統將爲其分配1M的內存空間。
Son s = new Son();//系統將爲其分配1.5M的內存空間。因爲子類會有一個隱藏的super指向父類實例,所以在實例 化子類對象之前,會先去實例化一個父類對象。也就是會先執行父類的構造函數。也是由於對象s中包含了父類的實例,所以子類對象s纔可以調用父類father的方法。
Son s1 = s; // s1指向s的1.5M內存
Father f1 = (Father)s; //這是f1將指向對象s那1.5M內存中的屬於父類實例的1M內存,所以f1這個對象只能調用父 類的方法和屬性(存在1M內存中的),而不能調用子類的方法(存在0.5M內存中)
Son s2 = (Son) f;//編譯通過,運行時報ClassCastException異常。因爲f中只有1M的內存,而子類的引用s2必須有 1.5M的內存才能運行,所以在運行時,會報類型轉換的異常。
Son s3 = (Son)f1;//這個語句可以正常運行。因爲f1是有子類對象s轉換而來的,所以她是具有1.5M內存的。只不 過f1指向的只有1M內存,但是既然他具備1.5M的內存,s3自然會指向1.5M的內存,所以這局運行時正常。
什麼是動態鏈接?
前提:父類引用指向了子類對象。Father f = new Son();
當父類引用f調用一個方法時,比如f.foo();
若 foo()這個方法在子類被重寫了,調用子類的foo();
若foo()這個方法在子類沒有被重寫。調用父類的foo();
以上兩點就是java語言的動態鏈接。這個特點也體現了Java語言的多態性。
注意:只有方法纔會被重寫,屬性不可以重寫。所以動態鏈接這個性質只是針對方法而言。f.name 一定指的是父類的name屬性。