?第一,談談final,?finally,?finalize的區別。
????第二,Anonymous?Inner?Class?(匿名內部類) ?是否可以extends(繼承)其它類,是否可以implements(實現)interface(接口)?
????第三,Static?Nested?Class?和?Inner?Class的不同,說得越多越好(面試題有的很籠統)。
????第四,&和&&的區別。
????第五,HashMap和Hashtable的區別。
????第六,Collection?和?Collections的區別。
????第七,什么時候用assert.
????第八,GC是什么??為什么要有GC?
????第九,String?s?=?new?String("xyz");創建了幾個String?Object?
????第十,Math.round(11.5)等於多少??Math.round(-11.5)等於多少?
????第十一,short?s1?=?1;?s1?= ?s1?+?1;有什么錯??short?s1?=?1;? s1?+=?1;有什么錯?
????第十二,sleep()?和?wait()?有什么區別?
????第十三,Java有沒有goto?
????第十四,數組有沒有length()這個方法??String有沒有length()這個方法?
????第十五,Overload和Override的區別。Overloaded的方法是否可以改變返回值的類型?
????第十六,Set里的元素是不能重復的,那么用什么方法來區分重復與否呢??是用==還是equals()??它們有何區別?
????第十七,給我一個你最常見到的runtime?exception.
????第十八,error和exception有什么區別?
????第十九,List,?Set,?Map是否繼承自Collection接口?
????第二十,abstract?class和interface有什么區別?
????第二十一,abstract的method是否可同時是static,是否可同時是native,是否可同時是synchronized?
????第二十二,接口是否可繼承接口??抽象類是否可實現(implements)接口??抽象類是否可繼承實體類(concrete?class)?
????第二十三,啟動一個線程是用run()還是start()?
????第二十四,構造器Constructor是否可被override?
????第二十五,是否可以繼承String類?
????第二十六,當一個線程進入一個對象的一個synchronized方法后,其它線程是否可進入此對象的其它方法?
????第二十七,try?{}里有一個return語句,那么緊跟在這個try后的finally?{}里的code會不會被執行,什么時候被執行,在return前還是后?
????第二十八,編程題:?用最有效率的方法算出2乘以8等於幾?
????第二十九,兩個對象值相同(x.equals(y)?==?true),但卻可有不同的hash?code,這句話對不對?
????第三十,當一個對象被當作參數傳遞到一個方法后,此方法可改變這個對象的屬性,并可返回變化后的結果,那么這里到底是值傳遞還是引用傳遞?
????第三十一,swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上?
????第三十二,編程題:?寫一個Singleton出來。
以下是答案
????第一,談談final,?finally,?finalize的區別。
????final?修飾符(關鍵字)如果一個類被聲明為final,意味著它不能再派生出新的子類,不能作為父類被繼承。因此一個類不能既被聲明為?abstract的,又被聲明為final的。將變量或方法聲明為final,可以保證它們在使用中不被改變。被聲明為final的變量必須在聲明時給定初值,而在以后的引用中只能讀取,不可修改。被聲明為final的方法也同樣只能使用,不能重載finally?再異常處理時提供?finally?塊來執行任何清除操作。如果拋出一個異常,那么相匹配的?catch?子句就會執行,然后控制就會進入?finally?塊(如果有的話)。
????finalize?方法名。Java?技術允許使用?finalize()?方法在垃圾收集器將對象從內存中清除出去之前做必要的清理工作。這個方法是由垃圾收集器在確定這個對象沒有被引用時對這個對象調用的。它是在?Object?類中定義的,因此所有的類都繼承了它。子類覆蓋?finalize()?方法以整理系統資源或者執行其他清理工作。finalize()?方法是在垃圾收集器刪除對象之前對這個對象調用的。
????第二,Anonymous?Inner?Class?(匿名內部類) ?是否可以extends(繼承)其它類,是否可以implements(實現)interface(接口)?
????匿名的內部類是沒有名字的內部類。不能extends(繼承)?其它類,但一個內部類可以作為一個接口,由另一個內部類實現。
????第三,Static?Nested?Class?和?Inner?Class的不同,說得越多越好(面試題有的很籠統)。
????Nested?Class? (一般是C++的說法),Inner?Class?(一般是JAVA的說法)。Java內部類與C++嵌套類最大的不同就在于是否有指向外部的引用上。具體可見http:?//www.frontfree.net/articles/services/view.asp?id=704&page= 1
????注:?靜態內部類(Inner?Class)意味著1創建一個static內部類的對象,不需要一個外部類對象,2不能從一個static內部類的一個對象訪問一個外部類對象
????第四,&和&&的區別。
????&是位運算符。&&是布爾邏輯運算符。
????第五,HashMap和Hashtable的區別。
????都屬于Map接口的類,實現了將惟一鍵映射到特定的值上。
????HashMap?類沒有分類或者排序。它允許一個?null?鍵和多個?null?值。
????Hashtable?類似于?HashMap,但是不允許? null?鍵和?null?值。它也比?HashMap?慢,因為它是同步的。
????第六,Collection?和?Collections的區別。
????Collections是個java.util下的類,它包含有各種有關集合操作的靜態方法。
????Collection是個java.util下的接口,它是各種集合結構的父接口。
第七,什么時候用assert。?
斷言是一個包含布爾表達式的語句,在執行這個語句時假定該表達式為?true。如果表達式計算為?false,那么系統會報告一個?AssertionError。它用于調試目的:?
assert(a?>?0);?//?throws?an?AssertionError?if?a?<=?0?
斷言可以有兩種形式:?
assert?Expression1?;?
assert?Expression1?:?Expression2?;?
Expression1?應該總是產生一個布爾值。?
Expression2?可以是得出一個值的任意表達式。這個值用于生成顯示更多調試信息的?String?消息。?
斷言在默認情況下是禁用的。要在編譯時啟用斷言,需要使用?source?1.4?標記:?
javac?-source?1.4?Test.java?
要在運行時啟用斷言,可使用?-enableassertions?或者?-ea?標記。?
要在運行時選擇禁用斷言,可使用?-da?或者?-disableassertions?標記。?
要系統類中啟用斷言,可使用?-esa?或者?-dsa?標記。還可以在包的基礎上啟用或者禁用斷言。?
可以在預計正常情況下不會到達的任何位置上放置斷言。斷言可以用于驗證傳遞給私有方法的參數。不過,斷言不應該用于驗證傳遞給公有方法的參數,因為不管是否啟用了斷言,公有方法都必須檢查其參數。不過,既可以在公有方法中,也可以在非公有方法中利用斷言測試后置條件。另外,斷言不應該以任何方式改變程序的狀態。?
第八,GC是什么??為什么要有GC??(基礎)。?
GC是垃圾收集器。Java?程序員不用擔心內存管理,因為垃圾收集器會自動進行管理。要請求垃圾收集,可以調用下面的方法之一:?
System.gc()?
Runtime.getRuntime().gc()?
第九,String?s?=?new?String("xyz");創建了幾個String?Object??
兩個對象,一個是“xyx”,一個是指向“xyx”的引用對象s。?
第十,Math.round(11.5)等於多少??Math.round(-11.5)等於多少??
Math.round(11.5)返回(long)12,Math.round(-11.5)返回(long)-11;?
第十一,short?s1?=?1;?s1?=?s1?+?1; 有什么錯??short?s1?=?1;?s1?+=?1;有什么錯? ?
short?s1?=?1;?s1?=?s1?+?1;有錯, s1是short型,s1+1是int型,不能顯式轉化為short型。可修改為s1?=(short)(s1?+?1) ?。short?s1?=?1;?s1?+=?1正確。?
第十二,sleep()?和?wait()?有什么區別??搞線程的最愛?
sleep()方法是使線程停止一段時間的方法。在sleep?時間間隔期滿后,線程不一定立即恢復執行。這是因為在那個時刻,其它線程可能正在運行而且沒有被調度為放棄執行,除非(a)“醒來”的線程具有更高的優先級,(b)正在運行的線程因為其它原因而阻塞。?
wait()是線程交互時,如果線程對一個同步對象x?發出一個wait()調用,該線程會暫停執行,被調對象進入等待狀態,直到被喚醒或等待時間到。?
第十三,Java有沒有goto??
Goto?java中的保留字,現在沒有在java中使用。?
第十四,數組有沒有length()這個方法??String有沒有length()這個方法??
數組沒有length()這個方法,有length的屬性。?
String有有length()這個方法。?
第十五,Overload和Override的區別。Overloaded的方法是否可以改變返回值的類型??
方法的重寫Overriding和重載Overloading是Java多態性的不同表現。重寫Overriding是父類與子類之間多態性的一種表現,重載Overloading是一個類中多態性的一種表現。如果在子類中定義某方法與其父類有相同的名稱和參數,我們說該方法被重寫? (Overriding)。子類的對象使用這個方法時,將調用子類中的定義,對它而言,父類中的定義如同被“屏蔽”了。如果在一個類中定義了多個同名的方法,它們或有不同的參數個數或有不同的參數類型,則稱為方法的重載(Overloading)。Overloaded的方法是可以改變返回值的類型。?
第十六,Set里的元素是不能重復的,那么用什么方法來區分重復與否呢??是用==還是equals()??它們有何區別??
Set里的元素是不能重復的,那么用iterator()方法來區分重復與否。equals()是判讀兩個Set是否相等。?
equals()和==方法決定引用值是否指向同一對象equals()在類中被覆蓋,為的是當兩個分離的對象的內容和類型相配的話,返回真值。?
第十七,給我一個你最常見到的runtime?exception。?
ArithmeticException,?ArrayStoreException,? BufferOverflowException,?BufferUnderflowException,??CannotRedoException,???
CannotUndoException,??ClassCastException,?CMMException,???ConcurrentModificationException,??
DOMException,?EmptyStackException,?IllegalArgumentException,??IllegalMonitorStateException,??
IllegalPathStateException,??IllegalStateException,??
ImagingOpException,??
IndexOutOfBoundsException,??MissingResourceException,??NegativeArraySizeException,??NoSuchElementException,??
NullPointerException,??ProfileDataException,?ProviderException,?
?RasterFormatException,??SecurityException,?SystemException,
?UndeclaredThrowableException,??
UnmodifiableSetException,??UnsupportedOperationException??
第十八,error和exception有什么區別??
error?表示恢復不是不可能但很困難的情況下的一種嚴重問題。比如說內存溢出。不可能指望程序能處理這樣的情況。
exception?表示一種設計或實現問題。也就是說,它表示如果程序運行正常,從不會發生的情況。?
第十九,List,?Set,?Map是否繼承自Collection接口??
List,Set是?
Map不是?
第二十,abstract?class和interface有什么區別??
聲明方法的存在而不去實現它的類被叫做抽象類(abstract?class),它用于要創建一個體現某些基本行為的類,并為該類聲明方法,但不能在該類中實現該類的情況。不能創建abstract?類的實例。然而可以創建一個變量,其類型是一個抽象類,并讓它指向具體子類的一個實例。不能有抽象構造函數或抽象靜態方法。Abstract?類的子類為它們父類中的所有抽象方法提供實現,否則它們也是抽象類為。取而代之,在子類中實現該方法。知道其行為的其它類可以在類中實現這些方法。?
接口(interface)是抽象類的變體。在接口中,所有方法都是抽象的。多繼承性可通過實現這樣的接口而獲得。接口中的所有方法都是抽象的,沒有一個有程序體。接口只可以定義static?final成員變量。接口的實現與子類相似,除了該實現類不能從接口定義中繼承行為。當類實現特殊接口時,它定義(即將程序體給予)所有這種接口的方法。然后,它可以在實現了該接口的類的任何對象上調用接口的方法。由于有抽象類,它允許使用接口名作為引用變量的類型。通常的動態聯編將生效。引用可以轉換到接口類型或從接口類型轉換,instanceof?運算符可以用來決定某對象的類是否實現了接口。?
第二十一,abstract的method是否可同時是static,是否可同時是native,是否可同時是synchronized??
都不能?
第二十二,接口是否可繼承接口??抽象類是否可實現(implements)接口??抽象類是否可繼承實體類(concrete?class)??
接口可以繼承接口。抽象類可以實現(implements)接口,抽象類是否可繼承實體類,但前提是實體類必須有明確的構造函數。?
第二十三,啟動一個線程是用run()還是start()??
啟動一個線程是調用start()方法,使線程所代表的虛擬處理機處于可運行狀態,這意味著它可以由JVM調度并執行。這并不意味著線程就會立即運行。run()方法可以產生必須退出的標志來停止一個線程。?
第二十四,構造器Constructor是否可被override??
構造器Constructor不能被繼承,因此不能重寫Overriding,但可以被重載Overloading。?
第二十五,是否可以繼承String類??
String類是final類故不可以繼承。?
第二十六,當一個線程進入一個對象的一個synchronized方法后,其它線程是否可進入此對象的其它方法??
不能,一個對象的一個synchronized方法只能由一個線程訪問。?
第二十七,try?{}里有一個return語句,那么緊跟在這個try后的finally?{}里的code會不會被執行,什么時候被執行,在return前還是后??
會執行,在return前執行。?
第二十八,編程題:?用最有效率的方法算出2乘以8等於幾??
有C背景的程序員特別喜歡問這種問題。?
2?<<?3?
第二十九,兩個對象值相同(x.equals(y)?==?true),但卻可有不同的hash?code,這句話對不對??
不對,有相同的hash?code。?
第三十,當一個對象被當作參數傳遞到一個方法后,此方法可改變這個對象的屬性,并可返回變化后的結果,那么這里到底是值傳遞還是引用傳遞??
是值傳遞。Java?編程語言只由值傳遞參數。當一個對象實例作為一個參數被傳遞到方法中時,參數的值就是對該對象的引用。對象的內容可以在被調用的方法中改變,但對象的引用是永遠不會改變的。?
第三十一,swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上??
switch(expr1)中,expr1是一個整數表達式。因此傳遞給?switch?和?case?語句的參數應該是?int、?short、?char?或者?byte。long, string?都不能作用于swtich。?
第三十二,編程題:?寫一個Singleton出來。
Singleton模式主要作用是保證在Java應用程序中,一個類Class只有一個實例存在。?
一般Singleton模式通常有幾種種形式:?
第一種形式:?定義一個類,它的構造函數為private的,它有一個static的private的該類變量,在類初始化時實例話,通過一個public的getInstance方法獲取對它的引用,繼而調用其中的方法。?
public?class?Singleton?{?
private?Singleton(){}?
//在自己內部定義自己一個實例,是不是很奇怪??
//注意這是private?只供內部調用?
private?static?Singleton?instance?=?new?Singleton();?
//這里提供了一個供外部訪問本class的靜態方法,可以直接訪問 ?
public?static?Singleton?getInstance()?{?
return?instance;? ?
?}?
}?
第二種形式:?
public?class?Singleton?{?
private?static?Singleton?instance?=?null;?
public?static?synchronized?Singleton?getInstance()?{?
//這個方法比上面有所改進,不用每次都進行生成對象,只是第一次 ? ?
//使用時生成實例,提高了效率!?
if?(instance==null)?
instance=new?Singleton();?
return?instance;? }?
}?
其他形式:?
定義一個類,它的構造函數為private的,所有方法為static的。?
一般認為第一種形式要更加安全些?
第三十三?Hashtable和HashMap?
Hashtable繼承自Dictionary類,而HashMap是Java1.2引進的Map?interface的一個實現?
HashMap允許將null作為一個entry的key或者value,而Hashtable不允許?
還有就是,HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因為contains方法容易讓人引起誤解。?
最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多個線程訪問Hashtable時,不需要自己為它的方法實現同步,而HashMap就必須為之提供外同步。?
Hashtable和HashMap采用的hash/rehash算法都大概一樣,所以性能不會有很大的差異。
posted @
2006-12-26 10:51 保爾任 閱讀(184) |
評論 (0) |
編輯 收藏
摘要: EJB方面94、EJB2.0有哪些內容?分別用在什么場合? EJB2.0和EJB1.1的區別?答:規范內容包括Bean提供者,應用程序裝配者,EJB容器,EJB配置工具,EJB服務提供者,系統管理員。這里面,EJB容器是EJB之所以能夠運行的核心。EJB容器管理著EJB的創建,撤消,激活,去活,與數據庫的連接等等重要的核心工作。JSP,Servlet,EJB,JNDI,JDBC,JMS.....9...
閱讀全文
posted @
2006-12-26 10:50 保爾任 閱讀(420) |
評論 (0) |
編輯 收藏
摘要: Java基礎方面:1、作用域public,private,protected,以及不寫時的區別答:區別如下:作用域?????????? 當前類?????? 同一package? 子孫類?????? 其他packagepublic??????????? √????????????? √????????????????? √???????????? √protected??????? √???????...
閱讀全文
posted @
2006-12-26 10:50 保爾任 閱讀(257) |
評論 (0) |
編輯 收藏
2.4版本的servlet規范在部屬描述符中新增加了一個<dispatcher>元素,這個元素有四個可能的值:即REQUEST,FORWARD,INCLUDE和ERROR,可以在一個<filter-mapping>元素中加入任意數目的<dispatcher>,使得filter將會作用于直接從客戶端過來的request,通過forward過來的request,通過include過來的request和通過<error-page>過來的request。如果沒有指定任何< ? dispatcher ? >元素,默認值是REQUEST。可以通過下面幾個例子來輔助理解。 ?
? 例1: ?
? <filter-mapping> ?
? <filter-name>Logging ? Filter</filter-name> ?
? <url-pattern>/products/*</url-pattern> ?
? </filter-mapping> ?
? 這種情況下,過濾器將會作用于直接從客戶端發過來的以/products/…開始的請求。因為這里沒有制定任何的< ? dispatcher ? >元素,默認值是REQUEST。 ?
? ?
? 例2: ?
? <filter-mapping> ?
? ? ? ? ? ? ? ? ? <filter-name>Logging ? Filter</filter-name> ?
? ? ? ? ? ? ? ? ? <servlet-name>ProductServlet</servlet-name> ?
? ? ? ? ? ? ? ? ? <dispatcher>INCLUDE</dispatcher> ?
? </filter-mapping> ?
? 這種情況下,如果請求是通過request ? dispatcher的include方法傳遞過來的對ProductServlet的請求,則要經過這個過濾器的過濾。其它的諸如從客戶端直接過來的對ProductServlet的請求等都不需要經過這個過濾器。 ?
? 指定filter的匹配方式有兩種方法:直接指定url-pattern和指定servlet,后者相當于把指定的servlet對應的url-pattern作為filter的匹配模式 ?
? filter的路徑匹配和servlet是一樣的,都遵循servlet規范中《SRV.11.2 ? Specification ? of ? Mappings》一節的說明 ?
? ?
? 例3: ?
? <filter-mapping> ?
? ? ? ? ? ? ? ? ? <filter-name>Logging ? Filter</filter-name> ?
? ? ? ? ? ? ? ? ? <url-pattern>/products/*</url-pattern> ?
? ? ? ? ? ? ? ? ? <dispatcher>FORWARD</dispatcher> ?
? ? ? ? ? ? ? ? ? <dispatcher>REQUEST</dispatcher> ?
? </filter-mapping> ?
? 在這種情況下,如果請求是以/products/…開頭的,并且是通過request ? dispatcher的forward方法傳遞過來或者直接從客戶端傳遞過來的,則必須經過這個過濾器。??
posted @
2006-12-22 08:46 保爾任 閱讀(828) |
評論 (0) |
編輯 收藏
摘要: UUID(Universally Unique Identifier)全局唯一標識符,是指在一臺機器上生成的數字,它保證對在同一時空中的所有機器都是唯一的。按照開放軟件基金會(OSF)制定的標準計算,用到了以太網卡地址、納秒級時間、芯片ID碼和許多可能的數字。由以下幾部分的組合:當前日期和時間(UUID的第一個部分與時間有關,如果你在生成一個UUID之后,過幾秒又生成一個UUID,則第一個部分不同...
閱讀全文
posted @
2006-12-12 11:33 保爾任 閱讀(964) |
評論 (2) |
編輯 收藏
?第一章 一般技術
1.java只有唯一一種參數傳遞方式:by value(值傳遞)。對于primitive types(基本型別)很容易理解,對于object references(對象引用),傳遞的是object reference的拷貝。
2.polymorphism(多態)優于instanceof:instanceof很容易被誤用,很多場合都應該以多態代替,無論何時看到instanceof,請判斷是否可以改進以消除它。
3.避免創建重復對象。比如一個類A的某個方法新建了一個類B,且此類B不會改變,則每次建立該類A的一個對象就會新建B的對象,此時應把
B設為private static final。
4.清除過期的對象引用。
5.避免使用終結函數。因為終結函數可能得不到執行或很久后得到執行,所以要避免使用。顯示的中止方法通常與try-finally結構結合使用,防止出現異常時終結函數得不到執行。
eg: Foo foo = new Foo(...);
??? try{
??????? //do what must be done with foo???
??? }finally{
??????? foo.terminate();
??? }
6.通過私有構造函數來強化不可實例化的能力。比如一些工具類不希望被實例化,然而在缺少顯示構造函數時編譯器會自動提供一個默認構造函數,為防止以上情況要構造一個顯示的私有的構造函數。
eg:public class UtilityClass{
???? private UtilityClass(){
???? }
?? }
7.通過私有構造函數強化singleton屬性。singleton是指這樣的類,它只能實例化一次。singleton通常被用來代表那些本質上具有唯一性的系統組件,比如視頻顯示或者文件系統。
? eg:public class Elvis{
?????? public static final Elvis INSTANCE = new Elvis();
?????? private Elvis(){
?????? }
???? }
8.考慮用靜態工廠方法代替構造函數,但如果沒有其他強烈的因素,最好還是簡單的使用構造函數,畢竟它是語言規范。靜態工廠方法實際上是一個簡單的靜態方法,他返回的是類的一個實例。
? 有點:a.與構造函數不同,靜態工廠方法具有名字。
??????? b.與構造函數不同,它們每次被調用的時候不要求非得創建一個對象。
??????? c.與構造函數不同,與構造函數不同,它們可以返回一個原類型的子類型對象。
第二章 所有對象都通用的方法(equals(),hashCode(),toString(),clone(),Comparable接口)
一.按規則使用equals():
1.使用equals的規則:
? a.如果一個class的兩個對象占據不同的內存空間也可被視為邏輯相等的話,那么得為這個class提供一個equals()
? b.檢查是否等于this
? c.比較關鍵域以判斷兩個對象是否相等
? d.如果有java.lang.Object以外的任何base class實現了equals(),那么就應該調用super.equals()
? e.如果只允許同一個class所產生的對象被視為相等,則通常使用getClass()
??? eg1:一般情況
??? public boolean equals(Object obj){
??????? if(this == obj){
????????? return true;
??????? }
??????? if(obj != nul && getClass() == obj.getClass()){
????????? Test test = (Test)obj;
????????? if(***){//相等條件
????????????? return true;
????????? }
??????? }
??????? return false;
????? }
??? eg2:調用super.equals()情況
??? public boolean equals(Object obj){
????? if(super.equals(obj)){//已經包含了this == obj; obj !=null && getClass() == obj.getClass()的判斷
??????? Test test = (Test)obj;
????????? if(***){//相等條件
????????????? return true;
????????? }
??????? }
??????? return false;
????? }
? f.只有在不得不對derived class對象與base classes對象進行比較的場合中,才使用instanceof,并且你應該明白這樣做帶來的可能問題和復雜性,并且derived class和base classes都用instanceof實現equals()時,這種比較不會展現“對稱相等性”。
??? Base b;Derived d;//分別表示父類、子類
??? 1)父類實現equals,子類繼承父類的equals,b.equals(d) == d.equals(d);
??? 2)父類子類分別實現了equals,b.equals(d) != d.equals(b);
??? 3)父類未實現equals,子類實現了equals,b.equals(d) != d.equals(b);
2.對于既不是float也不是double類型的primitive types,使用==操作符;對于對象引用域,可以遞歸的調用equals方法;對于float域,先使用Float.floatToIntBits轉換成int類型值,然后使用==操作符比較int類型的值;對于double域,先使用Double.doubleToLongBits轉換成int類型的值,然后使用==操作符比較long類型的值.(這是由于存在Float.NaN、-0.0f以及類似的double類型的常量)
二.hashCode():
1。改寫equals時總是要改寫hashCode方法,如果不這樣作,會導致該類無法與所有基于散列值(hash)的集合類在一起正常工作,這樣的集合類包括HashMap、HashSet、HashTable
2。hashCode方法的簡單方法:
? 1。把某個非零數值(比如17),保存在int result變量里。
? 2。對于對象中每一個關鍵域f(指equals方法中考慮的每一個域),完成以下步驟:
? a)為該域計算int類型的散列碼c:
??? i.該域為boolean型,c = f ? 0 : 1
??? ii.byte, char, short, int型, c = (int)f
??? iii.long型, c = (int)(f ^ (f >>> 32))
??? iv.float型, c = Float.floatToIntBits(f)
??? v.double型, Double.doubleToLongBits(f)得到long型,然后按iii計算散列值
??? vi.如果是對象引用,c = (this.*** == null) ? 0 : this.***.hashCode();
??? vii.如果該域是個數組,則把其中每一個元素當作單獨的域來處理
?? b)result = 37 * result + c;//把每個c都組合到result中
?? 3。返回result
?? eg1:
?public int hashCode() {
???? int result = 17;
???? //對于關鍵域是id的情況
???? int idValue = (this.getId() == null) ? 0 : this.getId().hashCode();
???? result = (result * 37) + idValue;
???? //如果還有第二個關鍵域name
???? //int nameValue = (this.getName() == null) ? 0 : this.getName().hashCode();
???? //result = (result * 37) + nameValue;
???? this.hashValue = result;
?return this.hashValue;
?}
??? eg2:
?如果一個類是非可變的,并且計算散列碼代價較大,則應把散列碼存到對象內部:
?private int hashValue = 17;//先定義hashValue,不需要get/set方法
?........................
?public int hashCode() {//對于關鍵域是id的情況
???? if (this.hashValue == 17) {
??int result = 17;
??int idValue = (this.getId() == null) ? 0 : this.getId().hashCode();
??result = (result * 37) + idValue;
??//如果還有第二個關鍵域name
??//int nameValue = (this.getName() == null) ? 0 : this.getName().hashCode();
??//result = (result * 37) + nameValue;
??this.hashValue = result;
???? }
???? return this.hashValue;
?}
三。toString():會使這個類用起來更加方便。
四。謹慎的改寫clone()。實現拷貝的方法有兩個:一是實現cloneable接口(effective java 39頁,沒仔細看),二是提供拷貝構造函數
? public Yum(Yum yum);
? 或是上面的微小變形:提供一個靜態工廠來代替構造函數:
? public static Yum newInstance(Yum yum);
五、用到搜索、排序、計算極值的情況時,考慮實現Comparable接口。
public int compareTo(Object o)//方法不需要手工檢查參數的類型,如參數類型不符合會拋出ClassCastException;如參數為null,該方法拋出NullPointerException。
第三章 類和接口
1。使類和成員(變量、方法、內部類、內部接口)的可訪問能力最小化。
2。private和friendly成員都是一個類實現中的一部分,并不會影響到導出API。然而,如果這些域所在的類實現了Serializable接口,那么這些成員可能會被泄漏到導出API中。
3。如果一個方法改寫了超類中的一個方法,那么子類中該方法的訪問級別不能低于父類中該方法的訪問級別。特別是:類實現了接口,那么接口中的方法在這個類中必須聲明為公有的,因為接口中方法默認為public abstract。
六、異常處理
1.決不可忽略異常,即catch后什么也不做。
2.決不可掩蓋異常
try{
? e1;//異常1
? e2;//異常2
}catch(Exception e){
? e.printStackTrace()
}//只能捕獲異常2
辦法:要仔細分析,用棧來保存異常
3.覆寫異常處理時:
父類不拋出異常時,自類不能拋出異常。
父類拋出異常時,自類三種情況:a)不拋出異常b)拋出父類異常c)拋出父類異常的派生異常。
4.只要有finally塊就一定會進入,即使try-catch塊有return/break/continue語句。
5.養成將try/catch塊放在循環外的習慣,在不啟動JIT時節省時間。
posted @
2006-12-12 11:32 保爾任 閱讀(268) |
評論 (0) |
編輯 收藏
摘要: Java
反射機制
?
摘要
...
閱讀全文
posted @
2006-12-12 11:32 保爾任 閱讀(303) |
評論 (0) |
編輯 收藏
?在已發布的Java1.4中在核心代碼庫中增加了許多新的API(如Loging,正則表達式,NIO)等,在最新發布的JDK1.5和即將發布的JDK1.6中也新增了許多API,其中比較有重大意義的就是Generics(范型)。
一.什么是Generics?Generics可以稱之為參數類型(parameterized types),由編譯器來驗證從客戶端將一種類型傳送給某一對象的機制。如Java.util.ArrayList,編譯器可以用Generics來保證類型安全。
在我們深入了解Generics之前,我們先來看一看當前的java 集合框架(Collection)。在j2SE1.4中所有集合的Root Interface是Collection
Collections example without genericity: Example 1
1 protected void collectionsExample() {
2??ArrayList list = new ArrayList();
3??list.add(new String("test string"));
4??list.add(new Integer(9)); // purposely placed here to create a runtime ClassCastException
5??inspectCollection(list);
6 }
7
8
9 protected void inspectCollection(Collection aCollection) {
10??Iterator i = aCollection.iterator();
11??while (i.hasNext()) {
12?? String element = (String) i.next();
13??}
14 }
以上的樣例程序包含的兩個方法,collectionExample方法建立了一個簡單的集合類型ArrayList,并在ArrayList中增加了一個 String和一個Integer對象.而在inspecCollection方法中,我們迭代這個ArrayList用String進行Cast。我們看第二個方法,就出現了一個問題,Collection在內部用的是Object,而我們要取出Collection中的對象時,需要進行Cast,那么開發者必需用實際的類型進行Cast,像這種向下造型,編譯器無法進行檢查,如此一來我們就要冒在代碼在運行拋出ClassCastException的危險。我們看inspecCollection方法,編譯時沒有問題,但在運行時就會拋出ClassCastException異常。所以我們一定要遠離這個重大的運行時錯誤
二.使用Generics從上一章節中的CassCastException這種異常,我們期望在代碼編譯時就能夠捕捉到,下面我們使用范型修改上一章的樣例程序。
//Example 2
1 protected void collectionsExample() {
2??ArrayList<String> list = new ArrayList<String>();
3??list.add(new String("test string"));
4??// list.add(new Integer(9)); this no longer compiles
5??inspectCollection(list);
6 }
7
8
9 protected void inspectCollection(Collection<String> aCollection) {
10??Iterator<String> i = aCollection.iterator();
11??while(i.hasNext()) {
12?? String element = i.next();
13??}
14 }
從上面第2行我們在創建ArrayList時使用了新語法,在JDK1.5中所有的Collection都加入了Generics的聲明。例:
//Example 3
1 public class ArrayList<E> extends AbstractList<E> {
2??// details omitted...
3??public void add(E element) {
4?? // details omitted
5??}
6??public Iterator<E> iterator() {
7?? // details omitted
8??}
9 }
這個E是一個類型變量,并沒有對它進行具體類型的定義,它只是在定義ArrayList時的類型占位符,在Example 2中的我們在定義ArrayList的實例時用String綁定在E上,當我們用add(E element)方法向ArrayList中增加對象時, 那么就像下面的寫法一樣: public void add(String element);因為在ArrayList所有方法都會用String來替代E,無論是方法的參數還是返回值。這時我們在看Example 2中的第四行,編譯就會反映出編譯錯誤。
所以在java中增加Generics主要的目的是為了增加類型安全。
通過上面的簡單的例子我們看到使用Generics的好處有:
1.在類型沒有變化時,Collection是類型安全的。
2.內在的類型轉換優于在外部的人工造型。
3.使Java 接口更加強壯,因為它增加了類型。
4.類型的匹配錯誤在編譯階段就可以捕捉到,而不是在代碼運行時。
受約束類型變量
雖然許多Class被設計成Generics,但類型變量可以是受限的
public class C1<T extends Number> { }
public class C2<T extends Person & Comparable> { }
第一個T變量必須繼承Number,第二個T必須繼承Person和實現Comparable
三.Generics 方法像Generics類一樣,方法和構造函數也可以有類型參數。方法的參數和返回值都可以有類型參數,進行Generics。
//Example 4
1 public <T extends Comparable> T max(T t1, T t2) {
2??if (t1.compareTo(t2) > 0)
3?? return t1;
4??else return t2;
5 }
這里,max方法的參數類型為單一的T類型,而T類型繼承了Comparable,max的參數和返回值都有相同的超類。下面的Example 5顯示了max方法的幾個約束。
//Example 5
1 Integer iresult = max(new Integer(100), new Integer(200));
2 String sresult = max("AA", "BB");
3 Number nresult = max(new Integer(100), "AAA"); // does not compile
在Example 5第1行參數都為Integer,所以返回值也是Integer,注意返回值沒有進行造型。
在Example 5第2行參數都為String,所以返回值也是String,注意返回值沒有進行造型。以上都調用了同一個方法。
在Example 5第3行產生以下編譯錯誤:
Example.java:10: incompatible types
found??: java.lang.Object&java.io.Serializable&java.lang.Comparable<?>
required: java.lang.Number
????Number nresult = max(new Integer(100), "AAA");這個錯誤發生是因為編譯器無法確定返回值類型,因為String和Integer都有相同的超類Object,注意就算我們修正了第三行,這行代碼在運行仍然會報錯,因為比較了不同的對象。
四.向下兼容任何一個新的特色在新的JDK版本中出來后,我們首先關心的是如何于以前編寫的代碼兼容。也就是說我們編寫的Example 1程序不需要任何的改變就可以運行,但是編譯器會給出一個"ROW TYPE"的警告。
在JDK1.4中編寫的代碼如何在JVM1.5中完全兼容運行,我們要人工進行一個:Type erasure處理過程五.通配符//Example 6
List<String> stringList = new ArrayList<String>(); //1
List<Object> objectList = stringList ;//2
objectList .add(new Object()); // 3
String s = stringList .get(0);//4
乍一看,Example 6 是正確的。但stringList本意是存放String類型的ArrayList,而objectList中可以存入任何對象,當在第3行進行處理時, stringList也就無法保證是String類型的ArrayList,此時編譯器不允許這樣的事出現,所以第3行將無法編譯。
//Example 7
void printCollection(Collection<Object> c)
{ for (Object e : c) {
System.out.println(e);
}}
Example 7的本意是打印所有Collection的對象,但是正如Example 6所說的,編譯會報錯,此時就可以用通配符“?”來修改Example 7
//Example 8
void printCollection(Collection<?> c)
{ for (Object e : c) {
System.out.println(e);
}}
Example 8中所有Collection類型就可以方便的打印了
有界通配符 <T extends Number>(上界) <T super Number>(下界)
六.創建自己的范型以下代碼來自http://www.java2s.com/ExampleCode/Language-Basics
1.一個參數的Generics
//Example 9(沒有使用范型)
class NonGen {??
??Object ob; // ob is now of type Object
??// Pass the constructor a reference to??
??// an object of type Object
??NonGen(Object o) {??
????ob = o;??
??}??
??// Return type Object.
??Object getob() {??
????return ob;??
??}??
??// Show type of ob.??
??void showType() {??
????System.out.println("Type of ob is " +??
?????????????????????? ob.getClass().getName());??
??}??
}??
// Demonstrate the non-generic class.??
public class NonGenDemo {??
??public static void main(String args[]) {??
????NonGen iOb;??
????// Create NonGen Object and store
????// an Integer in it. Autoboxing still occurs.
????iOb = new NonGen(88);??
????// Show the type of data used by iOb.
????iOb.showType();
????// Get the value of iOb.
????// This time, a cast is necessary.
????int v = (Integer) iOb.getob();??
????System.out.println("value: " + v);??
????System.out.println();??
????// Create another NonGen object and??
????// store a String in it.
????NonGen strOb = new NonGen("Non-Generics Test");??
????// Show the type of data used by strOb.
????strOb.showType();
????// Get the value of strOb.
????// Again, notice that a cast is necessary.??
????String str = (String) strOb.getob();??
????System.out.println("value: " + str);??
????// This compiles, but is conceptually wrong!
????iOb = strOb;
????v = (Integer) iOb.getob(); // runtime error!
??}??
}
??
//Example 10(使用范型)
class Example1<T>{
private T t;
Example1(T o){
??this.t=o;
??}
T getOb(){
??return t;
}
void ShowObject(){
??System.out.println("對象的類型是:"+t.getClass().getName());
}
}
public class GenericsExample1 {
/**
??* @param args
??*/
public static void main(String[] args) {
??// TODO Auto-generated method stub
??Example1<Integer> examplei=new Example1<Integer>(100);
??examplei.ShowObject();
??System.out.println("對象是:"+examplei.getOb());
??Example1<String> examples=new Example1<String>("Bill");
??examples.ShowObject();
??System.out.println("對象是:"+examples.getOb());
}
}
我們來看Example 9沒有使用范型,所以我們需要進行造型,而Example 10我們不需要任何的造型
2.二個參數的Generics
//Example 11
class TwoGen<T, V> {
?? T ob1;
?? V ob2;
?? // Pass the constructor a reference to??
?? // an object of type T.
?? TwoGen(T o1, V o2) {
???? ob1 = o1;
???? ob2 = o2;
?? }
?? // Show types of T and V.
?? void showTypes() {
???? System.out.println("Type of T is " +
????????????????????????ob1.getClass().getName());
???? System.out.println("Type of V is " +
????????????????????????ob2.getClass().getName());
?? }
?? T getob1() {
???? return ob1;
?? }
?? V getob2() {
???? return ob2;
?? }
}
public class GenericsExampleByTwoParam {
/**
??* @param args
??*/
public static void main(String[] args) {
??// TODO Auto-generated method stub
??TwoGen<Integer, String> tgObj =
?????? new TwoGen<Integer, String>(88, "Generics");
???? // Show the types.
???? tgObj.showTypes();
???? // Obtain and show values.
???? int v = tgObj.getob1();
???? System.out.println("value: " + v);
???? String str = tgObj.getob2();
???? System.out.println("value: " + str);
?? }
}
3.Generics的Hierarchy
//Example 12
class Stats<T extends Number> {??
?? T[] nums; // array of Number or subclass
?? // Pass the constructor a reference to??
?? // an array of type Number or subclass.
?? Stats(T[] o) {??
???? nums = o;??
?? }??
?? // Return type double in all cases.
?? double average() {??
???? double sum = 0.0;
???? for(int i=0; i < nums.length; i++)??
?????? sum += nums[i].doubleValue();
???? return sum / nums.length;
?? }??
}??
public class GenericsExampleByHierarchy {
/**
??* @param args
??*/
public static void main(String[] args) {
??// TODO Auto-generated method stub
?? Integer inums[] = { 1, 2, 3, 4, 5 };
???? Stats<Integer> iob = new Stats<Integer>(inums);??
???? double v = iob.average();
???? System.out.println("iob average is " + v);
???? Double dnums[] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
???? Stats<Double> dob = new Stats<Double>(dnums);??
???? double w = dob.average();
???? System.out.println("dob average is " + w);
???? // This won't compile because String is not a
???? // subclass of Number.
//???? String strs[] = { "1", "2", "3", "4", "5" };
//???? Stats<String> strob = new Stats<String>(strs);??
//???? double x = strob.average();
//???? System.out.println("strob average is " + v);
?? }??
}
??
4.使用通配符
//Example 14
class StatsWildCard<T extends Number> {
T[] nums; // array of Number or subclass
// Pass the constructor a reference to
// an array of type Number or subclass.
StatsWildCard(T[] o) {
??nums = o;
}
// Return type double in all cases.
double average() {
??double sum = 0.0;
??for (int i = 0; i < nums.length; i++)
?? sum += nums[i].doubleValue();
??return sum / nums.length;
}
// Determine if two averages are the same.
// Notice the use of the wildcard.
boolean sameAvg(StatsWildCard<?> ob) {
??if (average() == ob.average())
?? return true;
??return false;
}
}
public class GenericsExampleByWildcard {
/**
??* @param args
??*/
public static void main(String[] args) {
??// TODO Auto-generated method stub
??Integer inums[] = { 1, 2, 3, 4, 5 };
??StatsWildCard<Integer> iob = new StatsWildCard<Integer>(inums);
??double v = iob.average();
??System.out.println("iob average is " + v);
??Double dnums[] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
??StatsWildCard<Double> dob = new StatsWildCard<Double>(dnums);
??double w = dob.average();
??System.out.println("dob average is " + w);
??Float fnums[] = { 1.0F, 2.0F, 3.0F, 4.0F, 5.0F };
??StatsWildCard<Float> fob = new StatsWildCard<Float>(fnums);
??double x = fob.average();
??System.out.println("fob average is " + x);
??// See which arrays have same average.
??System.out.print("Averages of iob and dob ");
??if (iob.sameAvg(dob))
?? System.out.println("are the same.");
??else
?? System.out.println("differ.");
??System.out.print("Averages of iob and fob ");
??if (iob.sameAvg(fob))
?? System.out.println("are the same.");
??else
?? System.out.println("differ.");
}
}
5.使用邊界通配符//Example 15
class TwoD {
??int x, y;
??TwoD(int a, int b) {
????x = a;
????y = b;
??}
}
// Three-dimensional coordinates.
class ThreeD extends TwoD {
??int z;
??ThreeD(int a, int b, int c) {
????super(a, b);
????z = c;
??}
}
// Four-dimensional coordinates.
class FourD extends ThreeD {
??int t;
??FourD(int a, int b, int c, int d) {
????super(a, b, c);
????t = d;??
??}
}
// This class holds an array of coordinate objects.
class Coords<T extends TwoD> {
??T[] coords;
??Coords(T[] o) { coords = o; }
}
// Demonstrate a bounded wildcard.
public class BoundedWildcard {
??static void showXY(Coords<?> c) {
????System.out.println("X Y Coordinates:");
????for(int i=0; i < c.coords.length; i++)
??????System.out.println(c.coords[i].x + " " +
???????????????????????? c.coords[i].y);
????System.out.println();
??}
??static void showXYZ(Coords<? extends ThreeD> c) {
????System.out.println("X Y Z Coordinates:");
????for(int i=0; i < c.coords.length; i++)
??????System.out.println(c.coords[i].x + " " +
???????????????????????? c.coords[i].y + " " +
???????????????????????? c.coords[i].z);
????System.out.println();
??}
??static void showAll(Coords<? extends FourD> c) {
????System.out.println("X Y Z T Coordinates:");
????for(int i=0; i < c.coords.length; i++)
??????System.out.println(c.coords[i].x + " " +
???????????????????????? c.coords[i].y + " " +
???????????????????????? c.coords[i].z + " " +
???????????????????????? c.coords[i].t);
????System.out.println();
??}
??public static void main(String args[]) {
????TwoD td[] = {
??????new TwoD(0, 0),
??????new TwoD(7, 9),
??????new TwoD(18, 4),
??????new TwoD(-1, -23)
????};
????Coords<TwoD> tdlocs = new Coords<TwoD>(td);????
????System.out.println("Contents of tdlocs.");
????showXY(tdlocs); // OK, is a TwoD
//??showXYZ(tdlocs); // Error, not a ThreeD
//??showAll(tdlocs); // Erorr, not a FourD
????// Now, create some FourD objects.
????FourD fd[] = {
??????new FourD(1, 2, 3, 4),
??????new FourD(6, 8, 14, 8),
??????new FourD(22, 9, 4, 9),
??????new FourD(3, -2, -23, 17)
????};
????Coords<FourD> fdlocs = new Coords<FourD>(fd);????
????System.out.println("Contents of fdlocs.");
????// These are all OK.
????showXY(fdlocs);??
????showXYZ(fdlocs);
????showAll(fdlocs);
??}
}
6.ArrayList的Generics
//Example 16
public class ArrayListGenericDemo {
??public static void main(String[] args) {
????ArrayList<String> data = new ArrayList<String>();
????data.add("hello");
????data.add("goodbye");
????// data.add(new Date()); This won't compile!
????Iterator<String> it = data.iterator();
????while (it.hasNext()) {
??????String s = it.next();
??????System.out.println(s);
????}
??}
}
7.HashMap的Generics
//Example 17
public class HashDemoGeneric {
??public static void main(String[] args) {
????HashMap<Integer,String> map = new HashMap<Integer,String>();
????map.put(1, "Ian");
????map.put(42, "Scott");
????map.put(123, "Somebody else");
????String name = map.get(42);
????System.out.println(name);
??}
}
8.接口的Generics
//Example 18
interface MinMax<T extends Comparable<T>> {
??T min();
??T max();
}
// Now, implement MinMax
class MyClass<T extends Comparable<T>> implements MinMax<T> {
??T[] vals;
??MyClass(T[] o) { vals = o; }
??// Return the minimum value in vals.
??public T min() {
????T v = vals[0];
????for(int i=1; i < vals.length; i++)
??????if(vals[i].compareTo(v) < 0) v = vals[i];
????return v;
??}
??// Return the maximum value in vals.
??public T max() {
????T v = vals[0];
????for(int i=1; i < vals.length; i++)
??????if(vals[i].compareTo(v) > 0) v = vals[i];
????return v;
??}
}
public class GenIFDemo {
??public static void main(String args[]) {
????Integer inums[] = {3, 6, 2, 8, 6 };
????Character chs[] = {'b', 'r', 'p', 'w' };
????MyClass<Integer> iob = new MyClass<Integer>(inums);
????MyClass<Character> cob = new MyClass<Character>(chs);
????System.out.println("Max value in inums: " + iob.max());
????System.out.println("Min value in inums: " + iob.min());
????System.out.println("Max value in chs: " + cob.max());
????System.out.println("Min value in chs: " + cob.min());
??}
}
9.Exception的Generics
//Example 20
interface Executor<E extends Exception> {
????void execute() throws E;
}
public class GenericExceptionTest {
????public static void main(String args[]) {
????????try {
????????????Executor<IOException> e =
????????????????new Executor<IOException>() {
????????????????public void execute() throws IOException
????????????????{
????????????????????// code here that may throw an
????????????????????// IOException or a subtype of
????????????????????// IOException
????????????????}
????????????};
????????????e.execute();
????????} catch(IOException ioe) {
????????????System.out.println("IOException: " + ioe);
????????????ioe.printStackTrace();
????????}
????}
}??
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=661415
posted @
2006-12-12 11:31 保爾任 閱讀(245) |
評論 (0) |
編輯 收藏
Java虛擬機的深入研究
作者:劉學超
1??Java技術與Java虛擬機
說起Java,人們首先想到的是Java編程語言,然而事實上,Java是一種技術,它由四方面組成: Java編程語言、Java類文件格式、Java虛擬機和Java應用程序接口(Java API)。它們的關系如下圖所示:
圖1??Java四個方面的關系
運行期環境代表著Java平臺,開發人員編寫Java代碼(.java文件),然后將之編譯成字節碼(.class文件)。最后字節碼被裝入內存,一旦字節碼進入虛擬機,它就會被解釋器解釋執行,或者是被即時代碼發生器有選擇的轉換成機器碼執行。從上圖也可以看出Java平臺由Java虛擬機和Java應用程序接口搭建,Java語言則是進入這個平臺的通道,用Java語言編寫并編譯的程序可以運行在這個平臺上。這個平臺的結構如下圖所示:
在Java平臺的結構中, 可以看出,Java虛擬機(JVM) 處在核心的位置,是程序與底層操作系統和硬件無關的關鍵。它的下方是移植接口,移植接口由兩部分組成:適配器和Java操作系統, 其中依賴于平臺的部分稱為適配器;JVM 通過移植接口在具體的平臺和操作系統上實現;在JVM 的上方是Java的基本類庫和擴展類庫以及它們的API, 利用Java API編寫的應用程序(application) 和小程序(Java applet) 可以在任何Java平臺上運行而無需考慮底層平臺, 就是因為有Java虛擬機(JVM)實現了程序與操作系統的分離,從而實現了Java 的平臺無關性。
那么到底什么是Java虛擬機(JVM)呢?通常我們談論JVM時,我們的意思可能是:
- 對JVM規范的的比較抽象的說明;
- 對JVM的具體實現;
- 在程序運行期間所生成的一個JVM實例。
對JVM規范的的抽象說明是一些概念的集合,它們已經在書《The Java Virtual Machine Specification》(《Java虛擬機規范》)中被詳細地描述了;對JVM的具體實現要么是軟件,要么是軟件和硬件的組合,它已經被許多生產廠商所實現,并存在于多種平臺之上;運行Java程序的任務由JVM的運行期實例單個承擔。在本文中我們所討論的Java虛擬機(JVM)主要針對第三種情況而言。它可以被看成一個想象中的機器,在實際的計算機上通過軟件模擬來實現,有自己想象中的硬件,如處理器、堆棧、寄存器等,還有自己相應的指令系統。
JVM在它的生存周期中有一個明確的任務,那就是運行Java程序,因此當Java程序啟動的時候,就產生JVM的一個實例;當程序運行結束的時候,該實例也跟著消失了。下面我們從JVM的體系結構和它的運行過程這兩個方面來對它進行比較深入的研究。
2??Java虛擬機的體系結構
剛才已經提到,JVM可以由不同的廠商來實現。由于廠商的不同必然導致JVM在實現上的一些不同,然而JVM還是可以實現跨平臺的特性,這就要歸功于設計JVM時的體系結構了。
我們知道,一個JVM實例的行為不光是它自己的事,還涉及到它的子系統、存儲區域、數據類型和指令這些部分,它們描述了JVM的一個抽象的內部體系結構,其目的不光規定實現JVM時它內部的體系結構,更重要的是提供了一種方式,用于嚴格定義實現時的外部行為。每個JVM都有兩種機制,一個是裝載具有合適名稱的類(類或是接口),叫做類裝載子系統;另外的一個負責執行包含在已裝載的類或接口中的指令,叫做運行引擎。每個JVM又包括方法區、堆、Java棧、程序計數器和本地方法棧這五個部分,這幾個部分和類裝載機制與運行引擎機制一起組成的體系結構圖為:
圖3??JVM的體系結構
JVM的每個實例都有一個它自己的方法域和一個堆,運行于JVM內的所有的線程都共享這些區域;當虛擬機裝載類文件的時候,它解析其中的二進制數據所包含的類信息,并把它們放到方法域中;當程序運行的時候,JVM把程序初始化的所有對象置于堆上;而每個線程創建的時候,都會擁有自己的程序計數器和Java棧,其中程序計數器中的值指向下一條即將被執行的指令,線程的Java棧則存儲為該線程調用Java方法的狀態;本地方法調用的狀態被存儲在本地方法棧,該方法棧依賴于具體的實現。
下面分別對這幾個部分進行說明。
執行引擎處于JVM的核心位置,在Java虛擬機規范中,它的行為是由指令集所決定的。盡管對于每條指令,規范很詳細地說明了當JVM執行字節碼遇到指令時,它的實現應該做什么,但對于怎么做卻言之甚少。Java虛擬機支持大約248個字節碼。每個字節碼執行一種基本的CPU運算,例如,把一個整數加到寄存器,子程序轉移等。Java指令集相當于Java程序的匯編語言。
Java指令集中的指令包含一個單字節的操作符,用于指定要執行的操作,還有0個或多個操作數,提供操作所需的參數或數據。許多指令沒有操作數,僅由一個單字節的操作符構成。
虛擬機的內層循環的執行過程如下:
do{
取一個操作符字節;
根據操作符的值執行一個動作;
}while(程序未結束)
由于指令系統的簡單性,使得虛擬機執行的過程十分簡單,從而有利于提高執行的效率。指令中操作數的數量和大小是由操作符決定的。如果操作數比一個字節大,那么它存儲的順序是高位字節優先。例如,一個16位的參數存放時占用兩個字節,其值為:
第一個字節*256+第二個字節字節碼。
指令流一般只是字節對齊的。指令tableswitch和lookup是例外,在這兩條指令內部要求強制的4字節邊界對齊。
對于本地方法接口,實現JVM并不要求一定要有它的支持,甚至可以完全沒有。Sun公司實現Java本地接口(JNI)是出于可移植性的考慮,當然我們也可以設計出其它的本地接口來代替Sun公司的JNI。但是這些設計與實現是比較復雜的事情,需要確保垃圾回收器不會將那些正在被本地方法調用的對象釋放掉。
Java的堆是一個運行時數據區,類的實例(對象)從中分配空間,它的管理是由垃圾回收來負責的:不給程序員顯式釋放對象的能力。Java不規定具體使用的垃圾回收算法,可以根據系統的需求使用各種各樣的算法。
Java方法區與傳統語言中的編譯后代碼或是Unix進程中的正文段類似。它保存方法代碼(編譯后的java代碼)和符號表。在當前的Java實現中,方法代碼不包括在垃圾回收堆中,但計劃在將來的版本中實現。每個類文件包含了一個Java類或一個Java界面的編譯后的代碼。可以說類文件是Java語言的執行代碼文件。為了保證類文件的平臺無關性,Java虛擬機規范中對類文件的格式也作了詳細的說明。其具體細節請參考Sun公司的Java虛擬機規范。
Java虛擬機的寄存器用于保存機器的運行狀態,與微處理器中的某些專用寄存器類似。Java虛擬機的寄存器有四種:
- pc: Java程序計數器;
- optop: 指向操作數棧頂端的指針;
- frame: 指向當前執行方法的執行環境的指針;。
- vars: 指向當前執行方法的局部變量區第一個變量的指針。
在上述體系結構圖中,我們所說的是第一種,即程序計數器,每個線程一旦被創建就擁有了自己的程序計數器。當線程執行Java方法的時候,它包含該線程正在被執行的指令的地址。但是若線程執行的是一個本地的方法,那么程序計數器的值就不會被定義。
Java虛擬機的棧有三個區域:局部變量區、運行環境區、操作數區。
局部變量區
每個Java方法使用一個固定大小的局部變量集。它們按照與vars寄存器的字偏移量來尋址。局部變量都是32位的。長整數和雙精度浮點數占據了兩個局部變量的空間,卻按照第一個局部變量的索引來尋址。(例如,一個具有索引n的局部變量,如果是一個雙精度浮點數,那么它實際占據了索引n和n+1所代表的存儲空間)虛擬機規范并不要求在局部變量中的64位的值是64位對齊的。虛擬機提供了把局部變量中的值裝載到操作數棧的指令,也提供了把操作數棧中的值寫入局部變量的指令。
運行環境區
在運行環境中包含的信息用于動態鏈接,正常的方法返回以及異常捕捉。
動態鏈接
運行環境包括對指向當前類和當前方法的解釋器符號表的指針,用于支持方法代碼的動態鏈接。方法的class文件代碼在引用要調用的方法和要訪問的變量時使用符號。動態鏈接把符號形式的方法調用翻譯成實際方法調用,裝載必要的類以解釋還沒有定義的符號,并把變量訪問翻譯成與這些變量運行時的存儲結構相應的偏移地址。動態鏈接方法和變量使得方法中使用的其它類的變化不會影響到本程序的代碼。
正常的方法返回
如果當前方法正常地結束了,在執行了一條具有正確類型的返回指令時,調用的方法會得到一個返回值。執行環境在正常返回的情況下用于恢復調用者的寄存器,并把調用者的程序計數器增加一個恰當的數值,以跳過已執行過的方法調用指令,然后在調用者的執行環境中繼續執行下去。
異常捕捉
異常情況在Java中被稱作Error(錯誤)或Exception(異常),是Throwable類的子類,在程序中的原因是:①動態鏈接錯,如無法找到所需的class文件。②運行時錯,如對一個空指針的引用。程序使用了throw語句。
當異常發生時,Java虛擬機采取如下措施:
- 檢查與當前方法相聯系的catch子句表。每個catch子句包含其有效指令范圍,能夠處理的異常類型,以及處理異常的代碼塊地址。
- 與異常相匹配的catch子句應該符合下面的條件:造成異常的指令在其指令范圍之內,發生的異常類型是其能處理的異常類型的子類型。如果找到了匹配的catch子句,那么系統轉移到指定的異常處理塊處執行;如果沒有找到異常處理塊,重復尋找匹配的catch子句的過程,直到當前方法的所有嵌套的catch子句都被檢查過。
- 由于虛擬機從第一個匹配的catch子句處繼續執行,所以catch子句表中的順序是很重要的。因為Java代碼是結構化的,因此總可以把某個方法的所有的異常處理器都按序排列到一個表中,對任意可能的程序計數器的值,都可以用線性的順序找到合適的異常處理塊,以處理在該程序計數器值下發生的異常情況。
- 如果找不到匹配的catch子句,那么當前方法得到一個"未截獲異常"的結果并返回到當前方法的調用者,好像異常剛剛在其調用者中發生一樣。如果在調用者中仍然沒有找到相應的異常處理塊,那么這種錯誤將被傳播下去。如果錯誤被傳播到最頂層,那么系統將調用一個缺省的異常處理塊。
操作數棧區
機器指令只從操作數棧中取操作數,對它們進行操作,并把結果返回到棧中。選擇棧結構的原因是:在只有少量寄存器或非通用寄存器的機器(如Intel486)上,也能夠高效地模擬虛擬機的行為。操作數棧是32位的。它用于給方法傳遞參數,并從方法接收結果,也用于支持操作的參數,并保存操作的結果。例如,iadd指令將兩個整數相加。相加的兩個整數應該是操作數棧頂的兩個字。這兩個字是由先前的指令壓進堆棧的。這兩個整數將從堆棧彈出、相加,并把結果壓回到操作數棧中。
每個原始數據類型都有專門的指令對它們進行必須的操作。每個操作數在棧中需要一個存儲位置,除了long和double型,它們需要兩個位置。操作數只能被適用于其類型的操作符所操作。例如,壓入兩個int類型的數,如果把它們當作是一個long類型的數則是非法的。在Sun的虛擬機實現中,這個限制由字節碼驗證器強制實行。但是,有少數操作(操作符dupe和swap),用于對運行時數據區進行操作時是不考慮類型的。
本地方法棧,當一個線程調用本地方法時,它就不再受到虛擬機關于結構和安全限制方面的約束,它既可以訪問虛擬機的運行期數據區,也可以使用本地處理器以及任何類型的棧。例如,本地棧是一個C語言的棧,那么當C程序調用C函數時,函數的參數以某種順序被壓入棧,結果則返回給調用函數。在實現Java虛擬機時,本地方法接口使用的是C語言的模型棧,那么它的本地方法棧的調度與使用則完全與C語言的棧相同。
3??Java虛擬機的運行過程
上面對虛擬機的各個部分進行了比較詳細的說明,下面通過一個具體的例子來分析它的運行過程。
虛擬機通過調用某個指定類的方法main啟動,傳遞給main一個字符串數組參數,使指定的類被裝載,同時鏈接該類所使用的其它的類型,并且初始化它們。例如對于程序:
class HelloApp
{
public static void main(String[] args)
{
System.out.println("Hello World!");
for (int i = 0; i < args.length; i++ )
{
System.out.println(args[i]);
}
}
}
編譯后在命令行模式下鍵入: java HelloApp run virtual machine
將通過調用HelloApp的方法main來啟動java虛擬機,傳遞給main一個包含三個字符串"run"、"virtual"、"machine"的數組。現在我們略述虛擬機在執行HelloApp時可能采取的步驟。
開始試圖執行類HelloApp的main方法,發現該類并沒有被裝載,也就是說虛擬機當前不包含該類的二進制代表,于是虛擬機使用ClassLoader試圖尋找這樣的二進制代表。如果這個進程失敗,則拋出一個異常。類被裝載后同時在main方法被調用之前,必須對類HelloApp與其它類型進行鏈接然后初始化。鏈接包含三個階段:檢驗,準備和解析。檢驗檢查被裝載的主類的符號和語義,準備則創建類或接口的靜態域以及把這些域初始化為標準的默認值,解析負責檢查主類對其它類或接口的符號引用,在這一步它是可選的。類的初始化是對類中聲明的靜態初始化函數和靜態域的初始化構造方法的執行。一個類在初始化之前它的父類必須被初始化。整個過程如下:
圖4:虛擬機的運行過程
4??結束語
本文通過對JVM的體系結構的深入研究以及一個Java程序執行時虛擬機的運行過程的詳細分析,意在剖析清楚Java虛擬機的機理。
posted @
2006-12-12 11:30 保爾任 閱讀(241) |
評論 (0) |
編輯 收藏
abstract class和interface是Java語言中對于抽象類定義進行支持的兩種機制,正是由于這兩種機制的存在,才賦予了Java強大的面向對象能力。abstract class和interface之間在對于抽象類定義的支持方面具有很大的相似性,甚至可以相互替換,因此很多開發者在進行抽象類定義時對于abstract class和interface的選擇顯得比較隨意。其實,兩者之間還是有很大的區別的,對于它們的選擇甚至反映出對于問題領域本質的理解、對于設計意圖的理解是否正確、合理。本文將對它們之間的區別進行一番剖析,試圖給開發者提供一個在二者之間進行選擇的依據。
理解抽象類
abstract class和interface在Java語言中都是用來進行抽象類(本文中的抽象類并非從abstract class翻譯而來,它表示的是一個抽象體,而abstract class為Java語言中用于定義抽象類的一種方法,請讀者注意區分)定義的,那么什么是抽象類,使用抽象類能為我們帶來什么好處呢?
在面向對象的概念中,我們知道所有的對象都是通過類來描繪的,但是反過來卻不是這樣。并不是所有的類都是用來描繪對象的,如果一個類中沒有包含足夠的信息來描繪一個具體的對象,這樣的類就是抽象類。抽象類往往用來表征我們在對問題領域進行分析、設計中得出的抽象概念,是對一系列看上去不同,但是本質上相同的具體概念的抽象。比如:如果我們進行一個圖形編輯軟件的開發,就會發現問題領域存在著圓、三角形這樣一些具體概念,它們是不同的,但是它們又都屬于形狀這樣一個概念,形狀這個概念在問題領域是不存在的,它就是一個抽象概念。正是因為抽象的概念在問題領域沒有對應的具體概念,所以用以表征抽象概念的抽象類是不能夠實例化的。
在面向對象領域,抽象類主要用來進行類型隱藏。我們可以構造出一個固定的一組行為的抽象描述,但是這組行為卻能夠有任意個可能的具體實現方式。這個抽象描述就是抽象類,而這一組任意個可能的具體實現則表現為所有可能的派生類。模塊可以操作一個抽象體。由于模塊依賴于一個固定的抽象體,因此它可以是不允許修改的;同時,通過從這個抽象體派生,也可擴展此模塊的行為功能。熟悉OCP的讀者一定知道,為了能夠實現面向對象設計的一個最核心的原則OCP(Open-Closed Principle),抽象類是其中的關鍵所在。
從語法定義層面看abstract class和interface
在語法層面,Java語言對于abstract class和interface給出了不同的定義方式,下面以定義一個名為Demo的抽象類為例來說明這種不同。
使用abstract class的方式定義Demo抽象類的方式如下:
abstract class Demo {
abstract void method1();
abstract void method2();
…
}
使用interface的方式定義Demo抽象類的方式如下:
interface Demo {
void method1();
void method2();
…
}
在abstract class方式中,Demo可以有自己的數據成員,也可以有非abstarct的成員方法,而在interface方式的實現中,Demo只能夠有靜態的不能被修改的數據成員(也就是必須是static final的,不過在interface中一般不定義數據成員),所有的成員方法都是abstract的。從某種意義上說,interface是一種特殊形式的abstract class。
從編程的角度來看,abstract class和interface都可以用來實現"design by contract"的思想。但是在具體的使用上面還是有一些區別的。
首先,abstract class在Java語言中表示的是一種繼承關系,一個類只能使用一次繼承關系。但是,一個類卻可以實現多個interface。也許,這是Java語言的設計者在考慮Java對于多重繼承的支持方面的一種折中考慮吧。
其次,在abstract class的定義中,我們可以賦予方法的默認行為。但是在interface的定義中,方法卻不能擁有默認行為,為了繞過這個限制,必須使用委托,但是這會 增加一些復雜性,有時會造成很大的麻煩。
在抽象類中不能定義默認行為還存在另一個比較嚴重的問題,那就是可能會造成維護上的麻煩。因為如果后來想修改類的界面(一般通過abstract class或者interface來表示)以適應新的情況(比如,添加新的方法或者給已用的方法中添加新的參數)時,就會非常的麻煩,可能要花費很多的時間(對于派生類很多的情況,尤為如此)。但是如果界面是通過abstract class來實現的,那么可能就只需要修改定義在abstract class中的默認行為就可以了。
同樣,如果不能在抽象類中定義默認行為,就會導致同樣的方法實現出現在該抽象類的每一個派生類中,違反了"one rule,one place"原則,造成代碼重復,同樣不利于以后的維護。因此,在abstract class和interface間進行選擇時要非常的小心。
從設計理念層面看abstract class和interface
上面主要從語法定義和編程的角度論述了abstract class和interface的區別,這些層面的區別是比較低層次的、非本質的。本小節將從另一個層面:abstract class和interface所反映出的設計理念,來分析一下二者的區別。作者認為,從這個層面進行分析才能理解二者概念的本質所在。
前面已經提到過,abstarct class在Java語言中體現了一種繼承關系,要想使得繼承關系合理,父類和派生類之間必須存在"is a"關系,即父類和派生類在概念本質上應該是相同的(參考文獻〔3〕中有關于"is a"關系的大篇幅深入的論述,有興趣的讀者可以參考)。對于interface 來說則不然,并不要求interface的實現者和interface定義在概念本質上是一致的,僅僅是實現了interface定義的契約而已。為了使論述便于理解,下面將通過一個簡單的實例進行說明。
考慮這樣一個例子,假設在我們的問題領域中有一個關于Door的抽象概念,該Door具有執行兩個動作open和close,此時我們可以通過abstract class或者interface來定義一個表示該抽象概念的類型,定義方式分別如下所示:
使用abstract class方式定義Door:
abstract class Door {
abstract void open();
abstract void close();
}
使用interface方式定義Door:
interface Door {
void open();
void close();
}
其他具體的Door類型可以extends使用abstract class方式定義的Door或者implements使用interface方式定義的Door。看起來好像使用abstract class和interface沒有大的區別。
如果現在要求Door還要具有報警的功能。我們該如何設計針對該例子的類結構呢(在本例中,主要是為了展示abstract class和interface反映在設計理念上的區別,其他方面無關的問題都做了簡化或者忽略)?下面將羅列出可能的解決方案,并從設計理念層面對這些不同的方案進行分析。
解決方案一:
簡單的在Door的定義中增加一個alarm方法,如下:
abstract class Door {
abstract void open();
abstract void close();
abstract void alarm();
}
或者
interface Door {
void open();
void close();
void alarm();
}
那么具有報警功能的AlarmDoor的定義方式如下:
class AlarmDoor extends Door {
void open() { … }
void close() { … }
void alarm() { … }
}
或者
class AlarmDoor implements Door {
void open() { … }
void close() { … }
void alarm() { … }
}
這種方法違反了面向對象設計中的一個核心原則ISP(Interface Segregation Priciple),在Door的定義中把Door概念本身固有的行為方法和另外一個概念"報警器"的行為方法混在了一起。這樣引起的一個問題是那些僅僅依賴于Door這個概念的模塊會因為"報警器"這個概念的改變(比如:修改alarm方法的參數)而改變,反之依然。
解決方案二:
既然open、close和alarm屬于兩個不同的概念,根據ISP原則應該把它們分別定義在代表這兩個概念的抽象類中。定義方式有:這兩個概念都使用abstract class方式定義;兩個概念都使用interface方式定義;一個概念使用abstract class方式定義,另一個概念使用interface方式定義。
顯然,由于Java語言不支持多重繼承,所以兩個概念都使用abstract class方式定義是不可行的。后面兩種方式都是可行的,但是對于它們的選擇卻反映出對于問題領域中的概念本質的理解、對于設計意圖的反映是否正確、合理。我們一一來分析、說明。
如果兩個概念都使用interface方式來定義,那么就反映出兩個問題:1、我們可能沒有理解清楚問題領域,AlarmDoor在概念本質上到底是Door還是報警器?2、如果我們對于問題領域的理解沒有問題,比如:我們通過對于問題領域的分析發現AlarmDoor在概念本質上和Door是一致的,那么我們在實現時就沒有能夠正確的揭示我們的設計意圖,因為在這兩個概念的定義上(均使用interface方式定義)反映不出上述含義。
如果我們對于問題領域的理解是:AlarmDoor在概念本質上是Door,同時它有具有報警的功能。我們該如何來設計、實現來明確的反映出我們的意思呢?前面已經說過,abstract class在Java語言中表示一種繼承關系,而繼承關系在本質上是"is a"關系。所以對于Door這個概念,我們應該使用abstarct class方式來定義。另外,AlarmDoor又具有報警功能,說明它又能夠完成報警概念中定義的行為,所以報警概念可以通過interface方式定義。如下所示:
abstract class Door {
abstract void open();
abstract void close();
}
interface Alarm {
void alarm();
}
class AlarmDoor extends Door implements Alarm {
void open() { … }
void close() { … }
void alarm() { … }
}
這種實現方式基本上能夠明確的反映出我們對于問題領域的理解,正確的揭示我們的設計意圖。其實abstract class表示的是"is a"關系,interface表示的是"like a"關系,大家在選擇時可以作為一個依據,當然這是建立在對問題領域的理解上的,比如:如果我們認為AlarmDoor在概念本質上是報警器,同時又具有Door的功能,那么上述的定義方式就要反過來了。
?
結論
abstract class和interface是Java語言中的兩種定義抽象類的方式,它們之間有很大的相似性。但是對于它們的選擇卻又往往反映出對于問題領域中的概念本質的理解、對于設計意圖的反映是否正確、合理,因為它們表現了概念間的不同的關系(雖然都能夠實現需求的功能)。這其實也是語言的一種的慣用法。
posted @
2006-12-12 11:29 保爾任 閱讀(243) |
評論 (0) |
編輯 收藏