Java面向對象面試題

標簽(空格分隔): Java面向對象


1. 面向對象的特征有哪些方面?

  • 抽象:

    • 抽象就是找出一些事物的相似和共性之處,然后將這些事物歸為一個類,這個類只考慮這些事物的相似和共性之處,并且會忽略與當前主題和目標無關的那些方面,將注意力集中在與當前目標有關的方面。
    • 例如,看到一只螞蟻和大象,你能夠想象出它們的相同之處,那就是 抽象。抽象包括行為抽象和狀態抽象兩個方面。例如,定義一個 Person 類,如下:
      classPerson{ String name; int age; }人本來是很復雜的事物,有很多方面,但因為當前系統只需要了解人的姓名和年齡,所以上面定義的類中只包含姓名和年齡這兩個屬性,這就是一種抽像,使用抽象可以避免考慮一些與目標無關的細節。我對抽象的理解就是不要用顯微鏡去看一個事物的所有方面,這樣涉及 的內容就太多了,而是要善于劃分問題的邊界,當前系統需要什么,就只考慮什么。
  • 繼承:在定義和實現一個類的時候,可以在一個已經存在的類的基礎之上來進行,把這個已經存在的類所定義的內容作為自己的內容,并可以加入若干新的內容,或修改原來的方法使之更適合特殊的需要,這就是繼承。繼承是子類自動共享父類數據和方法的機制,這是類之間的一種關系,提高了軟件的可重用性和可擴展性。

  • 封裝:

    • 封裝是保證軟件部件具有優良的模塊性的基礎,封裝的目標就是要實現軟件部件的“高內聚、低耦合”,防止程序相互依賴性而帶來的變動影響。在面向對象的編程語言中,對象是封裝 的最基本單位,面向對象的封裝比傳統語言的封裝更為清晰、更為有力。面向對象的封裝就是把描述一個對象的屬性和行為的代碼封裝在一個“模塊”中,也就是一個類中,屬性用變量定義,行為用方法進行定義,方法可以直接訪問同一個對象中的屬性。通常情況下,只要記 住讓變量和訪問這個變量的方法放在一起,將一個類中的成員變量全部定義成私有的,只有這個類自己的方法才可以訪問到這些成員變量,這就基本上實現對象的封裝,就很容易找出要分配到這個類上的方法了,就基本上算是會面向對象的編程了。把握一個原則:把 對同一事物進行操作的方法和相關的方法放在同一個類中,把方法和它操作的數據放在同 一個類中。

    • 例如,人要在黑板上畫圓,這一共涉及三個對象:人、黑板、圓,畫圓的方法要分配給哪個對象呢?由于畫圓需要使用到圓心和半徑,圓心和半徑顯然是圓的屬性,如果將它們在類中定義成了私有的成員變量,那么,畫圓的方法必須分配給圓,它才能訪問到圓心和半徑這兩 個屬性,人以后只是調用圓的畫圓方法、表示給圓發給消息而已,畫圓這個方法不應該分配在人這個對象上,這就是面向對象的封裝性,即將對象封裝成一個高度自治和相對封閉的 個體,對象狀態(屬性)由這個對象自己的行為(方法)來讀取和改變。

    • 一個更便于理解的例子就是,司機將火車剎住了,剎車的動作是分配給司機,還是分配給火車,顯然,應該分配給火車,因為司機自身是不可能有那么大的力氣將一個火車給停下來的,只有火車自己 才能完成這一動作,火車需要調用內部的離合器和剎車片等多個器件協作才能完成剎車這個動作,司機剎車的過程只是給火車發了一個消息,通知火車要執行剎車動作而已。

  • 多態:多態是指程序中定義的引用變量所指向的具體類型和通過該引用變量發出的方法調用在編 程時并不確定,而是在程序運行期間才確定,即一個引用變量倒底會指向哪個類的實例對象, 該引用變量發出的方法調用到底是哪個類中實現的方法,必須在由程序運行期間才能決定。 因為在程序運行時才確定具體的類,這樣,不用修改源程序代碼,就可以讓引用變量綁定到 各種不同的類實現上,從而導致該引用調用的具體方法隨之改變,即不修改程序代碼就可以 改變程序運行時所綁定的具體代碼,讓程序可以選擇多個運行狀態,這就是多態性。多態性 增強了軟件的靈活性和擴展性。例如,下面代碼中的 UserDao 是一個接口,它定義引用變 量 userDao 指向的實例對象由 daofactory.getDao()在執行的時候返回,有時候指向的是 UserJdbcDao 這個實現,有時候指向的是 UserHibernateDao 這個實現,這樣,不用修改 源代碼,就可以改變 userDao 指向的具體類實現,從而導致 userDao.insertUser()方法調用 的具體代碼也隨之改變,即有時候調用的是 UserJdbcDao 的 insertUser 方法,有時候調用 的是 UserHibernateDao 的 insertUser 方法:
    UserDao userDao =daofactory.getDao(); userDao.insertUser(user);
    比喻:人吃飯,你看到的是左手,還是右手?

