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

2 重載和重寫是什么意思,區(qū)別是什么? (京東)
這道題想考察什么?
Java基礎(chǔ)
考察的知識點(diǎn)
面向?qū)ο蠖鄳B(tài)的基礎(chǔ)概念
考生應(yīng)該如何回答
重寫(Override)
重寫就是重新寫的意思,當(dāng)父類中的方法對于子類來說不適用或者需要擴(kuò)展增強(qiáng)時(shí),子類可以對從父類中繼承來的方法進(jìn)行重寫。
比如Activity是Android開發(fā)中四大組件之一。在Activity中存在各種聲明周期方法:onCreate、onStart .....等等。而我們應(yīng)用中需要使用Activity來展示UI,那么我們會(huì)需要編寫自己的類繼承自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)
重載則是在同一個(gè)類中,允許存在多個(gè)同名方法,只要它們的參數(shù)列表不同即可。比如在Android開發(fā)中,我們會(huì)使用LayoutInflater的inflate方法加載布局,inflate方法存在多個(gè)定義,其中包括兩個(gè)參數(shù)的,與三個(gè)參數(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) {
//......
}
可以看到在兩個(gè)參數(shù)的inflate方法中,會(huì)為我們調(diào)起三個(gè)參數(shù)的inflate方法,而定義第一個(gè)inflate方法的目的就是為了提供默認(rèn)的attachToRoot參數(shù)值。因?yàn)镴ava中無法定義方法參數(shù)默認(rèn)值,所以我們經(jīng)常使用重載達(dá)成此目的。
3 靜態(tài)內(nèi)部類是什么?和非靜態(tài)內(nèi)部類的區(qū)別是什么?
這道題想考察什么?
掌握static的作用與注意事項(xiàng)
考察的知識點(diǎn)
Java中關(guān)鍵字static
考生應(yīng)該如何回答
在定義內(nèi)部類時(shí),如果內(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;
}
}
}
此時(shí)對OuterClass.java 進(jìn)行編譯,會(huì)生成:OuterClass.class 與 OuterClass$InnerClass.class 兩個(gè)文件。對后者反編譯我們將看到:
public class OuterClass$InnerClass {
public OuterClass$InnerClass(OuterClass var1) {
this.this$0 = var1;
var1.i = 10;
}
}
可以看到,普通內(nèi)部類構(gòu)造方法中實(shí)際上會(huì)隱式的傳遞外部類實(shí)例對象給內(nèi)部類。在內(nèi)部類中使用外部類的屬性:i。實(shí)際上是通過外部類的實(shí)例對象:var1獲取的。同時(shí)如果我們需要構(gòu)建出InnerClass的實(shí)例對象,非靜態(tài)內(nèi)部類也無法脫離外部類實(shí)體被創(chuàng)建。
下面我們將InnerClass定義為static靜態(tài)內(nèi)部類:
public class OuterClass{
int i;
public static class InnerClass{
public InnerClass(){
//i = 10;
}
}
}
此時(shí)無法使用外部類的普通成員屬性:i。其對應(yīng)字節(jié)碼為:
public class OuterClass$InnerClass {
public OuterClass$InnerClass() {
}
}
靜態(tài)內(nèi)部類中不再隱式的持有外部類的實(shí)例對象。但是如果我們將屬性i定義為static,那么在靜態(tài)內(nèi)部類中也是可以直接使用外部類的靜態(tài)成員屬性的,此時(shí)字節(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() 實(shí)例化。
因此靜態(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)部類可以。
4 Java中在傳參數(shù)時(shí)是將值進(jìn)行傳遞,還是傳遞引用?
這道題想考察什么?
是否了解什么是值傳遞和引用傳遞與真實(shí)場景使用,是否熟悉什么是值傳遞和引用傳遞在工作中的表現(xiàn)是什么?
考察的知識點(diǎn)
什么是值傳遞和引用傳遞的概念,兩者對開發(fā)中編寫的代碼的影響
考生應(yīng)該如何回答
值傳遞:在方法調(diào)用時(shí),傳遞的參數(shù)是這個(gè)參數(shù)指向值的拷貝;
引用傳遞:在方法調(diào)用時(shí),傳遞引用的地址
在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 }
運(yùn)行結(jié)果:

傳遞過程的示意圖如下:

