Java常見面試題彙總-----------Java基礎(抽象類和接口、構造方法、內部類、枚舉類)

7. 抽象類和接口的比較

  1、什麼是抽象類? 就是對類更高的抽象。抽象類作爲多個子類的共同父類。它所體現的是一種模版設計,抽象類作爲多個子類的父類,可以把它理解爲系統實現過程中的中間產品,這個中間產品已經實現了系統的部分功能,但是不能當成最終產品,還需要進一步的完善。
  當父類的一些方法不能確定時,可以用abstract關鍵字來修飾該方法【抽象方法】,用abstract來修飾該類【抽象類】。
  1)、抽象類不能被實例化。
  2)、用abstract關鍵字來修飾一個方法時,這個方法就是抽象方法,抽象方法不能有主體【即不能實現】;用abstract關鍵字來修飾一個類時,這個類就叫抽象類。
  3)、抽象類不一定要包含abstract方法。也就是說抽象類可以沒有abstract方法;但是一旦類包含了abstract方法,則這個類必須聲明爲abstract。

  2、什麼是接口? 接口是一種規範,就像現實中生產主板和內存條或者網卡的不是同一家產商,但是爲何內存或者網卡插入到主板上就能用呢,因爲他們都遵守了某種規範。然後就可以使用。雖然他們的內部實現可能完全不同。就好比在java語言中的方法內部實現你不需要關心,只需要知道這個接口是怎樣的幹嘛的就行了,直接用。既然是一種規範,那他在編程語言中就能在架構中起到非常之大的作用,在一個應用程序之間的時候,接口體現的是一耦合標準。特別是在多個應用程序需要對接的時候。接口是多個應用程序的通信標準。接口就是給出一些沒有內容的方法,封裝在一起,到某個類要使用的時候,再根據具體情況把這些方法寫出來。
  接口是更加抽象的抽象的類,抽象類裏的方法(非抽象方法,抽象類可以有非抽象方法)可以有方法體,接口裏的所有方法都沒有方法體。接口體現了程序設計的多態和高內聚低偶和的設計思想。
  1)、接口中的所有方法都不能有主體,不能被實例化。
  2)、一個類可以實現多個接口。
  3)、接口中可以有變量【但變量不能用private和protected修飾】。a、Java接口中的變量是公共的(public),靜態的(static),最終的常量(final),相當於全局常量,所以定義接口變量時必須初始化。 b、接口中的變量,本質上都是static的,不管你加不加static修飾。c、在java開發中,我們經常用的變量,定義在接口中,作爲全局變量使用。訪問形式:接口名.變量名。
  4)、一個接口不能繼承其它的類,但是可以繼承別的接口。
  5)、接口沒有方法就可以作爲一個標誌,比如可序列化的接口Serializable,沒有方法的接口稱爲空接口。

7.1. 抽象類與接口的比較

  接口和抽象類都不能實例化,他們都位於繼承樹的頂端,用於被其他類實現和繼承。接口和抽象類都可以包含抽象方法,實現接口或繼承抽象類的普通子類都必須實現這些抽象方法。
  他們的區別:
  1、屬性:接口沒有普通屬性,只有靜態屬性,並且只能用public final static 修飾(並且是默認的,就算你在接口中定義Int i = 0 它也會被隱式的加上public final static);而抽象類可以有普通屬性,可以有靜態屬性(類屬性)。
  2、方法:接口中的方法都沒有方法體並且都是默認使用public abstracrt 修飾,不能定義靜態方法。 而抽象類可以有普通方法,也可以沒有抽象方法,也可以定義靜態方法。
  3、構造函數:接口中沒有構造器,抽象類中可以有構造器, 但是它不能用於new 對象 而是用於子類調用來初始化抽象類的操作。
  4、初始化塊:接口中不能包含初始化塊,而抽象方法中可以包含初始化塊。
  5、一個類只能有一個直接父類,包括抽象類,而類可以實現多個接口,彌補了java不能多繼承的不足。

7.2.新版本中的修改

  1、JDK1.8以前,抽象類中的方法默認訪問權限protected,JDK1.8時默認訪問權限default。
  2、JDK1.8接口,增加了default和static方法,這2個都可以有方法體的,default方法屬於實例,static方法屬於類(接口),接口的靜態方法不會被繼承,靜態變量會被繼承。
  3、JDK1.8,如果接口只有一個抽象方法自動變成函數式接口,可以用使用@FunctionInterface註解,接口有FunctionInterface註解只能有一個抽象方法。
  4、從java8開始接口裏可以有靜態方式,用static修飾,但是接口裏的靜態方法的修飾符只能是public,且默認是public。調用時(接口名.方法名)。
  5、java8裏,除了可以在接口裏寫靜態方法,還可以寫非靜態方法,但是必須用default修飾,且只能是public,默認也是public。

