java面試題解惑系列天時(shí)165_第1頁(yè)
java面試題解惑系列天時(shí)165_第2頁(yè)
java面試題解惑系列天時(shí)165_第3頁(yè)
java面試題解惑系列天時(shí)165_第4頁(yè)
java面試題解惑系列天時(shí)165_第5頁(yè)
已閱讀5頁(yè),還剩160頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、JAVA 面試題解惑系列(一)類的初始化順序關(guān)鍵字: java 面試題 初始化作者:臧圩人(zangweiren):2008-06-26大家在去參加面試的時(shí)候,經(jīng)常會(huì)遇到這樣的考題:給你兩個(gè)類的代碼,它們之間是繼承的關(guān)系,每個(gè)類里只有構(gòu)造器方法和一些變量,構(gòu)造器里可能還有一段代碼對(duì)變量值進(jìn)行了某種運(yùn)算,另外還有一些將變量值輸出到控制臺(tái)的代碼,然后讓我們判斷輸出的結(jié)果。這實(shí)際上是在考查我們對(duì)于繼承情況下類的初始化順序的了解。我們大家都知道,對(duì)于靜態(tài)變量、靜態(tài)初始化塊、變量、初始化塊、構(gòu)造器,它們的初始化順序依次是(靜態(tài)變量、靜態(tài)初始化塊)>(變量、初始化塊)>構(gòu)造器。我們也可以通過(guò)下

2、面的測(cè)試代碼來(lái)驗(yàn)證這一點(diǎn):public class InitialOrderTest /靜態(tài)變量public static String staticField = "靜態(tài)變量"/變量public String field = "變量"/靜態(tài)初始化塊static System.out.println(staticField);System.out.println("靜態(tài)初始化塊");/初始化塊System.out.println(field); System.out.println("初始化塊");/構(gòu)造器publ

3、ic InitialOrderTest() System.out.println("構(gòu)造器");public static void main(String args) new InitialOrderTest();運(yùn)行以上代碼,我們會(huì)得到如下的輸出結(jié)果:1. 靜態(tài)變量2. 靜態(tài)初始化塊3. 變量4. 初始化塊5. 構(gòu)造器這與上文中說(shuō)的完全符合。那么對(duì)于繼承情況下又會(huì)怎樣呢?我們?nèi)匀灰砸欢螠y(cè)試代碼來(lái)獲取最終結(jié)果:class Parent /靜態(tài)變量public static String p_StaticField = "父類-靜態(tài)變量"/變量publi

4、c String p_Field = "父類-變量"/靜態(tài)初始化塊static System.out.println(p_StaticField); System.out.println("父類-靜態(tài)初始化塊");/初始化塊System.out.println(p_Field); System.out.println("父類-初始化塊");/構(gòu)造器public Parent() System.out.println("父類-構(gòu)造器");public class SubClass extends Parent /靜態(tài)

5、變量public static String s_StaticField = "子類-靜態(tài)變量"/變量public String s_Field = "子類-變量"/靜態(tài)初始化塊static System.out.println(s_StaticField); System.out.println("子類-靜態(tài)初始化塊");/初始化塊System.out.println(s_Field); System.out.println("子類-初始化塊");/構(gòu)造器public SubClass() System.out.

6、println("子類-構(gòu)造器");/程序public static void main(String args) new SubClass();運(yùn)行一下上面的代碼,結(jié)果馬上呈現(xiàn)在我們的眼前:1. 父類-靜態(tài)變量2. 父類-靜態(tài)初始化塊3. 子類-靜態(tài)變量4. 子類-靜態(tài)初始化塊5. 父類-變量6. 父類-初始化塊7. 父類-構(gòu)造器8. 子類-變量9. 子類-初始化塊10. 子類-構(gòu)造器現(xiàn)在,結(jié)果已經(jīng)不言自明了。大家可能會(huì)注意到一點(diǎn),那就是,并不是父類完全初始化完畢后才進(jìn)行子類的初始化,實(shí)際上子類的靜態(tài)變量和靜態(tài)初始化塊的初始化是在父類的變量、初始化塊和構(gòu)造器初始化之前就完

7、成了。那么對(duì)于靜態(tài)變量和靜態(tài)初始化塊之間、變量和初始化塊之間的先后順序又是怎樣呢?是否靜態(tài)變量總是先于靜態(tài)初始化塊,變量總是先于初始化塊就被初始化了呢?實(shí)際上這取決于它們?cè)陬愔谐霈F(xiàn)的先后順序。我們以靜態(tài)變量和靜態(tài)初始化塊為例來(lái)進(jìn)行說(shuō)明。同樣,我們還是寫(xiě)一個(gè)類來(lái)進(jìn)試:public class TestOrder /靜態(tài)變量public static TestA a = new TestA();/靜態(tài)初始化塊static System.out.println("靜態(tài)初始化塊");/靜態(tài)變量public static TestB b = new TestB();public st

