請點贊,你的點贊對我意義重大,滿足下我的虛榮心。
??常在河邊走,哪有不濕鞋?;蛟S面試過程中你遇到的問題就在這呢?
??關(guān)注我,面試不迷路~
一、Java中提供了抽象類還有接口,開發(fā)中如何去選擇呢?
這道題想考察什么?
Java是面向?qū)ο缶幊痰模橄笫撬囊淮筇卣?,而體現(xiàn)這個特征的就是抽象類與接口。抽象類與接口某些情況下都能夠互相替代,但是如果真的都能夠互相替代,那Java為何會設(shè)計出抽象與接口的概念?這就需要面試者能夠掌握兩者的區(qū)別。
考察的知識點
OOP(面向?qū)ο螅┚幊趟枷?,抽象與接口的區(qū)別與應(yīng)用場景;
考生應(yīng)該如何回答
抽象類的設(shè)計目的,是代碼復(fù)用;接口的設(shè)計目的,是對類的行為進(jìn)行約束。
- 當(dāng)需要表示is-a的關(guān)系,并且需要代碼復(fù)用時用抽象類
- 當(dāng)需要表示has-a的關(guān)系,可以使用接口
比如狗具有睡覺和吃飯方法,我們可以使用接口定義:
public interface Dog {
public void sleep();
public void eat();
}
但是我們發(fā)現(xiàn)如果采用接口,就需要讓每個派生類都實現(xiàn)一次sleep方法。此時為了完成對sleep方法的復(fù)用,我們可以選擇采用抽象類來定義:
public abstract class Dog {
public void sleep(){
//......
}
public abstract void eat();
}
但是如果我們訓(xùn)練,讓狗擁有一項技能——握手,怎么添加這個行為? 將握手方法寫入抽象類中,但是這會導(dǎo)致所有的狗都能夠握手。
握手是訓(xùn)練出來的,是對狗的一種擴(kuò)展。所以這時候我們需要單獨定義握手這種行為。這種行為是否可以采用抽象類來定義呢?
public abstract Handshake{
abstract void doHandshake();
}
如果采用抽象類定義握手,那我們現(xiàn)在需要創(chuàng)建一類能夠握手的狗怎么辦?
public class HandShakeDog extends Dog //,Handshake
大家都知道在Java中不能多繼承,這是因為多繼承存在二義性問題。
二義性問題:一個類如果能夠繼承多個父類,那么如果其中父類A與父類B具有相同的方法,當(dāng)調(diào)用這個方法時會調(diào)用哪個父類的方法呢?
所以此時,我們就需要使用接口來定義握手行為:
public interface Handshake{
void doHandshake();
}
public class HandShakeDog extends Dog implements Handshake
不是所有的狗都會握手,也不止狗會握手。我們可以同樣訓(xùn)練貓,讓貓也具備握手的技能,那么貓Cat類,同樣能夠?qū)崿F(xiàn)此接口,這就是"has-a"的關(guān)系。
抽象類強(qiáng)調(diào)從屬關(guān)系,接口強(qiáng)調(diào)功能,除了使用場景的不同之外,在定義中的不同則是:

