本章更多討論了子類對父類的繼承可能導(dǎo)致的各種陷阱,比如隱藏(hidden),遮蔽(shadow),遮掩(obscure),覆寫(override),重載(overload)等行為。 1。首先來看看一個(gè)隱藏的例子: class Base { class Derived extends Base { public class PrivateMatter { 我們可能指望它打印Base,可很抱歉,此程序是無法編譯通過,剛一看錯(cuò)誤信息你可能愣住: 無權(quán)訪問private的className。。。 對 于實(shí)例方法,子類可以對父類的實(shí)例方法進(jìn)行覆寫,可對于實(shí)例變量(而且包括類變量,靜態(tài)方法,final靜態(tài)變量),子類只能隱藏父類中的相同名稱的變 量,而不是覆寫。根據(jù)new Derived()的編譯期類型為子類Derived,調(diào)用子類的className屬性,可此屬性是聲明為private的,所以無法編譯通過。如果我 們想調(diào)用父類中被隱藏的className,可以通過向上轉(zhuǎn)型來實(shí)現(xiàn): System.out.println(((Base)new Derived()).className); 此例告訴我們,JAVA語言中子類定義與父類相同類型和名稱的變量、嵌套類型和靜態(tài)方法,都將隱藏掉父類中相應(yīng)的方法,而不是覆寫,所以,請避免隱藏!此過程中當(dāng)然也不存在所謂多態(tài)。另外,我們不應(yīng)該違反這樣一條規(guī)則:對基類所做的任何行為,都應(yīng)當(dāng)可以同樣作用于子類。此例中子類className為private,違反了基類中className是public的定義,這樣的寫法應(yīng)該避免。
2。也許哪一天你突然想自己寫一個(gè)String來代替java.lang中的String,讓我們來看看會發(fā)生什么? public class StrungOut { class String { public String(java.lang.String s) { public java.lang.String toString() { StrungOut dose not have a main method! 怪了,明明有個(gè)main方法啊??請注意,main方法中的參數(shù)String []args,其中的String類型要求是java.lang.String,可JVM會自動地把把這些參數(shù)認(rèn)為是我們自定義的下面那個(gè)String類型,這就是錯(cuò)誤的原因所在。教訓(xùn):避免重用類名,特別是java平臺的類型,特別是java.lang包下的類名!在此例中,當(dāng)前類所在包中的所有類的main方法都將因此失效。 3。遮掩(obscure):我覺的翻譯成模糊也許更好。看看下面的例子: public class ShadesOfGray { class X { static C Y = new C(); class C { 你認(rèn)為他應(yīng)該打印什么呢??黑還是白?還是黑白不分:),光明的力量總是偉大,它一直打印的是:white。這說明了X.Y一直調(diào)用的是靜態(tài)變量Y,而不是靜態(tài)內(nèi)隱類Y。JAVA語言規(guī)范告訴我們,當(dāng)一個(gè)變量和一個(gè)類型具有相同的名字,明確他們位于相同的作用范圍內(nèi),變量名具有優(yōu)先權(quán),同樣,變量名與類型名將遮掩包名。 即 變量名>類型名>包名。其實(shí)上面的例子有更嚴(yán)重的問題,它并沒有遵循標(biāo)準(zhǔn)的JAVA命名習(xí)慣,變量應(yīng)該以小寫開頭(mixedCase的格 式),類名以大寫開頭(MaxedCase的格式),如果完全遵照習(xí)慣來寫,就不會出現(xiàn)此問題了。所以,請遵守標(biāo)準(zhǔn)的命名習(xí)慣。退一步,假設(shè)在某些情況下 我們只能以此方式書寫,那我們怎么訪問靜態(tài)內(nèi)隱類Y呢?兩種方法: System.out.println(((X.Y)null).Z); //借助表達(dá)式訪問類變量,還記的嗎? 在JDK5中還可以這樣: public static <T extends X.Y> void main(String args[]){ //繼承X.Y類解決此問題。 System.out.println(T.Z); }
4。 包A中的某個(gè)類被另一個(gè)包C中的子類所繼承,如果子類當(dāng)中“覆寫”了父類中的方法,而此方法在父類中不是聲明為public或者protected,那么 這并非覆寫,這兩個(gè)方法將沒有任何關(guān)系。所以,如果你希望某個(gè)類的一個(gè)方法被包外的子類所覆寫,請把此方法聲明為protected或者public。 5。遮蔽(shadow),這里討論了JDK5靜態(tài)導(dǎo)入需要注意的問題,看下面的例子: import static java.util.Arrays.toString; class ImportDuty { static void printArgs(Object... args) { 6。最后一個(gè)謎題很重要哦,我現(xiàn)在才知道JDK5對條件操作符(a?b:c)已經(jīng)有重大改變。試著分別在JDK1.4和JDK5中運(yùn)行下面的程序: import java.util.Random; public class CoinSide { public static CoinSide flip() { public static void main(String[] args) { class Heads extends CoinSide { public String toString() { class Tails extends CoinSide { public String toString() { incompatible types for ?: neither is a subtype of the other 說什么第2個(gè)操作數(shù)和第3個(gè)操作數(shù)都不是另外一個(gè)子類。而在JDK5下這個(gè)程序?qū)⒄_\(yùn)行,隨機(jī)打印heads或者tails。這是因?yàn)樵贘DK5以前,條件運(yùn)算符要求:當(dāng)?shù)?個(gè)和第3個(gè)操作數(shù)是引用類型時(shí),它們其中的一個(gè)必須是另外一個(gè)的子類。而例子中Heads和Tails都不是對方的子類,所以產(chǎn)生了上面的錯(cuò)誤。而在JDK5中,這個(gè)條件放寬了,第2個(gè)和第3個(gè)操作數(shù)如果是引用那么都是合法的,只不過其結(jié)果類型將是這兩種類型的最小公共超類。此例中Heads和Tails的超類向上追溯有CoinSide,Object,而CoinSide是他們的最小公共超類。 如果想在JDK5以前運(yùn)行上面的程序,可以把第2或者第3操作數(shù)向上轉(zhuǎn)型為他們的超類即可: public static CoinSide flip() { 另外一些謎題討論了對Object類中方法的覆寫問題,特別要注意不要覆寫變成了重載 Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=728172 |
|