2. 面向對象的一道繼承題目

Base類

package com.java.oop.test1;

public class Base {

    private String baseName = "base";
    private static String testName = "TestBase";

    public Base() {
        callName();
    }

    public void callName() {
        System.out.println(baseName);
    }
}

Sub類

package com.java.oop.test1;

public class Sub extends Base {

    private String baseName = "sub";
    private static String testName = "TestSub";

    public void callName() {
        System.out.println(baseName);
    }

    public static void main(String[] args) {
        Base base = new Sub();
    }
}

輸出結果
null
參考資料
類加載順序

3. Java 中的方法重寫 (Overriding) 和方法重載 (Overloading) 是什么意思?

  1. 重載
    • 重載 Overload 表示同一個類中可以有多個名稱相同的方法,但這些方法的參數列表各不相同(即參數個數或類 型不同)。
    • 至于 Overloaded的方法是否可以改變返回值的類型這個問題,要看你倒底想問什么呢?這個題目很模糊。如果 幾個Overloaded 的方法的參數列表不一樣,它們的返回者類型當然也可以不一樣。但我估計你想問的問題是:如果兩個方法的參數列表完全一樣,是否可以讓它們的返回值不同來實現重載 Overload。這是不行的,我們可以用反證法來說明這個問題,因為我們有時候調用一個方法時也可以不定義返回結果變量,即不要關心其返 回結果,例如,我們調用 map.remove(key) 方法時,雖然 remove 方法有返回值,但是我們通常都不會定義接收返回結果的變量,這時候假設該類中有兩個名稱和參數列表完全相同的方法,僅僅是返回類型不同,Java 就無法確定編程者倒底是想調用哪個方法了,因為它無法通過返回結果類型來判斷。
    • Overload 對我們來說可能比較熟悉,可以翻譯為重載,它是指我們可以定義一些名稱相同的方法,通過定義不 同的輸入參數來區分這些方法,然后再調用時,VM 就會根據不同的參數樣式,來選擇合適的方法執行。在使用 重載要注意以下的幾點:
      • 在使用重載時只能通過不同的參數樣式。例如,不同的參數類型,不同的參數個數,不同的參數順序(當然,同一方法內的幾個參數類型必須不一樣,例如可以是 fun(int,float) ,但是不能 fun(int,int)) ;
      • 不能通 過訪問權限、返回類型、拋出的異常進行重載;
      • 方法的異常類型和數目不會對重載造成影響;
      • 對于繼承來說,如果某一方法在父類中是訪問權限是 priavte,那么就不能在子類對其進行重載,如果定義的 話,也只是定義了一個新方法,而不會達到重載的效果。
  2. 重寫
    • 重寫 Override表示子類中的方法可以與父類中的某個方法的名稱和參數完全相同,通過子類創建的實例對象調 用這個方法時,將調用子類中的定義方法,這相當于把父類中定義的那個完全相同的方法給覆蓋了,這也是面向 對象編程的多態性的一種表現。子類覆蓋父類的方法時,只能比父類拋出更少的異常,或者是拋出父類拋出的異 常的子異常,因為子類可以解決父類的一些問題,不能比父類有更多的問題。子類方法的訪問權限只能比父類的 更大,不能更小。如果父類的方法是 private 類型,那么,子類則不存在覆蓋的限制,相當于子類中增加了一個 全新的方法。
    • Override 可以翻譯為覆蓋,從字面就可以知道,它是覆蓋了一個方法并且對其重寫,以求達到不同的作用。對我 們來說最熟悉的覆蓋就是對接口方法的實現,在接口中一般只是對方法進行了聲明,而我們在實現時,就需要實 現接口聲明的所有方法。除了這個典型的用法以外,我們在繼承中也可能會在子類覆蓋父類中的方法。
    • 在覆蓋要 注意以下的幾點:
      • 覆蓋的方法的標志必須要和被覆蓋的方法的標志完全匹配,才能達到覆蓋的效果;
      • 覆蓋的方法的返回值必須和被覆蓋的方法的返回一致;
      • 覆蓋的方法所拋出的異常必須和被覆蓋方法的所拋出的異常一致,或者是其子類;
      • 被覆蓋的方法不能為private,否則在其子類中只是新定義了一個方法,并沒有對其進行覆蓋。