8、atic void main(String args) new TestOrder();class TestA public TestA() System.out.println("Test-A");class TestB public TestB() System.out.println("Test-B");運(yùn)行上面的代碼,會(huì)得到如下的結(jié)果:1. Test-A2. 靜態(tài)初始化塊3. Test-B大家可以隨意改變變量 a、變量 b 以及靜態(tài)初始化塊的前后位置,就會(huì)發(fā)現(xiàn)輸出結(jié)果隨著它們?cè)陬愔谐霈F(xiàn)的前后順序而改變,這就說(shuō)明靜態(tài)變量和靜態(tài)初始化塊是依照他們?cè)陬?/p>

9、中的定義順序進(jìn)行初始化的。同樣,變量和初始化塊也遵循這個(gè)規(guī)律。了解了繼承情況下類的初始化順序之后,如何判斷最終輸出結(jié)果就迎刃而解了。(二)到底創(chuàng)建了幾個(gè) String 對(duì)象?關(guān)鍵字: java 面試題 string 創(chuàng)建幾個(gè)對(duì)象作者:臧圩人(zangweiren):2008-06-30我們首先來(lái)看一段代碼:String str=new String("abc");緊接著這段代碼之后的往往是這個(gè)問(wèn)題,那就是這行代碼究竟創(chuàng)建了幾個(gè) String 對(duì)象呢?相信大家對(duì)這道題并不陌生,也是眾所周知的,2 個(gè)。接下來(lái)我們就從這道題展開(kāi),一起回顧一下與創(chuàng)建 String 對(duì)象相關(guān)的一些

10、JAVA 知識(shí)。我們可以把上面這行代碼分成 String str、=、"abc"和 new String()四部分來(lái)str 只。String是定義了一個(gè)名為 str 的 String 類型的變量,因此它并沒(méi)有創(chuàng)建對(duì)象;=是對(duì)變量 str 進(jìn)行初始化,將某個(gè)對(duì)象的(或者叫句柄)賦值給它,顯然也沒(méi)有創(chuàng)建對(duì)象;現(xiàn)在只剩下 newString("abc")了。那么,new String("abc")為什么又能被看成"abc"和 new String()呢? 我們來(lái)看一下被我們調(diào)用了的 String 的構(gòu)造器:public

11、String(String original) /other code .大家都知道,我們常用的創(chuàng)建一個(gè)類的實(shí)例(對(duì)象)的方法有以下兩種:1. 使用 new 創(chuàng)建對(duì)象。2. 調(diào)用 Class 類的 newInstance 方法,利用反射機(jī)制創(chuàng)建對(duì)象。我們正是使用 new 調(diào)用了 String 類的上面那個(gè)構(gòu)造器方法創(chuàng)建了一個(gè)對(duì)象,并將它的賦值給了 str 變量。同時(shí)我們注意到,被調(diào)用的構(gòu)造器方法接受的參數(shù)也是一個(gè) String 對(duì)象, 這個(gè)對(duì)象正是"abc"。由此我們又要引入另外一種創(chuàng)建 String 對(duì)象的方式的討論引號(hào)內(nèi)包含文本。這種方式是 String 特有的,并且它

12、與 new 的方式存在很大區(qū)別。String str="abc"毫無(wú)疑問(wèn),這行代碼創(chuàng)建了一個(gè) String 對(duì)象。String a="abc"String b="abc"那這里呢?還是一個(gè)。String a="ab"+"cd"再看看這里呢?識(shí)的回顧了。仍是一個(gè)。有點(diǎn)奇怪嗎?說(shuō)到這里,我們就需要引入對(duì)字符串池相關(guān)知在 JAVA 虛擬機(jī)(JVM)中存在著一個(gè)字符串池,其中保存著很多String 對(duì)象,并且可以被共享使用,因此它提高了效率。由于 String 類是 final 的,它的值一經(jīng)創(chuàng)建就不可

13、改變,因此我們不用擔(dān)心String 對(duì)象共享而帶來(lái)程序的String 類維護(hù),我們可以調(diào)用。字符串池由intern()方法來(lái)字符串池。我們?cè)倩仡^看看 String a="abc",這行代碼被執(zhí)行的時(shí)候,JAVA 虛擬機(jī)首先在字符串池中查找是否已經(jīng)存在了值為"abc"的這么一個(gè)對(duì)象,它的判斷依據(jù)是String 類equals(Object obj)方法的返回值。如果有,則不再創(chuàng)建新的對(duì)象,直接返回已存在對(duì)象的;如果沒(méi)有,則先創(chuàng)建這個(gè)對(duì)象,然后把它加入到字符串池中,再將它的返回。因此,我們不難理解前面三個(gè)例子中頭兩個(gè)例子為什么是這個(gè)了。對(duì)于第三個(gè)例子:Str