8. 構造方法,構造方法重載,什麼是複製構造方法?

  構造方法是類的一種特殊方法,它的主要作用是完成對新對象的初始化。
  1、方法名和類名相同,沒有返回值。
  2、在創建(new)一個類的新對象時,系統會自動的調用該類的構造方法完成對新對象的初始化。
  3、普通方法可以和類名相同,和構造方法唯一的區別在於,構造方法沒有返回值(注意是沒有不是void)。
  4、定義類的時候,若沒有自定義構造方法,則會有一個默認的無參構造方法,若自定義了構造方法,則沒有無參構造方法。
  5、構造方法不能被繼承,構造方法只能被顯式或者隱式地調用。
  6、子類的構造方法總是先調用父類的構造方法,如果子類的構造方法沒有顯式地指出使用父類的哪個構造方法,子類則默認調用父類的無參構造方法(此時若父類自定義了構造方法,而子類又沒有用super則會報錯)。
  7、Java不支持像C++中那樣的複製構造方法(沒有這個概念),但是在Object類中有一個clone()方法。
  Protected Object clone() throws CloneNotSupportedException:創建並返回此對象的一個副本。“副本”的準確含義可能依賴於對象的類。
  這樣做的目的是,對於任何對象 x,表達式:x.clone() != x爲true,表達式:x.clone().getClass() == x.getClass()也爲true,但這些並非必須要滿足的要求。一般情況下:x.clone().equals(x)爲true,但這並非必須要滿足的要求。
  首先,使用這個方法的類必須實現java.lang.Cloneable接口,否則會拋出CloneNotSupportedException異常。Cloneable接口中不包含任何方法,所以實現它時只要在類聲明中加上implements語句即可。
  第二個比較特殊的地方在於這個方法是protected修飾的,覆寫clone()方法的時候需要寫成public,才能讓類外部的代碼調用。
  按照慣例,返回的對象應該通過調用super.clone獲得。如果一個類及其所有的超類(Object除外)都遵守此約定,則 x.clone().getClass() == x.getClass()。
  按照慣例,此方法返回的對象應該獨立於該對象(正被複制的對象)。要獲得此獨立性,在super.clone返回對象之前,有必要對該對象的一個或多個字段進行修改。這通常意味着要複製包含正在被複制對象的內部“深層結構”的所有可變對象,並使用對副本的引用替換對這些對象的引用。如果一個類只包含基本字段或對不變對象的引用,那麼通常不需要修改 super.clone 返回的對象中的字段。
  也就是clone的淺拷貝和深拷貝問題,在具體使用的時候需要特別注意:
  淺拷貝:如果一個對象內部還有一個引用類型的基本變量,那麼在拷貝該對象的時候,只是在通過clone方法新產生的新對象中拷貝一個該基本類型的引用。換句話說,也就是新對象和原對象他們內部都含有一個指向同一對象的引用。
  深拷貝:拷貝對象的時候,如果對象內部含有一個引用類型的變量,那麼就會再將該引用類型的變量指向的對象複製一份,然後引用該新對象。
  8、實際上,特別需要注意的一點是,Java對象並不是由構造器創建的,而是由new運算符創建的,在程序運行時,是new運算符在堆上開闢一塊空間,然後執行對象的初始化(其中包括調用構造器),當對象創建成功,也是new運算符將對象的起始地址返回給應用程序的(並非構造器)。實際上構造器的作用是在對象創建的時候進行類中成員變量的初始化,而絕非創建對象,構造器也沒有返回值。因此程序執行的順序是,先創建對象,然後求解構造器所有形參表達式的值(若形參表達式的計算出現異常,則不會調用構造器方法),最後調用構造器對對象進行初始化。

9. Java內部類

  內部類(inner class)是定義在另一個類內部的類。
  使用內部類的原因有三個:
  1)、內部類方法可以訪問該類定義所在的作用域中的數據,包括私有數據。
  2)、內部類能夠隱藏起來,不被同一個包中的其他的類所見。
  3)、想要定義一個回調函數,且不想編寫大量代碼時,使用匿名內部類比較便捷。
  4)、內部類有四種:成員內部類、局部內部類、靜態內部類、匿名內部類