4. Java 中,什么是構造函數?什么是構造函數重載?什么是復制構造函數?

當新對象被創建的時候,構造函數會被調用。每一個類都有構造函數。在程序員沒有給類提供構造函數的情況 下,Java 編譯器會為這個類創建一個默認的構造函數。

Java 中構造函數重載和方法重載很相似。可以為一個類創建多個構造函數。每一個構造函數必須有它自己唯一的 參數列表。

Java 不支持像 C++ 中那樣的復制構造函數,這個不同點是因為如果你不自己寫構造函數的情況下,Java不會創 建默認的復制構造函數。

5. 構造器 Constructor 是否可被 Override?

構造器 Constructor 不能被繼承,因此不能重寫 Override,但可以被重載 Overload。

6. Java 支持多繼承么?

不支持,Java 不支持多繼承。每個類都只能繼承一個類,但是可以實現多個接口。

7. 接口和抽象類的區別是什么?

Java 提供和支持創建抽象類和接口。它們的實現有共同點,不同點在于:

接口中所有的方法隱含的都是抽象的。而抽象類則可以同時包含抽象和非抽象的方法。

類可以實現很多個接口,但是只能繼承一個抽象類

類如果要實現一個接口,它必須要實現接口聲明的所有方法。但是,類可以不實現抽象類聲明的所有方法,當然,在這種情況下,類也必須得聲明成是抽象的。

抽象類可以在不提供接口方法實現的情況下實現接口。

Java 接口中聲明的變量默認都是 final 的。抽象類可以包含非 final 的變量。

Java 接口中的成員函數默認是 public 的。抽象類的成員函數可以是 private,protected 或者是 public 。

接口是絕對抽象的,不可以被實例化。抽象類也不可以被實例化,但是,如果它包含 main 方法的話是可以被調用的。

8. 下列說法正確的有()?

A.class 中的 constructor 不可省略
B.constructor 必須與 class 同名,但方法不能與class同名 C.constructor在一個對象被 new 時執行
D.一個 class 只能定義一個 constructor

答案:C
解析:這里可能會有誤區,其實普通的類方法是可以和類名同名的,和構造方法唯一的區分就是,構造方法沒有
返回值。

9. Java 接口的修飾符可以為?

A. private
B. protected
C. final
D. abstract

答案:CD 解析:接口很重要,為了說明情況,這里稍微啰嗦點:

(1)接口用于描述系統對外提供的所有服務,因此接口中的成員常量和方法都必須是公開(public)類型的,確保外部 使用者能訪問它們;

(2)接口僅僅描述系統能做什么,但不指明如何去做,所以接口中的方法都是抽象(abstract)方法;

(3)接口不涉及和任何具體實例相關的細節,因此接口沒有構造方法,不能被實例化,沒有實例變量,只有靜態(static)變量;

(4)接口中的變量是所有實現類共有的,既然共有,肯定是不變的東西,因為變化的東西也不能夠算共有。所以變量是不可變(final)類型,也就是常量了。

(5) 接口中不可以定義變量。如果接口可以定義變量,但是接口中的方法又都是抽象的,在接口中無法通過行為來修改屬性。有的人會說了,沒有關系,可以通過實現接口的對象的行為來修改接口中的屬性。這當然沒有問題,但是考慮這樣的情況。如果接口 A中有一個public 訪問權限的靜態變量 a。按照 Java 的語義,我們可以不 通過實現接口的對象來訪問變量 a,通過 A.a = xxx; 就可以改變接口中的變量 a的值了。正如抽象類中是可以這 樣做的,那么實現接口 A 的所有對象也都會自動擁有這一改變后的 a的值了,也就是說一個地方改變了 a,所有這些對象中 a的值也都跟著變了。這和抽象類有什么區別呢,怎么體現接口更高的抽象級別呢,怎么體現接口提供的統一的協議呢,那還要接口這種抽象來做什么呢?所以接口中不能出現變量,如果有變量,就和接口提供的統一的抽象這種思想是抵觸的。所以接口中的屬性必然是常量,只能讀不能改,這樣才能為實現接口的對象提供 一個統一的屬性。