14、ing a="ab"+"cd"由于常量的值在編譯的時(shí)候就被確定了。在這里,"ab"和"cd"都是常量,因此變量 a 的值在編譯時(shí)就可以確定。這行代碼編譯后的效果等同于:String a="abcd"因此這里只創(chuàng)建了一個(gè)對(duì)象"abcd",并且它被保存在字符串池里了?,F(xiàn)在問(wèn)題又來(lái)了,是不是所有經(jīng)過(guò)“+”連接后得到的字符串都會(huì)被添加到字符串池中呢?我們都知道“=”可以用來(lái)比較兩個(gè)變量,它有以下兩種情況:1.如果比較的是兩個(gè)基本類型(char,byte,short,int,long,

15、float,double,boolean),則是判斷它們的值是否相等。2.如果表較的是兩個(gè)對(duì)象變量,則是判斷它們的是否指向同一個(gè)對(duì)象。下面我們就用“=”來(lái)做幾個(gè)測(cè)試。為了便于說(shuō)明,我們把指向字符串池中已經(jīng)存在的對(duì)象也視為該對(duì)象被加入了字符串池:publicclass StringTest publicStringstatic void main(String args) a = "ab"/創(chuàng)建了一個(gè)對(duì)象,并加入字符串池中System.out.println("String a = "ab"");String b = "cd&

16、quot;/創(chuàng)建了一個(gè)對(duì)象,并加入字符串池中System.out.println("String b = "cd"");String c = "abcd"/創(chuàng)建了一個(gè)對(duì)象,并加入字符串池中String d = "ab" + "cd"/如果d 和c 指向了同一個(gè)對(duì)象,則說(shuō)明 d 也被加入了字符串池if (d = c) System.out.println(""ab"+"cd" 創(chuàng)建的對(duì)象 "加入了" 字符串池中");/

17、如果d 和c 沒(méi)有指向了同一個(gè)對(duì)象,則說(shuō)明 d 沒(méi)有被加入字符串池else System.out.println(""ab"+"cd" 創(chuàng)建的對(duì)象 "沒(méi)加入" 字符串池中");String e = a + "cd"/如果e 和c 指向了同一個(gè)對(duì)象,則說(shuō)明 e 也被加入了字符串池if (e = c) System.out.println(" a +"cd"創(chuàng)建的對(duì)象 "加入了"字符串池中");/如果e 和c 沒(méi)有指向了同一個(gè)對(duì)象,則說(shuō)明

18、e 沒(méi)有被加入字符串池else System.out.println(" a +"cd"創(chuàng)建的對(duì)象 "沒(méi)加入"字符串池中");String f = "ab" + b;/如果f 和c 指向了同一個(gè)對(duì)象,則說(shuō)明 f 也被加入了字符串池if (f = c) System.out.println(""ab"+ b 創(chuàng)建的對(duì)象 "加入了"字符串池中");/如果f 和c 沒(méi)有指向了同一個(gè)對(duì)象,則說(shuō)明 f 沒(méi)有被加入字符串池else System.out.println

19、(""ab"+ b 創(chuàng)建的對(duì)象 "沒(méi)加入"字符串池中");String g = a + b;/如果g 和c 指向了同一個(gè)對(duì)象,則說(shuō)明 g 也被加入了字符串池if (g = c) System.out.println(" a + b 創(chuàng)建的對(duì)象 "加入了"字符串池中");/如果g 和c 沒(méi)有指向了同一個(gè)對(duì)象,則說(shuō)明 g 沒(méi)有被加入字符串池else System.out.println(" a + b 創(chuàng)建的對(duì)象 "沒(méi)加入"字符串池中");運(yùn)行結(jié)果如下:1.2

20、..String a = "ab" String b = "cd""ab"+"cd" 創(chuàng)建的對(duì)象 "加入了" 字符串池中a +"cd" 創(chuàng)建的對(duì)象 "沒(méi)加入" 字符串池中"ab"+ b 創(chuàng)建的對(duì)象 "沒(méi)加入" 字符串池中a + b 創(chuàng)建的對(duì)象 "沒(méi)加入" 字符串池中從上面的結(jié)果中我們不難看出,只有使用引號(hào)包含文本的方式創(chuàng)建的 String 對(duì)象之間使用“+”連接產(chǎn)生的新對(duì)象才會(huì)被加入