9.1、成員內部類

  成員內部類也是最普通的內部類,它是外圍類的一個成員,所以他是可以無限制的訪問外圍類的所有成員屬性和方法,儘管是private的,但是外圍類要訪問內部類的成員屬性和方法則需要通過內部類實例來訪問。如果在內部類中定義有和外部類同名的實例變量,訪問:OuterClass.this.outerMember;
  在成員內部類中要注意兩點,第一:成員內部類中不能存在任何static的變量和方法(因爲需要先創建外部類,才能創建自己,可以聲明static的常量);第二:成員內部類是依附於外圍類的,所以只有先創建了外圍類才能夠創建內部類。

9.2、局部內部類

  局部內部類,它是嵌套在方法和作用域內的,對於這個類的使用主要是應用與解決比較複雜的問題,想創建一個類來輔助我們的解決方案,但又不希望這個類是公共可用的,所以就產生了局部內部類,局部內部類和成員內部類一樣被編譯,只是它的作用域發生了改變,它只能在該方法和屬性中被使用,出了該方法和屬性就會失效。
  局部內部類可以訪問的外部類的成員根據所在方法體不同。如果在靜態方法中:可以訪問外部類中所有靜態成員,包含私有;如果在實例方法中:可以訪問外部類中所有的成員,包含私有。局部內部類可以訪問所在方法中定義的局部變量,但是要求局部變量必須使用final修飾。

9.3、匿名內部類

  1、匿名內部類是沒有訪問修飾符的,也是唯一一種沒有構造方法的類。
  2、new 匿名內部類,這個類首先是要存在的。
  3、注意當所在方法的形參需要被匿名內部類使用,那麼這個形參就必須爲final。

9.4、靜態內部類(靜態嵌套類)

  使用static修飾的內部類我們稱之爲靜態內部類,或者稱之爲嵌套內部類。靜態內部類與非靜態內部類之間存在一個最大的區別,我們知道非靜態內部類在編譯完成之後會隱含地保存着一個引用,該引用是指向創建它的外圍類,但是靜態內部類卻沒有。沒有這個引用就意味着:
  1、它的創建是不需要依賴於外圍類的。
  2、它不能使用任何外圍類的非static成員變量和方法。

10. 枚舉類

  (1)、定義一個枚舉類,使用的是enum關鍵字而不是class。
  (2)、枚舉類的實例一定是有限多個(可枚舉的),所有的enum變量必須定義在枚舉類的第一行,用逗號隔開,定義完所有的變量後,以分號結束,如果只有枚舉變量,而沒有自定義變量,分號可以省略。枚舉變量最好大寫,在其他類中使用enum變量的時候,只需要【類名.變量名】就可以了,和使用靜態變量一樣。
  Enum變量默認添加public static final修飾。
  (3)enum類可以把它看成一個普通類,可以有構造器,成員方法,成員變量。當然和普通類也有一定的區別:
  枚舉類的構造方法默認使用private修飾,且只能使用private修飾;
  枚舉類默認繼承自Enum類,所以不能繼承其他類,Enum類實現了Serializable、Comparable接口;
  枚舉類默認使用final修飾,因此不能派生子類。如果需要擴展enum中的元素,在一個接口的內部,創建實現該接口的枚舉,以此將元素進行分組。達到將枚舉元素進行分組的目的。
  (4)、switch()參數可以使用enum
  (5)、enum允許程序員爲eunm實例編寫方法。所以可以爲每個enum實例賦予各自不同的行爲。
  (6)、常用方法

10.1、枚舉類方法:

  valueOf()方法:它的作用是傳來一個字符串,然後將它轉變爲對應的枚舉變量。前提是你傳的字符串和定義枚舉變量的字符串一模一樣,區分大小寫。如果你傳了一個不存在的字符串,那麼會拋出異常。
  values()方法:這個方法會返回包括所有枚舉變量的數組,可以方便的用來做循環。
  name()方法:它和toString()方法的返回值一樣,這兩個方法的默認實現是一樣的,唯一的區別是,你可以重寫toString方法。name變量就是枚舉變量的字符串形式。

10.2、枚舉變量方法:

  toString()方法:該方法直接返回枚舉定義枚舉變量的字符串。
  ordinal()方法:默認情況下,枚舉類會給所有的枚舉變量一個默認的次序,該次序從0開始,類似於數組的下標。而.ordinal()方法就是獲取這個次序(或者說下標)。枚舉類中枚舉變量的次序可以自定義。
  compareTo()方法(枚舉類實現了Comparable接口):該方法用來比較兩個枚舉變量的”大小”,實際上比較的是兩個枚舉變量的次序,返回兩個次序相減後的結果,如果爲負數,就證明變量1”小於”變量2 (變量1.compareTo(變量2),返回【變量1.ordinal()- 變量2.ordinal()】)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章