Java编程思想 学习笔记7
七、復(fù)用類
?
1.組合語法?
在新的類中產(chǎn)生現(xiàn)有類的對象。由于新的類是由現(xiàn)有類的對象所組成,所以這種方法叫做組合。
類中域為基本類型時能夠自動被初始化為零。對象引用被初始化為null。
編譯器不是簡單地為每一個引用都創(chuàng)建默認(rèn)對象,如果想初始化這些引用,可以在代碼中的下列位置進(jìn)行:
1.在定義對象的地方。這意味著它們總是能夠在構(gòu)造器被調(diào)用之前被初始化。
2.在類的構(gòu)造器中。
3.就在正要使用這些對象之前,這種方式稱為惰性初始化。
4.使用實(shí)例初始化。
2.繼承語法?
當(dāng)創(chuàng)建一個類時,總是在繼承,因此,除非已明確指出要從其他類中繼承,否則就是在隱式地從Java的標(biāo)準(zhǔn)根類Object進(jìn)行繼承。
為了繼承,一般的規(guī)則是將所有的數(shù)據(jù)成員都指定為private,將所有的方法都指定為public。
Java用super關(guān)鍵字表示超類的意思,當(dāng)前類就是從超類繼承來的。
①初始化基類
從外部看,導(dǎo)出類就像是一個與基類具有相同接口的新類,或許還會有一些額外的方法和域。但繼承并不只是復(fù)制基類的接口。當(dāng)創(chuàng)建了一個導(dǎo)出類的對象時,該對象包含了一個基類的子對象。這個子對象與你用基類直接創(chuàng)建的對象是一樣的。二者區(qū)別在于,后者來自于外部,而基類的子對象被包裝在導(dǎo)出類的內(nèi)部。
對基類子對象的正確初始化也是至關(guān)重要的,而且僅有一種方法來保證這一點(diǎn):構(gòu)造器中調(diào)用基類構(gòu)造器來執(zhí)行初始化。Java會自動在導(dǎo)出類的構(gòu)造器中插入對基類構(gòu)造器的調(diào)用。
但是,如果沒有默認(rèn)的基類構(gòu)造器,或者想調(diào)用一個帶參數(shù)的基類構(gòu)造器,就必須用關(guān)鍵字super顯式地編寫調(diào)用基類構(gòu)造器的語句,并且配以適當(dāng)?shù)膮?shù)列表。
調(diào)用基類構(gòu)造器必須是你在導(dǎo)出類構(gòu)造器中要做的第一件事。
3.代理?
第三種關(guān)系稱為代理,Java并沒有提供對它的直接支持。這是繼承和組合的中庸之道,因為我們將一個成員對象置于所要構(gòu)造的類中(就像組合),但與此同時我們在新類中暴露了該成員對象的所有方法(就像繼承)。例如,太空船需要一個控制模塊:
public class SpaceShipControls {void up(int velocity) {}void down(int velocity) {}void left(int velocity) {}void right(int velocity) {} }構(gòu)造太空船的一種方式是使用繼承:
public class SpaceShip extends SpaceShipControls {private String name;public SpaceShip(String name) {this.name = name;}public static void main(String[] args) {SpaceShip protector = new SpaceShip("NASA");protector.up(100);} }然而,SpaceShip并非真正的SpaceShipControls類型。更準(zhǔn)確地說,SpaceShip包含了SpaceShipControls,與此同時,SpaceShipControls的所有方法在SpaceShip中都暴露了出來。代理解決了此難題:
public class SpaceShipDelegation {private String name;private SpaceShipControls controls =new SpaceShipControls();public SpaceShipDelegation(String name) {this.name = name;}// Delegation methodspublic void back(int velocity) {controls.back(velocity);}public void up(int velocity) {controls.up(velocity);}...public static void main(String[] args) {SpaceShipDelegation protector =new SpaceShipDelegation("NASA");protector.up(100);} }4.結(jié)合使用組合和繼承?
名稱屏蔽
如果Java的基類擁有某個已被多次重載的方法的名稱,那么在導(dǎo)出類中重新定義該方法名稱并不會屏蔽其在基類中的任何版本。因此,無論是在該層或者它的基類中對方法進(jìn)行定義,重載機(jī)制都可以正常工作。
Java SE5新增加了@Override注解,它并不是關(guān)鍵字,但是可以把它當(dāng)作關(guān)鍵字使用。當(dāng)你想要覆寫某個方法時,可以選擇添加這個注解,在你不留心重載而并非覆寫了該方法時,編譯器就會生成一條錯誤消息。
5.在組合與繼承之間選擇?
組合技術(shù)通常用于想在新類中使用現(xiàn)有類的功能而非它的接口這種情形。即,在新類中嵌入某個對象,讓其實(shí)現(xiàn)所需要的功能,但新類的用戶看到的只是為新類所定義的接口,而非所嵌入對象的接口。
在繼承的時候,使用某個現(xiàn)有類,并開發(fā)一個它的特殊版本。通常,這意味著你在使用一個通用類,并為了某種特殊需要而將其特殊化。
思考一下,用一個“交通工具”對象來構(gòu)成一部“車子”是毫無意義的,因為“車子”并不包含“交通工具”,它僅是一種交通工具(is-a關(guān)系)。“is-a"(是一個)關(guān)系是用繼承來表達(dá)的,而“has-a”(有一個)的關(guān)系是用組合來表達(dá)的。
6.protected關(guān)鍵字?
protected,它指明“就類用戶而言,這是private的,但對于任何繼承于此類的導(dǎo)出類或其他任何位于同一個包內(nèi)的類來說,它卻是可以訪問的。”
盡管可以創(chuàng)建protected域,但是最好的方式還是將域保持為private;你應(yīng)當(dāng)一直保留“更改底層實(shí)現(xiàn)”的權(quán)利。然后通過protected方法來控制類的繼承者的訪問權(quán)限。
7.向上轉(zhuǎn)型?
“向新的類提供方法”并不是繼承技術(shù)中最重要的方面,其最重要的方面是用來表現(xiàn)新類和基類之間的關(guān)系。這種關(guān)系可以用“新類是現(xiàn)有類的一種類型”這句話加以概括。
由于繼承可以確保基類中所有的方法導(dǎo)出類中也同樣有效,所以能夠向基類發(fā)送的所以信息同樣也可以向?qū)С鲱惏l(fā)送。將導(dǎo)出類引用轉(zhuǎn)換為基類引用的動作,我們稱之為向上轉(zhuǎn)型。
由于向上轉(zhuǎn)型是從一個較專用類型向較通用類型轉(zhuǎn)換,所以總是很安全的。在向上轉(zhuǎn)型的過程中,類接口唯一可能發(fā)生的事情是丟失方法。
到底是該用組合還是繼承,一個最清晰的判斷方法就是問一問自己是否需要從新類向基類進(jìn)行向上轉(zhuǎn)型。如果必須向上轉(zhuǎn)型,則繼承是必要的。
8.final關(guān)鍵字?
①final數(shù)據(jù)
許多編程語言都有某種方法,來向編譯器告知一塊數(shù)據(jù)是恒定不變的。有時數(shù)據(jù)的恒定不變是很有用的,例如:
1.一個永不改變的編譯時常量
2.一個在運(yùn)行時被初始化的值,而你不希望它被改變。
在Java中,編譯時常量必須時基本數(shù)據(jù)類型,并且以關(guān)鍵字final表示。在對這個常量進(jìn)行定義的時候,必須對其進(jìn)行賦值。
一個既是static又是final的域只占據(jù)一段不能改變的存儲空間。根據(jù)慣例,既是static又是final的域?qū)⒂么髮懕硎?#xff0c;并使用下劃線分隔各個單詞。
對于對象引用,final使其引用恒定不變。然而對象本身卻是可以改變的,Java并未提供使任何對象恒定不變的途徑。
不能因為某數(shù)據(jù)是final的就認(rèn)為在編譯時可以知道它的值。
Java允許生成“空白final”,所謂空白final是指被聲明為final但又未給定初值的域。無論什么情況,編譯器都確保空白final在使用前都必須被初始化。但是,空白final在關(guān)鍵字final的使用上提供了更大的靈活性,為此,一個類中的final域就可以做到根據(jù)對象而有所不同,卻又保持恒定不變的特性。
Java允許在參數(shù)列表中以聲明的方式將參數(shù)指明為final。這意味著你無法在方法中更改參數(shù)引用所指向的對象。這一特性主要用來向匿名內(nèi)部類傳遞數(shù)據(jù)。
②final方法
使用final方法的原因有兩個。第一個原因是把方法鎖定,以防任何繼承類修改它的含義。這是出于設(shè)計的考慮:想要確保在繼承中使方法行為保持不變,并且不會被覆蓋。過去建議使用final方法的第二個原因是效率。在Java的早期實(shí)現(xiàn)中,如果將一個方法指明為final,就是同意編譯器將針對該方法的所有調(diào)都轉(zhuǎn)為內(nèi)嵌調(diào)用。如今不需要用final來進(jìn)行效率優(yōu)化了。
類中的所有的private方法都隱式地指定為是final的。
“覆蓋”只有在某方法是基類的接口的一部分時才會出現(xiàn)。即,必須能將一個對象向上轉(zhuǎn)型為它的基本類型并調(diào)用相同的方法。如果某方法為private,它就不是基類接口的一部分。它僅是一些隱藏于類中的程序代碼,只不過時具有相同的名稱而已。但如果在導(dǎo)出類中以相同的名稱生成一個public、protected或包訪問權(quán)限方法的話,該方法就不會產(chǎn)生在基類中出現(xiàn)的“僅具有相同名稱”的情況。此時你并沒有覆蓋該方法,僅是生成了一個新的方法。由于private方法無法觸及而且能有效隱藏,所有除了把它所歸屬的類的組織結(jié)構(gòu)的原因而存在外,其他任何事物都不需要考慮到它。
③final類
當(dāng)將某個類的整體定義為final時,就表明了你不打算繼承該類,而且也不允許別人這樣做。
由于final類禁止繼承,所以final類中所有的方法都隱式指定為是final的,因為無法覆蓋它們。
9.初始化及類的加載?
Java中所有事物都是對象。每個類的編譯代碼都存在于它自己的獨(dú)立的文件中,該文件只在需要使用程序代碼時才會被加載。一般來說,可以說:“類的代碼在初次使用時才加載。”這通常是指加載發(fā)生于創(chuàng)建類的第一個對象之時,但是當(dāng)訪問static域或static方法時,也會發(fā)生加載。(構(gòu)造器也是static方法。因此更準(zhǔn)確地說,類是在任何static成員被訪問時加載的。)
繼承與初始化
了解包括繼承在內(nèi)的初始化全過程,以對所發(fā)生的一切有個全局的把握,是很有益的。看下例:
class Insect {private int i = 9;protected int j;Insect() {System.out.println("i = "+i+", j = "+j);j=39;}private static int x1 = printInit("static Insect.x1 initialized");static int printInit(String s) {System.out.println(s);return 47;} }public class Beetle extends Insect {private int k = printInit("Beetle.k initialized");public Beetle() {System.out.println("k = "+k);System.out.println("j = "+j);}private static int x2 = printInit("static Beetle.x2 initialized");public static void main(String[] args) {System.out.println("Beetle constrictor"); Beetle b = new Beetle();} }/**Output static Insect.x1 initialized static Beetle.x2 initialized Beetle constrictor i = 9, j = 0 Beetle.k initialized k = 47 j = 39 */?
在Java上運(yùn)行Beetle時,第一件事就是試圖訪問Bettle.main()(一個static方法),于是加載器開始啟動并找出Beetle類的編譯代碼(在名為Beetle.class的文件中)。在加載過程中,編譯器注意到它有一個基類(由extends),于是它繼續(xù)進(jìn)行加載。不管是否打算創(chuàng)建一個該基類的對象,這都要發(fā)生。
如果該基類還有自身的基類,那么第二個基類就會被加載,如此類推。接下來,根基類中的static初始化(此例中是Insect)即會被執(zhí)行,然后是下一個導(dǎo)出類,以此類推。這種方式很重要,因為導(dǎo)出類的static初始化可能會依賴于基類成員能否被正確初始化。
至此為止,必要的類都已加載完畢,對象就可以創(chuàng)建了。首先,對象中的所有基本類型都會被設(shè)為默認(rèn)值、對象引用被設(shè)為null——這是通過將對象內(nèi)存設(shè)為二進(jìn)制零值而一舉生成的。然后,基類的構(gòu)造器會被調(diào)用。在基類構(gòu)造器完成以后,實(shí)例變量按其次序被初始化。最后,構(gòu)造器的其余部分將執(zhí)行。
10.總結(jié)?
繼承和組合都能從現(xiàn)有類型生成新類型。組合一般是將現(xiàn)有類型作為新類型底層實(shí)現(xiàn)的一部分來加以復(fù)用,而繼承復(fù)用的是接口。
盡管面向?qū)ο缶幊虒^承極力強(qiáng)調(diào),但在開始一個設(shè)計時,一般應(yīng)優(yōu)先選擇使用組合(或者可能是代理),只在確實(shí)必要時才使用繼承。
?
轉(zhuǎn)載于:https://www.cnblogs.com/fht-litost/p/8388186.html
總結(jié)
以上是生活随笔為你收集整理的Java编程思想 学习笔记7的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 图像平滑与滤波
- 下一篇: LVS负载均衡群集的了解与基本配置(一)