21、字符串池中。對(duì)于所有包含new 方式新建對(duì)象(包括null)的“+”連接表達(dá)式,它所產(chǎn)生的新對(duì)象都被加入字符串池中,對(duì)此我們不再贅述。但是有一種情況需要引起我們的注意。請(qǐng)看下面的代碼:public class StringStaticTest /常量Apublic staticfinal String A = "ab"/常量Bpublic staticfinal String B = "cd"public staticvoid main(String args) /將兩個(gè)常量用+連接對(duì)s 進(jìn)行初始化String s String tif (s =A +

22、B;= "abcd"t) System.out.println("s 等于 t,它們是同一個(gè)對(duì)象"); else System.out.println("s 不等于 t,它們不是同一個(gè)對(duì)象");這段代碼的運(yùn)行結(jié)果如下:.s 等于t,它們是同一個(gè)對(duì)象這又是為什么呢?原因是這樣的,對(duì)于常量來(lái)講,它的值是固定的,因此在編譯期就能被確定了,而變量的值只有到運(yùn)行時(shí)才能被確定,因?yàn)檫@個(gè)變量可以被不同的方法調(diào)用,從而可能引起值的改變。在上面的例子中,A 和B 都是常量,值是固定的,因此 s 的值也是固定的, 它在類被編譯時(shí)就已經(jīng)確定了。也就是說(shuō):S

23、tring s=A+B;等同于:String s="ab"+"cd"我對(duì)上面的例子稍加改變看看會(huì)出現(xiàn)什么情況:public class StringStaticTest/常量Apublic staticfinal String A;/常量Bpublic staticfinal String B;static A = "ab"B = "cd"public staticvoid main(Stringargs) /將兩個(gè)常量用+連接對(duì)s 進(jìn)行初始化String s String tif (s =A + B;= &quo

24、t;abcd"t) System.out.println("s 等于 t,它們是同一個(gè)對(duì)象"); else System.out.println("s 不等于 t,它們不是同一個(gè)對(duì)象");它的運(yùn)行結(jié)果是這樣:.s 不等于t,它們不是同一個(gè)對(duì)象只是做了一點(diǎn)改動(dòng),結(jié)果就和剛剛的例子恰好相反。我們?cè)賮?lái)分析一下。A 和B 雖然被定義為常量(只能被賦值一次),但是它們都沒(méi)有馬上被賦值。在運(yùn)算出 s 的值之前,他們何時(shí)被賦值,以及被賦予什么樣的值,都是個(gè)變數(shù)。因此 A 和B 在被賦值之前,性質(zhì)類似于一個(gè)變量。那么 s 就不能在編譯期被確定,而只能在運(yùn)行時(shí)被

25、創(chuàng)建了。由于字符串池中對(duì)象的共享能夠帶來(lái)效率的提高,因此我們提倡大家用引號(hào)包含文本的方式來(lái)創(chuàng)建 String 對(duì)象,實(shí)際上這也是我們?cè)诰幊讨谐2捎玫?。接下?lái)我們?cè)賮?lái)看看 intern()方法,它的定義如下:public native String intern();這是一個(gè)本地方法。在調(diào)用這個(gè)方法時(shí),JAVA 虛擬機(jī)首先檢查字符串池中是否已經(jīng)存在與該對(duì)象值相等對(duì)象存在,如果有則返回字符串池中對(duì)象的;如果沒(méi)有,則先在字符串池中創(chuàng)建一個(gè)相同值的 String 對(duì)象,然后再將它的返回。我們來(lái)看這段代碼:public class StringInternTest public static void

26、main(String args) /使用char 數(shù)組來(lái)初始化 a,避免在 a 被創(chuàng)建之前字符串池中已經(jīng)存在了值為"abcd"的對(duì)象String a String bif (b = new String(new char 'a', 'b', 'c', 'd' );= ern();a) System.out.println("b 被加入了字符串池中,沒(méi)有新建對(duì)象"); else System.out.println("b 沒(méi)被加入字符串池中,新建了對(duì)象");運(yùn)行