通俗的講,你認為是要變化的東西,就放在你自己的實現中,不能放在接口中去,接口只是對一類事物的屬性和行為更高層次的抽象。對修改關閉,對擴展(不同的實現implements)開放,接口是對開閉原則的一種體現。
所以:
接口的方法默認是 public abstract;
接口中不可以定義變量即只能定義常量(加上final修飾就會變成常量)。所以接口的屬性默認是 public static final 常量,且必須賦初值。
注意:final 和 abstract 不能同時出現。

10. 下面是 People 和 Child 類的定義和構造方法,每個構造方法都輸出編號。在 執行 new Child("mike") 的時候都有哪些構造方法被順序調用?請選擇輸出結果

class People { String name;
public People() { System.out.print(1);
}
public People(String name) { System.out.print(2); this.name = name;
} }
class Child extends People { People father;
public Child(String name) { System.out.print(3);
this.name = name;
father = new People(name + ":F");
}
public Child() { System.out.print(4);
} }

A. 312 B. 32 C. 432 D. 132
答案:D

解析:
考察的又是父類與子類的構造函數調用次序。在 Java中,子類的構造過程中必須調用其父類的構造函數,是因為有繼承關系存在時,子類要把父類的內容繼承下來。但如果父類有多個構造函數時,該如何選擇調用呢?

第一個規則:子類的構造過程中,必須調用其父類的構造方法。一個類,如果我們不寫構造方法,那么編譯器會幫我們加上一個默認的構造方法(就是沒有參數的構造方法),但是如果你自己寫了構造方法,那么編譯器就不會給你添加了,所以有時候當你 new一個子類對象的時候,肯定調用了子類的構造方法,但是如果在子類構造方法中我們并沒有顯示的調用基類的構造方法,如:super(); 這樣就會調用父類沒有參數的構造方法。

第二個規則:如果子類的構造方法中既沒有顯示的調用基類構造方法,而基類中又沒有無參的構造方法,則編譯出錯,所以,通常我們需要顯示的:super(參數列表),來調用父類有參數的構造函數,此時無參的構造函數就不會被調用。

總之,一句話:
子類沒有顯示調用父類構造函數,不管子類構造函數是否帶參數都默認調用父類無參的構造函數,若父類沒有則編譯出錯。

12. 兩個對象值相同(x.equals(y) == true),但卻可有不同的 hash code,這句 話對不對?

答:不對,如果兩個對象 x 和 y 滿足 x.equals(y) == true,它們的哈希碼(hash code)應當相同。

Java 對于 eqauls 方法和hashCode方法是這樣規定的:

(1)如果兩個對象相同(equals 方法返回 true ),那么它們的 h ashCode 值一定要相同;

(2)如果兩個對象的 hashCode相同,它們并不一定相同。當然,你未必要按照要求 去做,但是如果你違背了上述原則就會發現在使用容器時,相同的對象可以出現在 Set 集合中,同時增加新元素 的效率會大大下降(對于使用哈希存儲的系統,如果哈希碼頻繁的沖突將會造成存取性能急劇下降)。

補充:關于 equals 和 hashCode 方法,很多 Java 程序都知道,但很多人也就是僅僅知道而已,在 Joshua Bl och 的大作《Effective Java》(很多軟件公司,《Effective Java》、《Java 編程思想》以及《重構:改善既 有代碼質量》是 Java 程序員必看書籍,如果你還沒看過,那就趕緊去亞馬遜買一本吧)中是這樣介紹 equals 方 法的:

首先 equals 方法必須滿足
自反性(x.equals(x)必須返回true)、
對稱性(x.equals(y) 返回true時,y.e quals(x) 也必須返回 true)、
傳遞性(x.equals(y)和y.equals(z)都返回 true 時,x.equals(z)也必須返回tru e)
和一致性(當x和y引用的對象信息沒有被修改時,多次調用x.equals(y)應該得到同樣的返回值),
而且對于 任何非 null 值的引用 x,x.equals(null) 必須返回 false。

實現高質量的 equals 方法的訣竅包括:

  1. 使用 == 操作符檢查“參數是否為這個對象的引用”;
  2. 使用 instanceof 操作符檢查“參數是否為正確的類型”;
  3. 對于類中的關鍵屬性,檢查參數傳入對象的屬性是否與之相匹配;
  4. 編寫完 equals 方法后,問自己它是否滿足對稱性、傳遞性、一致性;
  5. 重寫 equals 時總是要重寫 hashCode;
  6. 不要將 equals 方法參數中的 Object 對象替換為其他的類型,在重寫時不要忘掉 @Override 注解。