分析:num作為參數(shù)傳遞給changeNum()方法時(shí),是將內(nèi)存空間中num所指向的那個(gè)存儲(chǔ)單元中存放的值1復(fù)制了一份傳遞給了changeNum()方法中的x變量,而這個(gè)x變量也在內(nèi)存空間中分配的一個(gè)存儲(chǔ)單元。這時(shí)就把num對的值1傳遞給了x變量所指向的存儲(chǔ)單元中。此后在changeNum()方法中對x變量的一切操作都是針對于x所指向的這個(gè)存儲(chǔ)單元,與num所指向的存儲(chǔ)單元無關(guān)。
所以,在changeNum()方法被調(diào)用后,num所指向的存儲(chǔ)單元的值還是沒有發(fā)生變化,這就是所謂的“值傳遞”。
值傳遞的精髓是:傳遞的是存儲(chǔ)單元中的內(nèi)容,而不是存儲(chǔ)單元的引用。
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 }
運(yùn)行結(jié)果:

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

01.當(dāng)程序執(zhí)行到第3行 Person person = new Person()時(shí),程序在堆內(nèi)存(heap)中開辟了一塊內(nèi)存空間用來存儲(chǔ)Person類實(shí)例對象,同時(shí)在棧內(nèi)存(stack)中開辟了一個(gè)存儲(chǔ)單元來存儲(chǔ)該實(shí)例對象的引用,即上圖中person指向的存儲(chǔ)單元。
02.當(dāng)程序執(zhí)行到第5行 change(person)時(shí),person作為參數(shù)(實(shí)參)傳遞給了change()方法。這里是person將自己的存儲(chǔ)單元的內(nèi)容傳遞給了change()方法的p變量。 此后在change()方法中對p變量的一切操作都是針對于p變量所指向的存儲(chǔ)單元,與perosn所指向的存儲(chǔ)單元就沒有關(guān)系了。
因此Java的參數(shù)傳遞,不管是基本數(shù)據(jù)類型還是引用類型的參數(shù),都是按值傳遞!
5 使用equals和==進(jìn)行比較的區(qū)別
這道題想考察什么?
在開發(fā)中當(dāng)需要對引用類型和基本數(shù)據(jù)類型比較時(shí)應(yīng)該怎么做,為什么有區(qū)別。
考察的知識點(diǎn)
equals 的實(shí)現(xiàn)以及棧和堆的內(nèi)存管理。
考生應(yīng)該如何回答
==
對于不同的數(shù)據(jù)類型,使用==進(jìn)行比較的意義不同:
- 基本數(shù)據(jù)類型(也稱原始數(shù)據(jù)類型) :byte,short,char,int,long,float,double,boolean。用==,比較的是他們的值;
- 引用數(shù)據(jù)類型:用(==)進(jìn)行比較的時(shí)候,比較的是地址。
對于引用類型,除非是同一個(gè)new出來的對象,他們的比較的結(jié)果為true,否則為false。因?yàn)槊縩ew一次,都會(huì)重新開辟堆內(nèi)存空間,哪怕他們的值一致,但是也是在不同的地址存放。所以對于引用類型的值比較應(yīng)該使用equals方法。
equals()方法介紹
equals 方法是 Object 中定義的一個(gè)方法,源碼如下:
public boolean equals(Object obj) {
return (this == obj);
}
可以看到實(shí)際上就是用的 == 實(shí)現(xiàn)的,所以這個(gè)原始方法意義不大,一般在類中做比較的時(shí)候,都會(huì)重寫這個(gè)方法,如String、Integer、Date等。
//Integer
public boolean equals(Object obj) {
if (obj instanceof Integer) {
//比較Integer中包裝的int值
return value == ((Integer)obj).intValue();
}
return false;
}
//String
public boolean equals(Object anObject) {
if (this == anObject) { //同一個(gè)對象
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = length();
if (n == anotherString.length()) {
int i = 0;
while (n-- != 0) {
if (charAt(i) != anotherString.charAt(i)) //逐個(gè)字符比較
return false;
i++;
}
return true;
}
}
return false;
}
String的比較
public class StringDemo {
public static void main(String args[]) {
String str1 = "Hello";
String str2 = new String("Hello");
String str3 = str2; // 引用傳遞
System.out.println(str1 == str2); // false
System.out.println(str1 == str3); // false
System.out.println(str2 == str3); // true
System.out.println(str1.equals(str2)); // true
System.out.println(str1.equals(str3)); // true
System.out.println(str2.equals(str3)); // true
}
}
棧和堆的內(nèi)存分析圖:

由此可見,equals 是比較字符串的內(nèi)容是否一樣,== 是比較字符串的堆內(nèi)存地址是否一樣。
結(jié)論
equals和==的區(qū)別,需要分情況討論:
- 沒有重寫 equals ,則 equals 和 == 是一樣的。
- 如果重寫了 equals,則需看 equals 的方法實(shí)現(xiàn)。以 String 類為例:
- equals 是比較字符串的內(nèi)容是否一樣;
- == 是比較字符串的堆內(nèi)存地址是否一樣,或者說引用的值是否相等。
文末
此面試題會(huì)持續(xù)更新?。。?!