27、結(jié)果:.b 沒(méi)被加入字符串池中,新建了對(duì)象如果 String 類的 intern()方法在沒(méi)有找到相同值的對(duì)象時(shí),是把當(dāng)前對(duì)象加入字符串池中,然后返回它的的話,那么 b 和a 指向的就是同一個(gè)對(duì)象;否則b 指向的對(duì)象就是 JAVA 虛擬機(jī)在字符串池中新建的,只是它的值與 a 相同罷了。上面這段代碼的運(yùn)行結(jié)果恰恰印證了這一點(diǎn)。最后我們?cè)賮?lái)說(shuō)說(shuō) String 對(duì)象在 JAVA 虛擬機(jī)(JVM)中的,以及字符串池與堆(heap)和棧(stack)的關(guān)系。我們首先回顧一下堆和棧的區(qū)別:.棧(stack):主要保存基本類型(或者叫內(nèi)置類型)(char、byte、short、int、long、float、

28、double、boolean)和對(duì)象的享,速度僅次于寄存器(register),快于堆。.,數(shù)據(jù)可以共堆(heap):用于對(duì)象。我們查看 String 類的源碼就會(huì)發(fā)現(xiàn),它有一個(gè) value 屬性,保存著 String 對(duì)象的值,類型是char,這也正說(shuō)明了字符串就是字符的序列。當(dāng)執(zhí)行 String a="abc"時(shí),JAVA 虛擬機(jī)會(huì)在棧中創(chuàng)建三個(gè) char 型的值''、'b'和'',然后在堆中創(chuàng)建一個(gè) String 對(duì)象,它的值(value)是剛才在棧中創(chuàng)建的三個(gè) char(a) 型值組成(c) 的數(shù)組'a'

29、;,'b','c',最后這個(gè)新創(chuàng)建的 String 對(duì)象會(huì)被添加到字符串池中。如果我們接著執(zhí)行Stringb=new String("abc");代碼,由于"abc"已經(jīng)被創(chuàng)建并保存于字符串池中,因此 JAVA 虛擬機(jī)只會(huì)在堆中新創(chuàng)建一個(gè) String 對(duì)象,但是它的值(value)是共享前一行代碼執(zhí)行時(shí)在棧中創(chuàng)建的三個(gè) char 型值值'a'、'b'和'c'。說(shuō)到這里,我們對(duì)于篇首提出的 String str=new String("abc")為什么是創(chuàng)

30、建了兩個(gè)對(duì)象這個(gè)問(wèn)題就已經(jīng)相當(dāng)明了了。(三)變量(屬性)的覆蓋關(guān)鍵字: java 面試題 繼承 變量的覆蓋屬性作者:臧圩人(zangweiren):2008-07-03我們來(lái)看看這么一道題:class ParentClass publicint i= 10;publicpublicclassint iSubClass extends ParentClass = 30;publicstatic void main(String args) ParentClass parentClass = new SubClass(); SubClass subClass = new SubClass();Sy

31、stem.out.println(parentClass.i + subClass.i);控制臺(tái)的輸出結(jié)果是多少呢?20?40?還是 60?變量,或者叫做類的屬性,在繼承的情況下,如果父類和子類存在同名的變量會(huì)出現(xiàn)什么情況呢?這就是這道題要考查的知識(shí)點(diǎn)變量(屬性)的覆蓋。這個(gè)問(wèn)題雖然簡(jiǎn)單,但是情況卻比較復(fù)雜。因?yàn)槲覀儾粌H要考慮變量、靜態(tài)變量和常量三種情況,還要考慮private、friendly(即不加對(duì)屬性的不同影響。修飾符)、protected 和 public 四種權(quán)限下我們先從普通變量說(shuō)起。依照我們的慣例,先來(lái)看一段代碼:class ParentClass private String

32、 privateField = "父類變量-private"/* friendly */String friendlyField = "父類變量-friendly" protected String protectedField = "父類變量-protected" public String publicField = "父類變量-public"/ private 的變量無(wú)法直接,因此我們給他增加了一個(gè)方法public returnString getPrivateFieldValue() privateFiel

33、d;publicclass SubClass extends ParentClass private String privateField = "子類變量-private"/* friendly */String friendlyField = "子類變量-friendly"protected String protectedField = "子類變量-protected"public String publicField = "子類變量-public"/ private 的變量無(wú)法直接,因此我們給他增加了一個(gè)方

