此文已經(jīng)同步至個(gè)人站點(diǎn)博客,點(diǎn)擊下方鏈接可以體驗(yàn)更加閱讀模式:
《java題庫(kù)》
一.面向?qū)ο蟮幕靖拍?/h4>
1.解釋下多態(tài)性(polymorphism),封裝性(encapsulation),內(nèi)聚(cohesion)以及耦合(coupling)
抽象:抽象是將一類對(duì)象的共同特征總結(jié)出來(lái)構(gòu)造類的過(guò)程,包括數(shù)據(jù)抽象和行為抽象兩方面。抽象只關(guān)注對(duì)象有哪些屬性和行為,并不關(guān)注這些行為的細(xì)節(jié)是什么。
封裝:通常認(rèn)為封裝是把數(shù)據(jù)和操作數(shù)據(jù)的方法綁定起來(lái),對(duì)數(shù)據(jù)的訪問(wèn)只能通過(guò)已定義的接口。面向?qū)ο蟮谋举|(zhì)就是將現(xiàn)實(shí)世界描繪成一系列完全自治、封閉的對(duì)象。我們?cè)陬愔芯帉?xiě)的方法就是對(duì)實(shí)現(xiàn)細(xì)節(jié)的一種封裝;我們編寫(xiě)一個(gè)類就是對(duì)數(shù)據(jù)和數(shù)據(jù)操作的封裝??梢哉f(shuō),封裝就是隱藏一切可隱藏的東西,只向外界提供最簡(jiǎn)單的編程接口(可以想想普通洗衣機(jī)和全自動(dòng)洗衣機(jī)的差別,明顯全自動(dòng)洗衣機(jī)封裝更好因此操作起來(lái)更簡(jiǎn)單;我們現(xiàn)在使用的智能手機(jī)也是封裝得足夠好的,因?yàn)閹讉€(gè)按鍵就搞定了所有的事情)。
好處有三:1、隱藏了類的實(shí)現(xiàn);2、操作簡(jiǎn)單;3、提高對(duì)象數(shù)據(jù)的安全性
內(nèi)聚:進(jìn)行架構(gòu)設(shè)計(jì)時(shí)的內(nèi)聚高低是指,設(shè)計(jì)某個(gè)模塊或者關(guān)注點(diǎn)時(shí),模塊或關(guān)注點(diǎn)內(nèi)部的一系列相關(guān)功能的相關(guān)程度的高低。高內(nèi)聚提供了更好的可維護(hù)性和可復(fù)用性。而低內(nèi)聚的模塊則表名模塊直接的依賴程度高,那么一旦修改了該模塊依賴的對(duì)象則無(wú)法使用該模塊,必須也進(jìn)行相應(yīng)的修改才可以繼續(xù)使用。
耦合:簡(jiǎn)單地說(shuō),軟件工程中對(duì)象之間的耦合度就是對(duì)象之間的依賴性。指導(dǎo)使用和維護(hù)對(duì)象的主要問(wèn)題是對(duì)象之間的多重依賴性。對(duì)象之間的耦合越高,維護(hù)成本越高。因此對(duì)象的設(shè)計(jì)應(yīng)使類和構(gòu)件之間的耦合最小。耦合性是程序結(jié)構(gòu)中各個(gè)模塊之間相互關(guān)聯(lián)的度量。它取決于各個(gè)模塊之間的接口的復(fù)雜程度、調(diào)用模塊的方式以及哪些信息通過(guò)接口。
耦合可以分為以下幾種,它們之間的耦合度由高到低排列如下:
- 內(nèi)容耦合。當(dāng)一個(gè)模塊直接修改或操作另一個(gè)模塊的數(shù)據(jù)時(shí),或一個(gè)模塊不通過(guò)正常入口而轉(zhuǎn)入另一個(gè)模塊時(shí),這樣的耦合被稱為內(nèi)容耦合。內(nèi)容耦合是最高程度的耦合,應(yīng)該避免使用之。
- 公共耦合。兩個(gè)或兩個(gè)以上的模塊共同引用一個(gè)全局?jǐn)?shù)據(jù)項(xiàng),這種耦合被稱為公共耦合。在具有大量公共耦合的結(jié)構(gòu)中,確定究竟是哪個(gè)模塊給全局變量賦了一個(gè)特定的值是十分困難的。
-
外部耦合 。一組模塊都訪問(wèn)同一全局簡(jiǎn)單變量而不是同一全局?jǐn)?shù)據(jù)結(jié)構(gòu),而且不是通過(guò)參數(shù)表傳遞該全局變量的信息,則稱之為外部耦合。
控制耦合 。一個(gè)模塊通過(guò)接口向另一個(gè)模塊傳遞一個(gè)控制信號(hào),接受信號(hào)的模塊根據(jù)信號(hào)值而進(jìn)行適當(dāng)?shù)膭?dòng)作,這種耦合被稱為控制耦合。
標(biāo)記耦合 。若一個(gè)模塊A通過(guò)接口向兩個(gè)模塊B和C傳遞一個(gè)公共參數(shù),那么稱模塊B和C之間存在一個(gè)標(biāo)記耦合。 - 數(shù)據(jù)耦合。模塊之間通過(guò)參數(shù)來(lái)傳遞數(shù)據(jù),那么被稱為數(shù)據(jù)耦合。數(shù)據(jù)耦合是最低的一種耦合形式,系統(tǒng)中一般都存在這種類型的耦合,因?yàn)闉榱送瓿梢恍┯幸饬x的功能,往往需要將某些模塊的輸出數(shù)據(jù)作為另一些模塊的輸入數(shù)據(jù)。
-
非直接耦合 。兩個(gè)模塊之間沒(méi)有直接關(guān)系,它們之間的聯(lián)系完全是通過(guò)主模塊的控制和調(diào)用來(lái)實(shí)現(xiàn)的。
高內(nèi)聚&低耦合
2.多態(tài)的用途和實(shí)現(xiàn)原理(★★★)
1、編譯時(shí)多態(tài)(又稱靜態(tài)多態(tài))
2、運(yùn)行時(shí)多態(tài)(又稱動(dòng)態(tài)多態(tài))
重載(overload)就是編譯時(shí)多態(tài)的一個(gè)例子,編譯時(shí)多態(tài)在編譯時(shí)就已經(jīng)確定,運(yùn)行時(shí)運(yùn)行的時(shí)候調(diào)用的是確定的方法。
我們通常所說(shuō)的多態(tài)指的都是運(yùn)行時(shí)多態(tài),也就是編譯時(shí)不確定究竟調(diào)用哪個(gè)具體方法,一直延遲到運(yùn)行時(shí)才能確定。這也是為什么有時(shí)候多態(tài)方法又被稱為延遲方法的原因。
- 多態(tài)通常有兩種實(shí)現(xiàn)方法:
1、子類繼承父類(extends)
2、子類實(shí)現(xiàn)接口(implements) -
多態(tài)最大的用途
個(gè)人認(rèn)為在于對(duì)設(shè)計(jì)和架構(gòu)的復(fù)用,更進(jìn)一步來(lái)說(shuō),《設(shè)計(jì)模式》中提倡的針對(duì)接口編程而不是針對(duì)實(shí)現(xiàn)編程就是充分利用多態(tài)的典型例子。定義功能和組件時(shí)定義接口,實(shí)現(xiàn)可以留到之后的流程中。同時(shí)一個(gè)接口可以有多個(gè)實(shí)現(xiàn),甚至于完全可以在一個(gè)設(shè)計(jì)中同時(shí)使用一個(gè)接口的多種實(shí)現(xiàn)。 -
多態(tài)實(shí)現(xiàn)原理
多態(tài)允許具體訪問(wèn)時(shí)實(shí)現(xiàn)方法的動(dòng)態(tài)綁定。Java對(duì)于動(dòng)態(tài)綁定的實(shí)現(xiàn)主要依賴于方法表,通過(guò)繼承和接口的多態(tài)實(shí)現(xiàn)有所不同。
繼承:在執(zhí)行某個(gè)方法時(shí),在方法區(qū)中找到該類的方法表,再確認(rèn)該方法在方法表中的偏移量,找到該方法后如果被重寫(xiě)則直接調(diào)用,否則認(rèn)為沒(méi)有重寫(xiě)父類該方法,這時(shí)會(huì)按照繼承關(guān)系搜索父類的方法表中該偏移量對(duì)應(yīng)的方法。
接口:Java 允許一個(gè)類實(shí)現(xiàn)多個(gè)接口,從某種意義上來(lái)說(shuō)相當(dāng)于多繼承,這樣同一個(gè)接口的的方法在不同類方法表中的位置就可能不一樣了。所以不能通過(guò)偏移量的方法,而是通過(guò)搜索完整的方法表。
tips:因?yàn)槊看谓涌谡{(diào)用都要搜索方法表,所以從效率上來(lái)說(shuō),接口方法的調(diào)用總是慢于類方法的調(diào)用的。
《Java 多態(tài)的實(shí)現(xiàn)機(jī)制》
《Java技術(shù)——多態(tài)的實(shí)現(xiàn)原理》
弄清了多態(tài)的原理才能搞清楚如下現(xiàn)象:
當(dāng)父類和子類有相同的成員變量,多態(tài)下訪問(wèn)的是父類的成員變量(不管是靜態(tài)還是非靜態(tài)的成員變量)。
當(dāng)子類重寫(xiě)父類方法,多態(tài)調(diào)用方法時(shí)(非靜態(tài)的成員方法)訪問(wèn)的是子類的成員方法,(靜態(tài)的成員方法)訪問(wèn)的是父類的靜態(tài)方法。
3.對(duì)象封裝的原則是什么?
在面向?qū)ο蟪淌皆O(shè)計(jì)方法中,封裝(英語(yǔ):Encapsulation)是指一種將抽象性函式接口的實(shí)現(xiàn)細(xì)節(jié)部份包裝、隱藏起來(lái)的方法。封裝可以被認(rèn)為是一個(gè)保護(hù)屏障,防止該類的代碼和數(shù)據(jù)被外部類定義的代碼隨機(jī)訪問(wèn)。要訪問(wèn)該類的代碼和數(shù)據(jù),必須通過(guò)嚴(yán)格的接口控制。封裝最主要的功能在于我們能修改自己的實(shí)現(xiàn)代碼,而不用修改那些調(diào)用我們代碼的程序片段。適當(dāng)?shù)姆庋b可以讓程式碼更容易理解與維護(hù),也加強(qiáng)了程式碼的安全性。
- 修改屬性的可見(jiàn)性來(lái)限制對(duì)屬性的訪問(wèn)(一般限制為private);
- 對(duì)每個(gè)值屬性提供對(duì)外的公共方法訪問(wèn),也就是創(chuàng)建一對(duì)賦取值方法,用于對(duì)私有屬性的訪問(wèn);
4.獲得一個(gè)類的類對(duì)象有哪些方式?
- 1.通過(guò)對(duì)象的getClass方法進(jìn)行獲取。這種方式需要具體的類和該類的對(duì)象,以及調(diào)用getClass方法。
- 2.任何數(shù)據(jù)類型(包括基本數(shù)據(jù)類型)都具備著一個(gè)靜態(tài)的屬性class,通過(guò)它可直接獲取到該類型對(duì)應(yīng)的Class對(duì)象。這種方式要使用具體的類,然后調(diào)用類中的靜態(tài)屬性class完成,無(wú)需調(diào)用方法,性能更好。
- 3.通過(guò)Class.forName()方法獲取。這種方式僅需使用類名,就可以獲取該類的Class對(duì)象,更有利于擴(kuò)展。
import org.junit.Test;
/**
* 演示獲取Class c對(duì)象的三種方法
*@fileName ReflectGetClass.java
*/
public class ReflectGetClass {
/**
* 法1:通過(guò)對(duì)象---對(duì)象.getClass()來(lái)獲取c(一個(gè)Class對(duì)象)
*/
@Test
public void get1(){
Person p=new Person("Jack", 23);
Class c=p.getClass();//來(lái)自O(shè)bject方法
}
/**
* 法2:通過(guò)類(類型)---任何數(shù)據(jù)類型包括(基本數(shù)據(jù)類型)都有一個(gè)靜態(tài)的屬性class ,他就是c 一個(gè)Class對(duì)象
*/
@Test
public void get2(){
Class c=Person.class;
Class c2=int.class;
}
/**
* 法3:通過(guò)字符串(類全名 )---能夠?qū)崿F(xiàn)解耦:Class.forName(str)
*/
@Test
public void get3(){
try {
Class c=Class.forName("cn.hncu.reflect.test.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
5.重載(Overload)和重寫(xiě)(Override)的區(qū)別。重載的方法能否根據(jù)返回類型進(jìn)行區(qū)分?
一、重寫(xiě)(override)
override是重寫(xiě)(覆蓋)了一個(gè)方法,以實(shí)現(xiàn)不同的功能。一般是用于子類在繼承父類時(shí),重寫(xiě)(重新實(shí)現(xiàn))父類中的方法。重寫(xiě)(覆蓋)的規(guī)則:
- 1、重寫(xiě)方法的參數(shù)列表必須完全與被重寫(xiě)的方法的相同,否則不能稱其為重寫(xiě)而是重載.
- 2、重寫(xiě)方法的訪問(wèn)修飾符一定要大于被重寫(xiě)方法的訪問(wèn)修飾符(public>protected>default>private)。
- 3、重寫(xiě)的方法的返回值必須和被重寫(xiě)的方法的返回一致;
- 4、重寫(xiě)的方法所拋出的異常必須和被重寫(xiě)方法的所拋出的異常一致,或者是其子類;
- 5、被重寫(xiě)的方法不能為private,否則在其子類中只是新定義了一個(gè)方法,并沒(méi)s有對(duì)其進(jìn)行重寫(xiě)。
- 6、靜態(tài)方法不能被重寫(xiě)為非靜態(tài)的方法(會(huì)編譯出錯(cuò))。
二、overload是重載
一般是用于在一個(gè)類內(nèi)實(shí)現(xiàn)若干重載的方法,這些方法的名稱相同而參數(shù)形式不同。
重載的規(guī)則: - 1、在使用重載時(shí)只能通過(guò)相同的方法名、不同的參數(shù)形式實(shí)現(xiàn)。不同的參數(shù)類型可以是不同的參數(shù)類型,不同的參數(shù)個(gè)數(shù),不同的參數(shù)順序(參數(shù)類型必須不一樣);
- 2、不能通過(guò)訪問(wèn)權(quán)限、返回類型、拋出的異常進(jìn)行重載;
- 3、方法的異常類型和數(shù)目不會(huì)對(duì)重載造成影響;
多態(tài)的概念比較復(fù)雜,有多種意義的多態(tài),一個(gè)有趣但不嚴(yán)謹(jǐn)?shù)恼f(shuō)法是:繼承是子類使用父類的方法,而多態(tài)則是父類使用子類的方法。一般,我們使用多態(tài)是為了避免在父類里大量重載引起代碼臃腫且難于維護(hù)。
《java編程思想》中很好的回答了不能以返回值來(lái)區(qū)分重載方法:
void f(){ }
void f(){ return 1; }
假如有int x=f(),這里是可以區(qū)分重載方法,但有時(shí)候并不需要返回值,只是調(diào)用方法,那么像這樣的f()就讓人無(wú)法理解了。
6.說(shuō)出幾條 Java 中方法重載的最佳實(shí)踐?
- a)不要重載這樣的方法:一個(gè)方法接收 int 參數(shù),而另個(gè)方法接收 Integer 參數(shù)。
- b)不要重載參數(shù)數(shù)量一致,而只是參數(shù)順序不同的方法。
- c)如果重載的方法參數(shù)個(gè)數(shù)多于 5 個(gè),采用可變參數(shù)。
二、抽象類和接口
1.抽象類和接口的區(qū)別
- 一、相似性
- 接口和抽象類都不能被實(shí)例化,它們都位于繼承樹(shù)的頂端,用于被其他類實(shí)現(xiàn)和繼承。
- 接口和抽象類都可以包含抽象方法,實(shí)現(xiàn)接口或繼承抽象類的普通子類都必須實(shí)現(xiàn)這些抽象方法。
二、接口和抽象類的區(qū)別 - 抽象類中的方法可以有方法體,就是能實(shí)現(xiàn)方法的具體功能,但是接口中的方法不行。
- 抽象類中的成員變量可以是各種類型的,而接口中的成員變量只能是 public static final (代碼里不用寫(xiě),隱式包含有)類型的。
- 接口中不能含有靜態(tài)代碼塊以及靜態(tài)方法(用 static 修飾的方法),而抽象類是可以有靜態(tài)代碼塊和靜態(tài)方法。
- 一個(gè)類只能繼承一個(gè)抽象類,而一個(gè)類卻可以實(shí)現(xiàn)多個(gè)接口。
接口是隱式抽象的,當(dāng)聲明一個(gè)接口類的時(shí)候,不必使用abstract關(guān)鍵字。
接口中每一個(gè)方法也是隱式抽象的,聲明時(shí)同樣不需要abstract關(guān)鍵字。
接口中的方法都是公有的(隱式就是public)。
接口可以繼承接口。
抽象類可以實(shí)現(xiàn)(implements)接口
抽象類可繼承具體類。
抽象類中可以有靜態(tài)的main方法。
備注:只要明白了接口和抽象類的本質(zhì)和作用,這些問(wèn)題都很好回答,你想想,如果你是java語(yǔ)言的設(shè)計(jì)者,你是否會(huì)提供這樣的支持,如果不提供的話,有什么理由嗎?如果你沒(méi)有道理不提供,那答案就是肯定的了。只有記住抽象類與普通類的唯一區(qū)別就是不能創(chuàng)建實(shí)例對(duì)象和允許有abstract方法。
2.java接口的基本概念,是否可繼承,以及優(yōu)點(diǎn)?
接口(Interface),在JAVA編程語(yǔ)言中是一個(gè)抽象類型,是抽象方法的集合。接口通常以interface來(lái)聲明。一個(gè)類通過(guò)繼承接口的方式,從而來(lái)繼承接口的抽象方法。如果一個(gè)類只由抽象方法和全局常量組成,那么這種情況下不會(huì)將其定義為一個(gè)抽象類。只會(huì)定義為一個(gè)接口,所以接口嚴(yán)格的來(lái)講屬于一個(gè)特殊的類,而這個(gè)類里面只有抽象方法和全局常量,就連構(gòu)造方法也沒(méi)有。
- 一個(gè)接口可以繼承多個(gè)接口.
interface C extends A, B {}是可以的. - 一個(gè)類可以實(shí)現(xiàn)多個(gè)接口:
class D implements A,B,C{} - 但是一個(gè)類只能繼承一個(gè)類,不能繼承多個(gè)類
class B extends A{} - 在繼承類的同時(shí),也可以繼承接口:
class E extends D implements A,B,C{}
這也正是選擇用接口而不是抽象類的原因
3、接口的優(yōu)點(diǎn)或者說(shuō)面向接口編程的思想是什么(這里要結(jié)合運(yùn)行時(shí)多態(tài)更好理解)
在系統(tǒng)分析和架構(gòu)中,分清層次和依賴關(guān)系,每個(gè)層次不是直接向其上層提供服務(wù)(即不是直接實(shí)例化在上層中),而是通過(guò)定義一組接口,僅向上層暴露其接口功能,上層對(duì)于下層僅僅是接口依賴,而不依賴具體類。
好處:首先對(duì)系統(tǒng)靈活性大有好處。當(dāng)下層需要改變時(shí),只要接口及接口功能不變,則上層不用做任何修改。甚至可以在不改動(dòng)上層代碼時(shí)將下層整個(gè)替換掉。接口體現(xiàn)的是一種規(guī)范和實(shí)現(xiàn)分離的設(shè)計(jì)哲學(xué),充分利用接口可以極好地降低程序各模塊之間的耦合,從而提高系統(tǒng)的可擴(kuò)展性和可維護(hù)性?;谶@種原則,通常推薦“面向接口”編程,而不是面向?qū)崿F(xiàn)類編程,希望通過(guò)面向接口編程來(lái)降低程序的耦合。
總的來(lái)說(shuō)就是:降低程序耦合度,提高系統(tǒng)的可擴(kuò)展性和維護(hù)性。
三、繼承
1、繼承(Inheritance)與聚合(Aggregation)的區(qū)別在哪里
2、繼承和組合之間有什么不同
- 如果存在一種IS-A的關(guān)系(比如Bee“是一個(gè)”Insect),并且一個(gè)類需要向另一個(gè)類暴露所有的方法接口,那么更應(yīng)該用繼承的機(jī)制。
- 如果存在一種HAS-A的關(guān)系(比如Bee“有一個(gè)”attack功能),那么更應(yīng)該運(yùn)用組合。
3、為什么類只能單繼承,接口可以多繼承
首先,類的多繼承有缺點(diǎn):
第一,如果一個(gè)類繼承多個(gè)父類,如果父類中的方法名如果相同,那么就會(huì)產(chǎn)生歧義。
第二,如果父類中的方法同名,子類中沒(méi)有覆蓋,同樣會(huì)產(chǎn)生上面的錯(cuò)誤。
但是接口就設(shè)計(jì)成多繼承,是因?yàn)榻涌诳梢员苊馍鲜鰡?wèn)題:
首先,接口中的只有抽象方法和靜態(tài)常量。對(duì)于一個(gè)類實(shí)現(xiàn)多個(gè)接口的情況和一個(gè)接口繼承多個(gè)接口的情況,因?yàn)榻涌谥挥谐橄蠓椒?,具體方法只能由實(shí)現(xiàn)接口的類實(shí)現(xiàn)(也是因?yàn)閷?shí)現(xiàn)類一定會(huì)覆蓋接口中的方法),在調(diào)用的時(shí)候始終只會(huì)調(diào)用實(shí)現(xiàn)類(也就是子類覆蓋的方法)的方法(不存在歧義),因此不存在 多繼承的第二個(gè)缺點(diǎn);而又因?yàn)榻涌谥挥徐o態(tài)的常量,但是由于靜態(tài)變量是在編譯期決定調(diào)用關(guān)系的,即使存在一定的沖突也會(huì)在編譯時(shí)提示出錯(cuò);而引用靜態(tài)變量一般直接使用類名或接口名,從而避免產(chǎn)生歧義,因此也不存在多繼承的第一個(gè)缺點(diǎn)。
4、存在兩個(gè)類,C 繼承 B,B 繼承 A,能將 B 轉(zhuǎn)換為 C 么?如 C = (C) B
不能轉(zhuǎn)換,測(cè)試代碼報(bào)錯(cuò):
Exception in thread "main" java.lang.ClassCastException: interfaceDemo.B cannot be cast to interfaceDemo.C
5、如果類 a 繼承類 b,實(shí)現(xiàn)接口c,而類 b 和接口 c 中定義了同名變量,請(qǐng)問(wèn)會(huì)出現(xiàn)什么問(wèn)題
四、泛型
1、泛型的存在是用來(lái)解決什么問(wèn)題?
首先需要明確泛型的概念,泛型(Generics )是把類型參數(shù)化,運(yùn)用于類、接口、方法中,可以通過(guò)執(zhí)行泛型類型調(diào)用 分配一個(gè)類型,將用分配的具體類型替換泛型類型。然后,所分配的類型將用于限制容器內(nèi)使用的值,這樣就無(wú)需進(jìn)行類型轉(zhuǎn)換,還可以在編譯時(shí)提供更強(qiáng)的類型檢查。
總結(jié)來(lái)說(shuō)就是:
(1)消除顯示的強(qiáng)制類型轉(zhuǎn)換,提高代碼復(fù)用
(2)提供更強(qiáng)的類型檢查,避免運(yùn)行時(shí)的ClassCastException。
這個(gè)問(wèn)題產(chǎn)生的背景是針對(duì)容器中,基于繼承的泛型實(shí)現(xiàn)會(huì)帶來(lái)兩個(gè)問(wèn)題,請(qǐng)看代碼:
public class ArrayList {
public Object get(int i) { ... }
public void add(Object o) { ... }
...
private Object[] elementData;
}
基于繼承的泛型實(shí)現(xiàn)會(huì)帶來(lái)兩個(gè)問(wèn)題:第一個(gè)問(wèn)題是有關(guān)get()方法的,我們每次調(diào)用get()方法都會(huì)返回一個(gè)Object對(duì)象,每一次都要強(qiáng)制類型轉(zhuǎn)換為我們需要的類型,這樣會(huì)顯得很麻煩;第二個(gè)問(wèn)題是有關(guān)add方法的,假如我們往聚合了String對(duì)象的ArrayList中加入一個(gè)File對(duì)象,編譯器不會(huì)產(chǎn)生任何錯(cuò)誤提示,而這不是我們想要的。所以,從Java 5開(kāi)始,ArrayList在使用時(shí)可以加上一個(gè)類型參數(shù)(type parameter),這個(gè)類型參數(shù)用來(lái)指明ArrayList中的元素類型。類型參數(shù)的引入解決了以上提到的兩個(gè)問(wèn)題,如以下代碼所示:
ArrayList<String> s = new ArrayList<String>();
s.add("abc");
String s = s.get(0); //無(wú)需進(jìn)行強(qiáng)制轉(zhuǎn)換
s.add(123); //編譯錯(cuò)誤,只能向其中添加String對(duì)象
2、泛型的常用特點(diǎn)?
這里其實(shí)問(wèn)的就是泛型在使用過(guò)程中遵循的相關(guān)規(guī)范。類型參數(shù)(又稱類型變量)用作占位符,指示在運(yùn)行時(shí)為類分配類型。根據(jù)需要,可能有一個(gè)或多個(gè)類型參數(shù),并且可以用于整個(gè)類。根據(jù)慣例,類型參數(shù)是單個(gè)大寫(xiě)字母,該字母用于指示所定義的參數(shù)類型。下面列出每個(gè)用例的標(biāo)準(zhǔn)類型參數(shù):
E:元素
K:鍵
N:數(shù)字
T:類型
V:值
S、U、V 等:多參數(shù)情況中的第 2、3、4 個(gè)類型
? 表示不確定的java類型(無(wú)限制通配符類型)
五、匿名內(nèi)部類
內(nèi)部類(nested classes),面向?qū)ο蟪绦蛟O(shè)計(jì)中,可以在一個(gè)類的內(nèi)部定義另一個(gè)類。嵌套類分為兩種,即靜態(tài)嵌套類和非靜態(tài)嵌套類。靜態(tài)嵌套類使用很少,最重要的是非靜態(tài)嵌套類,也即是被稱作為內(nèi)部類(inner)。內(nèi)部類是JAVA語(yǔ)言的主要附加部分。內(nèi)部類幾乎可以處于一個(gè)類內(nèi)部任何位置,可以與實(shí)例變量處于同一級(jí),或處于方法之內(nèi),甚至是一個(gè)表達(dá)式的一部分。
1、匿名內(nèi)部類是否可以繼承其它類?是否可以實(shí)現(xiàn)接口?
使用匿名內(nèi)部類我們必須要繼承一個(gè)父類或者實(shí)現(xiàn)一個(gè)接口,當(dāng)然也僅能只繼承一個(gè)父類或者實(shí)現(xiàn)一個(gè)接口。同時(shí)它也是沒(méi)有class關(guān)鍵字,這是因?yàn)槟涿麅?nèi)部類是直接使用new來(lái)生成一個(gè)對(duì)象的引用,當(dāng)然這個(gè)引用是隱式的。不可以繼承其它類和實(shí)現(xiàn)接口。
2、內(nèi)部類分為幾種?
- 成員內(nèi)部類,在一個(gè)類(外部類)中直接定義的內(nèi)部類;
- 局部?jī)?nèi)部類,在一個(gè)方法(外部類的方法)中定義的內(nèi)部類;
- 匿名內(nèi)部類,
1.成員內(nèi)部類
可以訪問(wèn)它的外部類的所有成員變量和方法,不管是靜態(tài)的還是非靜態(tài)的都可以。
在外部類里面創(chuàng)建成員內(nèi)部類的實(shí)例:this.new B();
在外部類之外創(chuàng)建內(nèi)部類的實(shí)例:(new Test1()).new B().go();
2.局部?jī)?nèi)部類
定義在方法中,比方法的范圍還小。是內(nèi)部類中最少用到的一種類型。像局部變量一樣,不能被public,protected, private和static修飾。只能訪問(wèn)方法中定義的final類型的局部變量。
方法內(nèi)部類在方法中定義,所以只能在方法中使用,即只能在方法當(dāng)中生成方法內(nèi)部類的實(shí)例并且調(diào)用其方法。
3.匿名內(nèi)部類
沒(méi)有名字的局部?jī)?nèi)部類,不使用關(guān)鍵字class, extends, implements, 沒(méi)有構(gòu)造方法。什么情況下需要使用匿名內(nèi)部類?如果滿足下面的一些條件,使用匿名內(nèi)部類是比較合適的:
- 只用到類的一個(gè)實(shí)例。
- 類在定義后馬上用到。
- 類非常?。⊿UN推薦是在4行代碼以下)
- 給類命名并不會(huì)導(dǎo)致你的代碼更容易被理解。
在使用匿名內(nèi)部類時(shí),要記住以下幾個(gè)原則:
- 匿名內(nèi)部類不能有構(gòu)造方法。
- 匿名內(nèi)部類不能定義任何靜態(tài)成員、方法和類。
- 匿名內(nèi)部類不能是public,protected,private,static。
- 只能創(chuàng)建匿名內(nèi)部類的一個(gè)實(shí)例。
- 一個(gè)匿名內(nèi)部類一定是在new的后面,用其隱含實(shí)現(xiàn)一個(gè)接口或?qū)崿F(xiàn)一個(gè)類。
- 因匿名內(nèi)部類為局部?jī)?nèi)部類,所以局部?jī)?nèi)部類的所有限制都對(duì)其生效。
//實(shí)例代碼
interface innerclass{
public void print();
}
public class Main {
public static void main(String[] args) {
innerclass i = new innerclass() {
@Override
public void print() {
System.out.println("匿名內(nèi)部類");
// TODO Auto-generated method stub
}
};
i.print();
}
}
匿名內(nèi)部類的高頻使用場(chǎng)景是在多線程下(靈活使用箭頭函數(shù)語(yǔ)法糖):
// Java 8之前:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Before Java8, too much code for too little to do");
}
}).start();
//Java 8方式:
new Thread(() -> System.out.println("In Java8, Lambda expression!!") ).start();
3、內(nèi)部類可以引用它的包含類(外部類)的成員嗎?
內(nèi)部類可以直接訪問(wèn)外部類的成員屬性
4、請(qǐng)說(shuō)一下 Java 中為什么要引入內(nèi)部類?還有匿名內(nèi)部類?
- 內(nèi)部類對(duì)象可以訪問(wèn)創(chuàng)建它的對(duì)象的實(shí)現(xiàn),包括私有數(shù)據(jù);
- 內(nèi)部類不為同一包的其他類所見(jiàn),具有很好的封裝性;
- 使用內(nèi)部類可以很方便的編寫(xiě)事件驅(qū)動(dòng)程序;
- 匿名內(nèi)部類可以方便的定義運(yùn)行時(shí)回調(diào);
- 內(nèi)部類可以方便的定義