二、重載和重寫是什么意思,區(qū)別是什么?
這道題想考察什么?
Java基礎(chǔ)
考察的知識點
面向?qū)ο蠖鄳B(tài)的基礎(chǔ)概念
考生應(yīng)該如何回答
重寫(Override)
重寫就是重新寫的意思,當(dāng)父類中的方法對于子類來說不適用或者需要擴(kuò)展增強(qiáng)時,子類可以對從父類中繼承來的方法進(jìn)行重寫。
比如Activity是Android開發(fā)中四大組件之一。在Activity中存在各種聲明周期方法:onCreate、onStart .....等等。而我們應(yīng)用中需要使用Activity來展示UI,那么我們會需要編寫自己的類繼承自Activity。
public class MainActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
在上述代碼中,onCreate就是被我們重寫的Activity中定義方法。我們在onCreate增加了setContentView
的調(diào)用,完成了對超類Activity中對應(yīng)方法的修改與擴(kuò)展。
重載(Overload)
重載則是在同一個類中,允許存在多個同名方法,只要它們的參數(shù)列表不同即可。比如在Android開發(fā)中,我們會使用LayoutInflater的inflate方法加載布局,inflate方法存在多個定義,其中包括兩個參數(shù)的,與三個參數(shù)的:
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
//......
}
可以看到在兩個參數(shù)的inflate方法中,會為我們調(diào)起三個參數(shù)的inflate方法,而定義第一個inflate方法的目的就是為了提供默認(rèn)的attachToRoot參數(shù)值。因為Java中無法定義方法參數(shù)默認(rèn)值,所以我們經(jīng)常使用重載達(dá)成此目的。
三、靜態(tài)內(nèi)部類是什么?和非靜態(tài)內(nèi)部類的區(qū)別是什么?
這道題想考察什么?
掌握static的作用與注意事項
考察的知識點
Java中關(guān)鍵字static
考生應(yīng)該如何回答
在定義內(nèi)部類時,如果內(nèi)部類被static聲明,則該內(nèi)部類為靜態(tài)內(nèi)部類。
public class OuterClass{
static class InnerClass{
}
}
當(dāng)內(nèi)部類被static聲明,那么在內(nèi)部類中就無法直接使用外部類的屬性。比如編寫普通內(nèi)部類:
public class OuterClass{
int i;
public class InnerClass{
public InnerClass(){
i = 10;
}
}
}
此時對OuterClass.java 進(jìn)行編譯,會生成:OuterClass.class 與 OuterClass$InnerClass.class 兩個文件。對后者反編譯我們將看到:
public class OuterClass$InnerClass {
public OuterClass$InnerClass(OuterClass var1) {
this.this$0 = var1;
var1.i = 10;
}
}
可以看到,普通內(nèi)部類構(gòu)造方法中實際上會隱式的傳遞外部類實例對象給內(nèi)部類。在內(nèi)部類中使用外部類的屬性:i。實際上是通過外部類的實例對象:var1獲取的。同時如果我們需要構(gòu)建出InnerClass的實例對象,非靜態(tài)內(nèi)部類也無法脫離外部類實體被創(chuàng)建。
下面我們將InnerClass定義為static靜態(tài)內(nèi)部類:
public class OuterClass{
int i;
public static class InnerClass{
public InnerClass(){
//i = 10;
}
}
}
此時無法使用外部類的普通成員屬性:i。其對應(yīng)字節(jié)碼為:
public class OuterClass$InnerClass {
public OuterClass$InnerClass() {
}
}
靜態(tài)內(nèi)部類中不再隱式的持有外部類的實例對象。但是如果我們將屬性i定義為static,那么在靜態(tài)內(nèi)部類中也是可以直接使用外部類的靜態(tài)成員屬性的,此時字節(jié)碼為:
public class OuterClass$InnerClass {
public OuterClass$InnerClass() {
OuterClass.i = 10;
}
}
內(nèi)部靜態(tài)類不需要有指向外部類的引用。但非靜態(tài)內(nèi)部類需要持有對外部類的引用。但是靜態(tài)內(nèi)部類能夠直接利用new OuterClass.InnerClass() 實例化。
因此靜態(tài)內(nèi)部類與非靜態(tài)內(nèi)部類的區(qū)別有:
- 非靜態(tài)內(nèi)部類能夠訪問外部類的靜態(tài)和非靜態(tài)成員,靜態(tài)類只能訪問外部類的靜態(tài)成員。
- 非靜態(tài)內(nèi)部類不能脫離外部類被創(chuàng)建,靜態(tài)內(nèi)部類可以。
四、Java中在傳參數(shù)時是將值進(jìn)行傳遞,還是傳遞引用?
這道題想考察什么?
是否了解什么是值傳遞和引用傳遞與真實場景使用,是否熟悉什么是值傳遞和引用傳遞在工作中的表現(xiàn)是什么?
考察的知識點
什么是值傳遞和引用傳遞的概念,兩者對開發(fā)中編寫的代碼的影響
考生應(yīng)該如何回答
值傳遞:在方法調(diào)用時,傳遞的參數(shù)是這個參數(shù)指向值的拷貝;
引用傳遞:在方法調(diào)用時,傳遞引用的地址
在Java中對于參數(shù)的傳遞可以分為兩種情況:
1.基本數(shù)據(jù)類型的參數(shù)
1 public class TransferTest {
2 public static void main(String[] args) {
3 int num = 1;
4 System.out.println("changeNum()方法調(diào)用之前:num = " + num);
5 changeNum(num);
6 System.out.println("changeNum()方法調(diào)用之后:num = " + num);
7 }
8
9 public static void changeNum(int x) {
10 x = 2;
11 }
12 }
運行結(jié)果:

傳遞過程的示意圖如下:

分析:num作為參數(shù)傳遞給changeNum()方法時,是將內(nèi)存空間中num所指向的那個存儲單元中存放的值1復(fù)制了一份傳遞給了changeNum()方法中的x變量,而這個x變量也在內(nèi)存空間中分配的一個存儲單元。這時就把num對的值1傳遞給了x變量所指向的存儲單元中。此后在changeNum()方法中對x變量的一切操作都是針對于x所指向的這個存儲單元,與num所指向的存儲單元無關(guān)。
所以,在changeNum()方法被調(diào)用后,num所指向的存儲單元的值還是沒有發(fā)生變化,這就是所謂的“值傳遞”。
值傳遞的精髓是:傳遞的是存儲單元中的內(nèi)容,而不是存儲單元的引用。
2.引用類型的參數(shù)
1 public class TransferTest2 {
2 public static void main(String[] args) {
3 Person person = new Person();
4 System.out.println(person);
5 change(person);
6 System.out.println(person);
7 }
8
9 public static void change(Person p) {
10 p = new Person();
11 }
12 }
13
14 /**
15 * Person類
16 */
17 class Person {
18
19 }
運行結(jié)果:

可以看出兩次打印結(jié)果一致。即調(diào)用change()方法后,person變量并沒發(fā)生改變。
傳遞過程的示意圖如下:

分析:
01.當(dāng)程序執(zhí)行到第3行 Person person = new Person()時,程序在堆內(nèi)存(heap)中開辟了一塊內(nèi)存空間用來存儲Person類實例對象,同時在棧內(nèi)存(stack)中開辟了一個存儲單元來存儲該實例對象的引用,即上圖中person指向的存儲單元。
02.當(dāng)程序執(zhí)行到第5行 change(person)時,person作為參數(shù)(實參)傳遞給了change()方法。這里是person將自己的存儲單元的內(nèi)容傳遞給了change()方法的p變量。 此后在change()方法中對p變量的一切操作都是針對于p變量所指向的存儲單元,與perosn所指向的存儲單元就沒有關(guān)系了。
因此Java的參數(shù)傳遞,不管是基本數(shù)據(jù)類型還是引用類型的參數(shù),都是按值傳遞!
今天的面試分享到此結(jié)束拉~下期在見