34、法public returnString getPrivateFieldValue() privateField;publicstatic void main(String args) /為了便于查閱,我們統(tǒng)一按照 private、friendly、protected、public 的順序/輸出下列三種情況中變量的值/ ParentClass 類型,ParentClass 對(duì)象ParentClass parentClass = new ParentClass(); System.out.println("ParentClass parentClass = new ParentClas

35、s();"); System.out.println(parentClass.getPrivateFieldValue(); System.out.println(parentClass.friendlyField); System.out.println(parentCtectedField); System.out.println(parentClass.publicField);System.out.println();/ ParentClass 類型,SubClass 對(duì)象ParentClass subClass = new SubClass();System

36、.out.println("ParentClass subClass = new SubClass();"); System.out.println(subClass.getPrivateFieldValue(); System.out.println(subClass.friendlyField); System.out.println(subCtectedField);System.out.println(subClass.publicField);System.out.println();/ SubClass 類型,SubClass 對(duì)象SubClas

37、s subClazz = new SubClass(); System.out.println("SubClass subClazz = new SubClass();"); System.out.println(subClazz.getPrivateFieldValue(); System.out.println(subClazz.friendlyField); System.out.println(subCtectedField); System.out.println(subClazz.publicField);這段代碼的運(yùn)行結(jié)果如下:.

38、.5.ParentClass parentClass = new ParentClass();父類變量-private 父類變量-friendly 父類變量-protected 父類變量-publicParentClass subClass = new SubClass();子類變量-private 父類變量-friendly父類變量-protected父類變量-publicSubClass subClazz = new SubClass();子類變量-private 子類變量-friendly16. 子類變量-protected17. 子類變

39、量-public從上面的結(jié)果中可以看出,private 的變量與其它三種的權(quán)限變量的不同,這是由于方法重寫(xiě)(override)而引起的。關(guān)于重寫(xiě)知識(shí)的回顧留給以后的章節(jié),這里我們來(lái)看一下其它三種權(quán)限下變量的覆蓋情況。分析上面的輸出結(jié)果就會(huì)發(fā)現(xiàn),變量的值取決于我們定義的變量的類型,而不是創(chuàng)建的對(duì)象的類型。在上面的例子中,同名的變量權(quán)限也是相同的,那么對(duì)于名稱相同但是權(quán)限不同的變量,情況又會(huì)怎樣呢?事實(shí)勝于雄辯,我們繼續(xù)來(lái)做測(cè)試。由于 private 變量的特殊性,在接下來(lái)的實(shí)驗(yàn)中我們都把它排除在外,不予考慮。由于上面的例子已經(jīng)說(shuō)明了,當(dāng)變量類型是父類(ParentClass)時(shí),不管我們創(chuàng)建的對(duì)

40、象是父類(ParentClass)的還是子類(SubClass)的,都不存在屬性覆蓋的問(wèn)題,因此接下來(lái)我們也只考慮變量類型和創(chuàng)建對(duì)象都是子類(SubClass)的情況。class ParentClass /* friendly */String field = "父類變量"public class SubClass extends ParentClass protected String field = "子類變量"public static void main(String args) SubClass subClass = new SubClass(

41、);System.out.println(subClass.field);運(yùn)行結(jié)果:.子類變量class ParentClass public String field = "父類變量"public class SubClass extends ParentClass protected String field = "子類變量"public static void main(String args) SubClass subClass = new SubClass(); System.out.println(subClass.field);運(yùn)行結(jié)果:.

42、子類變量上面兩段不同的代碼,輸出結(jié)果確是相同的。事實(shí)上,我們可以將父類和子類屬性前的修飾符在 friendly、protected 和 public 之間任意切換,得到的結(jié)果都是相同的。也就是說(shuō)訪問(wèn)修飾符并不影響屬性的覆蓋,關(guān)于這一點(diǎn)大家可以自行編寫(xiě)測(cè)試代碼驗(yàn)證。對(duì)于靜態(tài)變量和常量又會(huì)怎樣呢?我們繼續(xù)來(lái)看:class ParentClass publicstatic String staticField = "父類靜態(tài)變量"publicfinal String finalField = "父類常量"publicpublicstatic final Str

43、ing staticFinalField = "父類靜態(tài)常量"class SubClass extends ParentClass publicstatic String staticField = "子類靜態(tài)變量"publicfinal String finalField = "子類常量"public static final String staticFinalField = "子類靜態(tài)常量"public static void main(String args) SubClass subClass = new

44、 SubClass(); System.out.println(SubClass.staticField);/注意,這里的 subClass 變量,不是 SubClass 類System.out.println(subClass.finalField); System.out.println(SubClass.staticFinalField);運(yùn)行結(jié)果如下:1.2.3.子類靜態(tài)變量子類常量子類靜態(tài)常量雖然上面的結(jié)果中包含“子類靜態(tài)變量”和“子類靜態(tài)常量”,但這并不表示父類的“靜態(tài)變量”和“靜態(tài)常量”可以被子類覆蓋,因?yàn)樗鼈兌际菍儆陬?,而不屬于?duì)象。上面的例子中,結(jié)果是否會(huì)相同呢?用對(duì)象來(lái)對(duì)變

45、量(屬性)的覆蓋做測(cè)試,如果是基本類型的變量,是肯定的,這里我們就不再一一舉例說(shuō)明了。最后,我們來(lái)做個(gè)總結(jié)。通過(guò)以上測(cè)試,可以得出一下結(jié)論:1. 由于 private 變量受權(quán)限的限制,它不能被覆蓋。2. 屬性的值取父類還是子類并不取決于我們創(chuàng)建對(duì)象的類型,而是取決于我們定義的變量的類型。.friendly、protected 和 public 修飾符并不影響屬性的覆蓋。靜態(tài)變量和靜態(tài)常量屬于類,不屬于對(duì)象,因此它們不能被覆蓋。常量可以被覆蓋。對(duì)于基本類型和對(duì)象,它們適用同樣的覆蓋規(guī)律。我們?cè)倩氐狡椎哪堑李},大家都已經(jīng)知道了,輸出結(jié)果應(yīng)該是 40。(四)final、finall