13. 接口是否可繼承(extends)接口? 抽象類是否可實現(implements)接 口? 抽象類是否可繼承具體類(concrete class)?

答:接口可以繼承接口。抽象類可以實現(implements)接口,抽象類可繼承具體類,但前提是具體類必須有明確 的構造函數。

14. 指出下面程序的運行結果:

class A{
static{ 
    System.out.print("1");
}
public A(){ 
    System.out.print("2");
    } 
}

class B extends A{

static{ 
    System.out.print("a");
}

public B(){ 
    System.out.print("b");
}
}


public class Hello{
public static void main(String[] args){ 
    A ab = new B();
    ab = new B();
} }

答:執行結果:1a2b2b。創建對象時構造器的調用順序是:先初始化靜態成員,然后調用父類構造器,再初始化非靜態成員,最后調用自身構造器。

15. Class.forName(String className)這個方法的作用

答:通過類的全名獲得該類的類對象

16. 什么是 AOP 和 OOP,IOC 和 DI 有什么不同?

答:
1)面向對象編程(Object Oriented Programming,OOP,面向對象程序設計)是一種計算機編程架構。AO P 是 OOP 的延續,是 Aspect Oriented Programming 的縮寫,意思是面向切面編程。 將通用需求功能從不 相關類之中分離出來;同時,能夠使得很多類共享一個行為,一旦行為發生變化,不必修改很多類,只要修改這 個行為就可以。AOP 就是這種實現分散關注的編程方法,它將“關注”封裝在“方面”中

2)控制反轉 IOC(Inversion of Control) 控制指的就是程序相關類之間的依賴關系.傳統觀念設計中,通常由調用者來創建被調用者的實例, 在 Spring里,創建被調用者的工作不再由調用者來完成,而是由 Spring 容器 完成,依賴關系被反轉了,稱為控制反轉,目的是為了獲得更好的擴展性和良好的可維護性。依賴注入(Depend ency injection)創建被調用者的工作由 Spring 容器完成,然后注入調用者,因此也稱依賴注入。控制反轉和依 賴注入是同一個概念。

16. 判斷下列語句是否正確,如果有錯誤,請指出錯誤所在?

interface A{
int add(final A a);
}
class B implements A{
long add(final A a){
return this.hashCode() + a.hashCode(); }
}

答:返回值不是 long 類型

17. 靜態變量和實例變量的區別?

  • 在語法定義上的區別:靜態變量前要加static關鍵字,而實例變量前則不加。

  • 在程序運行時的區別:實例變量屬于某個對象的屬性, 必須創建了實例對象(比如 new 一個), 其中的實例變量才會被分配空間,才能使用這個實例變量. 靜態變量不屬于某個實例對象,而是屬于類,所以也稱為類變量, 只要程序加載了類的字節碼,不用創建任何實例對象,靜態變量就會被分配空間, 靜態變量就可以被使用了.

  • 總之,實例變量必須創建對象后才可以通過這個對象來使用,靜態變量則可以直接使用類名來引用.

例如, 對于下面的程序, 無論創建多少個實例對象, 永遠都只分配了一個staticVar變量, 并且每創建一個實例對象, 這個staticVar就會加; 但是, 每創建一個實例對象, 就會分配一個instanceVar, 即可能分配多個instanceVar, 并且每個instanceVar的值都只自加了1次.

public class VariantTest{

        public static int staticVar = 0;
        public int instanceVar = 0;

        public VariantTest(){
               staticVar++;
               instanceVar++;
               System.out.println(“staticVar=” + staticVar + ”,instanceVar=”+ instanceVar);
        }
}

18. 一個類是由哪些變量構成的?

  • Local variable 本地變量
  • instance variables 實例變量
  • class variables 類變量

Local variable
在方法體, 構造體內部定義的變量 在方法結束的時候就被摧毀

instance variables
在類里但是不在方法里 在類被載入的時候被實例化

class variables
在類里但在方法外, 加了 static 關鍵字. 也可以叫做靜態變量

19. java 中實現多態的機制是什么?

靠的是父類或接口定義的引用變量可以指向子類或具體實現類的實例對象,而程序調用的方法在運行期才動態綁定,就是引用變量所指向的具體實例對象的方法,也就是內存里正在運行的那個對象的方法,而不是引用變量的類型中定義的方法。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容