Java常見面試題匯總-----------Java基礎(chǔ)(抽象類和接口、構(gòu)造方法、內(nèi)部類、枚舉類)

7. 抽象類和接口的比較

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

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

7.1. 抽象類與接口的比較

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

7.2.新版本中的修改

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

8. 構(gòu)造方法,構(gòu)造方法重載,什么是復(fù)制構(gòu)造方法?

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

9. Java內(nèi)部類

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

9.1、成員內(nèi)部類

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

9.2、局部內(nèi)部類

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

9.3、匿名內(nèi)部類

??1、匿名內(nèi)部類是沒有訪問修飾符的,也是唯一一種沒有構(gòu)造方法的類。
??2、new 匿名內(nèi)部類,這個類首先是要存在的。
??3、注意當(dāng)所在方法的形參需要被匿名內(nèi)部類使用,那么這個形參就必須為final。

9.4、靜態(tài)內(nèi)部類(靜態(tài)嵌套類)

??使用static修飾的內(nèi)部類我們稱之為靜態(tài)內(nèi)部類,或者稱之為嵌套內(nèi)部類。靜態(tài)內(nèi)部類與非靜態(tài)內(nèi)部類之間存在一個最大的區(qū)別,我們知道非靜態(tài)內(nèi)部類在編譯完成之后會隱含地保存著一個引用,該引用是指向創(chuàng)建它的外圍類,但是靜態(tài)內(nèi)部類卻沒有。沒有這個引用就意味著:
??1、它的創(chuàng)建是不需要依賴于外圍類的。
??2、它不能使用任何外圍類的非static成員變量和方法。

10. 枚舉類

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

10.1、枚舉類方法:

??valueOf()方法:它的作用是傳來一個字符串,然后將它轉(zhuǎn)變?yōu)閷?yīng)的枚舉變量。前提是你傳的字符串和定義枚舉變量的字符串一模一樣,區(qū)分大小寫。如果你傳了一個不存在的字符串,那么會拋出異常。
??values()方法:這個方法會返回包括所有枚舉變量的數(shù)組,可以方便的用來做循環(huán)。
??name()方法:它和toString()方法的返回值一樣,這兩個方法的默認(rèn)實現(xiàn)是一樣的,唯一的區(qū)別是,你可以重寫toString方法。name變量就是枚舉變量的字符串形式。

10.2、枚舉變量方法:

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容