46、y 和 finalize 的區(qū)別關(guān)鍵字: java 面試題 final finally finalize作者:臧圩人(zangweiren):2008-07-08final、finally 和 finalize 的區(qū)別是什么?這是一道再經(jīng)典不過(guò)的面試題了,我們?cè)诟鱾€(gè)公司的面試題中幾乎都能看到它的身影。final、finally 和 finalize 雖然長(zhǎng)得像孿生三兄弟一樣,但是它們的含義和用法卻是大相徑庭。這一次我們就一起來(lái)回顧一下這方面的知識(shí)。final 關(guān)鍵字我們首先來(lái)說(shuō)說(shuō) final。它可以用于以下四個(gè)地方:.定義變量,包括靜態(tài)的和非靜態(tài)的。定義方法的參數(shù)。定義方法。定義

47、類。我們依次來(lái)回顧一下每種情況下 final 的作用。首先來(lái)看第一種情況,如果 final 修飾的是一個(gè)基本類型,就表示這個(gè)變量被賦予的值是不可變的,即它是個(gè)常量;如果 final 修飾的是一個(gè)對(duì)象,就表示這個(gè)變量被賦予的是不可變的,這里需要提醒大家注意的是,不可改變的只是這個(gè)變量所保存的的,并不是這個(gè)所指向的對(duì)象。在第二種情況下,final含義與第一種情況相同。實(shí)際上對(duì)于前兩種情況,有一種更貼切的表述final 的含義的描述,那就是,如果一個(gè)變量或方法參數(shù)被 final 修飾,就表示它只能被賦值一次,但是 JAVA 虛擬機(jī)為變量設(shè)定的默認(rèn)值不記作一次賦值。被 final 修飾的變量必須被初始

48、化。初始化的方式有以下幾種:.在定義的時(shí)候初始化。final 變量可以在初始化塊中初始化,不可以在靜態(tài)初始化塊中初始化。靜態(tài) final 變量可以在靜態(tài)初始化塊中初始化,不可以在初始化塊中初始化。final 變量還可以在類的構(gòu)造器中初始化,但是靜態(tài) final 變量不可以。通過(guò)下面的代碼可以驗(yàn)證以上的觀點(diǎn):public class FinalTest /在定義時(shí)初始化public final int A = 10;public final int B;/在初始化塊中初始化B = 20;/非靜態(tài) final 變量不能在靜態(tài)初始化塊中初始化/public final int C; s

49、tatic C = 30;/靜態(tài)常量,在定義時(shí)初始化public static final int STATIC_D = 40;public static final int STATIC_E;/靜態(tài)常量,在靜態(tài)初始化塊中初始化static STATIC_E = 50;/靜態(tài)變量不能在初始化塊中初始化/ public static final int STATIC_F;/STATIC_F = 60;public final int G;/靜態(tài) final 變量不可以在構(gòu)造器中初始化/ public static final int STATIC_H;/在構(gòu)造器中初始化public FinalT

50、est() G = 70;/靜態(tài) final 變量不可以在構(gòu)造器中初始化/ STATIC_H = 80;/給 final 的變量第二次賦值時(shí),編譯會(huì)報(bào)錯(cuò)/A = 99;/STATIC_D = 99;/final 變量未被初始化,編譯時(shí)就會(huì)報(bào)錯(cuò)public final int I;/靜態(tài) final 變量未被初始化,編譯時(shí)就會(huì)報(bào)錯(cuò)/ public static final int STATIC_J;我們運(yùn)行上面的代碼之后出了可以發(fā)現(xiàn) final 變量(常量)和靜態(tài) final 變量(靜態(tài)常量) 未被初始化時(shí),編譯會(huì)報(bào)錯(cuò)。用 final 修飾的變量(常量)比非final 的變量(普通變量)擁有更高

51、的效率,因此我們?cè)趯?shí)際編程中應(yīng)該盡可能多的用常量來(lái)代替普通變量,這也是一個(gè)很好的編程習(xí)慣。當(dāng)final 用來(lái)定義一個(gè)方法時(shí),會(huì)有什么效果呢?正如大家所知,它表示這個(gè)方法不可以被子類重寫(xiě),但是它這不影響它被子類繼承。我們寫(xiě)段代碼來(lái)驗(yàn)證一下:class ParentClass public final void TestFinal() System.out.println("父類-這是一個(gè) final 方法");public class SubClass extends ParentClass /*/子類無(wú)法重寫(xiě)(override)父類的 final 方法,否則編譯時(shí)會(huì)報(bào)錯(cuò)pu

52、blic void TestFinal() System.out.println("子類-重寫(xiě) final 方法");public static void main(String args) SubClass sc = new SubClass(); sc.TestFinal();這里需要特殊說(shuō)明的是,具有 private類權(quán)限的方法也可以增加 final 修飾,但是由于子無(wú)法繼承 private 方法,因此也無(wú)法重寫(xiě)它。編譯器在處理private 方法時(shí),是按照 final 方法來(lái)對(duì)待的,這樣可以提高該方法被調(diào)用時(shí)的效率。不過(guò)子類仍然可以定義同父類中的private 方法

53、具有同樣結(jié)構(gòu)的方法,但是這并必產(chǎn)生重寫(xiě)的效果,而且它們之間也不存在然。最后我們?cè)賮?lái)回顧一下 final 用于類的情況。這個(gè)大家應(yīng)該也很熟悉了,因?yàn)槲覀冏畛S玫腟tring 類就是 final 的。由于 final 類不允許被繼承,編譯器在處理時(shí)把它的所有方法都當(dāng)作final 的,因此 final 類比普通類擁有更高的效率。而由關(guān)鍵字 abstract 定義的抽象類含有必須由繼承自它的子類重載實(shí)現(xiàn)的抽象方法,因此無(wú)法同時(shí)用final 和abstract 來(lái)修飾同一個(gè)類。同樣的道理,final 也不能用來(lái)修飾接口。 final 的類的所有方法都不能被重寫(xiě),但這并不表示final 的類的屬性(變量)

54、值也是不可改變的,要想做到 final 類的屬性值不可改變,必須給它增加 final 修飾,請(qǐng)看下面的例子:public final class FinalTest int i = 10;public static void main(String args) FinalTest ft = new FinalTest();ft.i = 99;System.out.println(ft.i);運(yùn)行上面的代碼試試看,結(jié)果是 99,而不是初始化時(shí)的 10。finally 語(yǔ)句接下來(lái)我們一起回顧一下 finally 的用法。這個(gè)就比較簡(jiǎn)單了,它只能用在 try/catch 語(yǔ)句中,并且附帶著一個(gè)語(yǔ)句塊

55、,表示這段語(yǔ)句最終總是被執(zhí)行。請(qǐng)看下面的代碼:public final class FinallyTest public static void main(String args) try throw new NullPointerException(); catch (NullPointerException e) System.out.println("程序拋出了異常"); finally System.out.println("執(zhí)行了 finally 語(yǔ)句塊");運(yùn)行結(jié)果說(shuō)明了 finally 的作用:1. 程序拋出了異常2. 執(zhí)行了 finall

56、y 語(yǔ)句塊請(qǐng)大家注意,捕獲程序拋出的異常之后,既不加處理,也不繼續(xù)向上拋出異常,并不是良好的編程習(xí)慣,它掩蓋了程序執(zhí)行中發(fā)生的錯(cuò)誤,這里只是方便演示,請(qǐng)不要學(xué)習(xí)。那么,有沒(méi)有一種情況使finally 語(yǔ)句塊得不到執(zhí)行呢?大家可能想到了return、continue、break 這三個(gè)可以打亂代碼順序執(zhí)行語(yǔ)句的規(guī)律。那我們就來(lái)試試看, 這三個(gè)語(yǔ)句是否能影響finally 語(yǔ)句塊的執(zhí)行:public final class FinallyTest /測(cè)試 return 語(yǔ)句public ReturnClass testReturn() try return new ReturnClass(); catch (Exception e) e.printStackTrace(); finally System.out.println("執(zhí)行了 finally 語(yǔ)句");return null;/測(cè)試 continue 語(yǔ)句public void testContinue() for (int i = 0; i < 3; i+) try System.out.println(i);if (i = 1) continue; catch (Exception e) e.printStackTrace(); finally System.out.prin

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論