<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    java世界
    有些人注定要生活在彼岸,可以親近可以愛憐,甚至可以窮盡一生去思念,只是無法觸及有些距離,注定不能跨越只能倆倆相望,就像有些愛只能養在心里長在眼中,不能捧在手里放在身邊,注定只能邂逅無法遭遇!
    posts - 12,comments - 15,trackbacks - 0
    建議只是建議而已。
    ?1. 你們的項目組使用源代碼管理工具了么?
    應該用。VSS、CVS、PVCS、ClearCase、CCC/Harvest、FireFly都可以。我的選擇是VSS。
    ?
    2. 你們的項目組使用缺陷管理系統了么?
    ?應該用。ClearQuest太復雜,我的推薦是BugZilla。

    3. 你們的測試組還在用Word寫測試用例么? 
    ?不要用Word寫測試用例(Test Case)。應該用一個專門的系統,可以是Test Manager,也可以是自己開發一個ASP.NET的小網站。主要目的是Track和Browse。
    ?
    4. 你們的項目組有沒有建立一個門戶網站? 
     要有一個門戶網站,用來放Contact Info、Baselined Schedule、News等等。推薦Sharepoint Portal Server 2003來實現,15分鐘就搞定。買不起SPS 2003可以用WSS (Windows Sharepoint Service)。

    5. 你們的項目組用了你能買到最好的工具么? 
     應該用盡量好的工具來工作。比如,應該用VS.NET而不是Notepad來寫C#。用Notepad寫程序多半只是一種炫耀。但也要考慮到經費,所以說是"你能買到最好的"。
    ?
    6. 你們的程序員工作在安靜的環境里么? 
     需要安靜環境。這點極端重要,而且要保證每個人的空間大于一定面積。
    ?
    7. 你們的員工每個人都有一部電話么?
    需要每人一部電話。而且電話最好是帶留言功能的。當然,上這么一套帶留言電話系統開銷不小。不過至少每人一部電話要有,千萬別搞得經常有人站起來喊:"某某某電話"。《人件》里面就強烈譴責這種做法。
    ?
    8. 你們每個人都知道出了問題應該找誰么? 
     應該知道。任何一個Feature至少都應該有一個Owner,當然,Owner可以繼續Dispatch給其他人。

    9. 你遇到過有人說"我以為…"么? 
     要消滅"我以為"。Never assume anything。
    ?
    10. 你們的項目組中所有的人都坐在一起么? 
     需要。我反對Virtual Team,也反對Dev在美國、Test在中國這種開發方式。能坐在一起就最好坐在一起,好處多得不得了。
    ?
    11. 你們的進度表是否反映最新開發進展情況?  
     應該反映。但是,應該用Baseline的方法來管理進度表:維護一份穩定的Schedule,再維護一份最新更改。Baseline的方法也應該用于其它的Spec。Baseline是變更管理里面的一個重要手段。
    ?
    12. 你們的工作量是先由每個人自己估算的么? 
     應該讓每個人自己估算。要從下而上估算工作量,而不是從上往下分派。除非有其他原因,比如政治任務工期固定等。
    ?
    13. 你們的開發人員從項目一開始就加班么? 
     不要這樣。不要一開始就搞疲勞戰。從項目一開始就加班,只能說明項目進度不合理。當然,一些對日軟件外包必須天天加班,那屬于剝削的范疇。
    ?
    14. 你們的項目計劃中Buffer Time是加在每個小任務后面的么? 
     不要。Buffer Time加在每個小任務后面,很容易輕易的就被消耗掉。Buffer Time要整段的加在一個Milestone或者checkpoint前面。
    ?
    15. 值得再多花一些時間,從95%做到100%好值得,非常值得。 
     尤其當項目后期人困馬乏的時候,要堅持。這會給產品帶來質的區別。
    ?
    16. 登記新缺陷時,是否寫清了重現步驟?
     要。這屬于Dev和Test之間的溝通手段。面對面溝通需要,詳細填寫Repro Steps也需要。
    ?
    17. 寫新代碼前會把已知缺陷解決么?
    ?要。每個人的缺陷不能超過10個或15個,否則必須先解決老的bug才能繼續寫新代碼。
    ?
    18. 你們對缺陷的輕重緩急有事先的約定么? 
     必須有定義。Severity要分1、2、3,約定好:藍屏和Data Lost算Sev 1,Function Error算Sev 2,界面上的算Sev 3。但這種約定可以根據產品質量現狀適當進行調整。
    ?
    19. 你們對意見不一的缺陷有三國會議么?
    ?必須要有。要有一個明確的決策過程。這類似于CCB (Change Control Board)的概念。
    ?
    20. 所有的缺陷都是由登記的人最后關閉的么?  
     Bug應該由Opener關閉。Dev不能私自關閉Bug。
    ?
    21. 你們的程序員厭惡修改老的代碼么? 
     厭惡是正常的。解決方法是組織Code Review,單獨留出時間來。XP也是一個方法。
    ?
    22. 你們項目組有Team Morale Activity么? 
     每個月都要搞一次,吃飯、唱歌、Outing、打球、開卡丁車等等,一定要有。不要剩這些錢。
    ?
    23. 你們項目組有自己的Logo么? 
     要有自己的Logo。至少應該有自己的Codename。
    ?
    24. 你們的員工有印有公司Logo的T-Shirt么? 
     要有。能增強歸屬感。當然,T-Shirt要做的好看一些,最好用80支的棉來做。別沒穿幾次就破破爛爛的。

    25. 總經理至少每月參加次項目組會議要的。 
     要讓team member覺得高層關注這個項目。
    ?
    26. 你們是給每個Dev開一個分支么? 
     反對。Branch的管理以及Merge的工作量太大,而且容易出錯。
    ?
    27. 有人長期不Check-In代碼么? 
     不可以。對大部分項目來說,最多兩三天就應該Check-In。
    ?
    28. 在Check-In代碼時都填寫注釋了么? 
     要寫的,至少一兩句話,比如"解決了Bug No.225(給bug編號)"。如果往高處拔,這也算做"配置審計"的一部分。
    ?
    29. 有沒有設定每天Check-In的最后期限? 
     要的,要明確Check-In Deadline。否則會Build Break。
    ?
    30. 你們能把所有源碼一下子編譯成安裝文件嗎?  
     要的。這是每日編譯(Daily Build)的基礎。而且必須要能夠做成自動的。
    ?
    31. 你們的項目組做每日編譯么? 
     當然要做。有三樣東西是軟件項目/產品開發必備的:1. bug management; 2. source control; 3. daily build。

    32. 你們公司有沒有積累一個項目風險列表? 
     要。Risk Inventory。否則,下個項目開始的時候,又只能拍腦袋分析Risk了。
    ?
    33. 設計越簡單越好越簡單越好。 
     設計時候多一句話,將來可能就帶來無窮無盡的煩惱。應該從一開始就勇敢的砍。這叫scope management。

    34. 盡量利用現有的產品、技術、代碼千萬別什么東西都自己Coding。
    BizTalk和Sharepoint就是最好的例子,有這兩個作為基礎,可以把起點提高很多。或者可以盡量多用現成的Control之類的。或者盡量用XML,而不是自己去Parse一個文本文件;盡量用RegExp,而不是自己從頭操作字符串,等等等等。這就是"軟件復用"的體現。
    ?
    35. 你們會隔一段時間就停下來夯實代碼么? 
     要。最好一個月左右一次。傳言去年年初Windows組在Stevb的命令下停過一個月增強安全。Btw,"夯"這個字念"hang",第一聲。

    36. 你們的項目組每個人都寫Daily Report么? 
     要寫。五分鐘就夠了,寫10句話左右,告訴自己小組的人今天我干了什么。一則為了溝通,二則鞭策自己(要是游手好閑一天,自己都會不好意思寫的)。

    37. 你們的項目經理會發出Weekly Report么? 
     要。也是為了溝通。內容包括目前進度,可能的風險,質量狀況,各種工作的進展等。
    ?
    38. 你們項目組是否至少每周全體開會一次? 
     要。一定要開會。程序員討厭開會,但每個禮拜開會時間加起來至少應該有4小時。包括team meeting, spec review meeting, bug triage meeting。千萬別大家悶頭寫code。
    ?
    39. 你們項目組的會議、討論都有記錄么? 
     會前發meeting request和agenda,會中有人負責主持和記錄,會后有人負責發meeting minutes,這都是effective meeting的要點。而且,每個會議都要形成agreements和action items。
    ?
    40. 其他部門知道你們項目組在干什么么? 
     要發一些Newsflash給整個大組織。Show your team's value。否則,當你坐在電梯里面,其他部門的人問:"你們在干嘛",你回答"ABC項目"的時候,別人全然不知,那種感覺不太好。
    ?
    41. 通過Email進行所有正式溝通
    Email的好處是免得抵賴。但也要避免矯枉過正,最好的方法是先用電話和當面說,然后Email來確認。

    42. 為項目組建立多個Mailing Group  
     如果在AD+Exchange里面,就建Distribution List。比如,我會建ABC Project Core Team,ABC Project Dev Team,ABC Project All Testers,ABC Project Extended Team等等。這樣發起Email來方便,而且能讓該收到email的人都收到、不該收到不被騷擾。
    ?
    43. 每個人都知道哪里可以找到全部的文檔么? 
     應該每個人都知道。這叫做知識管理(Knowledge Management)。最方便的就是把文檔放在一個集中的File Share,更好的方法是用Sharepoint。
    ?
    44. 你做決定、做變化時,告訴大家原因了么? 
     要告訴大家原因。Empower team member的手段之一是提供足夠的information,這是MSF一開篇的幾個原則之一。的確如此,tell me why是人之常情,tell me why了才能有understanding。中國人做事喜歡搞限制,限制信息,似乎能夠看到某一份文件的人就是有身份的人。大錯特錯。權威、權力,不在于是不是能access information/data,而在于是不是掌握資源。
    ?
    45. Stay agile and expect change 要這樣。 
     需求一定會變的,已經寫好的代碼一定會被要求修改的。做好心理準備,對change不要抗拒,而是expect change。
    ?
    46. 你們有沒有專職的軟件測試人員? 
     要有專職測試。如果人手不夠,可以peer test,交換了測試。千萬別自己測試自己的。
    ?
    47. 你們的測試有一份總的計劃來規定做什么和怎么做么?
    ?這就是Test Plan。要不要做性能測試?要不要做Usability測試?什么時候開始測試性能?測試通過的標準是什么?用什么手段,自動的還是手動的?這些問題需要用Test Plan來回答。
    ?
    48. 你是先寫Test Case然后再測試的么? 
     應該如此。應該先設計再編程、先test case再測試。當然,事情是靈活的。我有時候在做第一遍測試的同時補上test case。至于先test case再開發,我不喜歡,因為不習慣,太麻煩,至于別人推薦,那試試看也無妨。
    ?
    49. 你是否會為各種輸入組合創建測試用例? 
     不要,不要搞邊界條件組合。當心組合爆炸。有很多test case工具能夠自動生成各種邊界條件的組合--但要想清楚,你是否有時間去運行那么多test case。

    50. 你們的程序員能看到測試用例么? 
     要。讓Dev看到Test Case吧。我們都是為了同一個目的走到一起來的:提高質量。

    51. 你們是否隨便抓一些人來做易用性測試?  
     要這么做。自己看自己寫的程序界面,怎么看都是順眼的。這叫做審美疲勞--臭的看久了也就不臭了,不方便的永久了也就習慣了。
    ?
    52. 你對自動測試的期望正確么? 
     別期望太高。依我看,除了性能測試以外,還是暫時先忘掉"自動測試"吧,忘掉WinRunner和LoadRunner吧。對于國內的軟件測試的現狀來說,只能"矯枉必須過正"了。

    53. 你們的性能測試是等所有功能都開發完才做的么? 
     不能這樣。性能測試不能被歸到所謂的"系統測試"階段。早測早改正,早死早升天。
    ?
    54. 你注意到測試中的殺蟲劑效應了么? 
     蟲子有抗藥性,Bug也有。發現的新Bug越來越少是正常的。這時候,最好大家交換一下測試的area,或者用用看其他工具和手法,就又會發現一些新bug了。
    ?
    55. 你們項目組中有人能說出產品的當前整體質量情況么? 
     要有。當老板問起這個產品目前質量如何,Test Lead/Manager應該負責回答。
    ?
    56. 你們有單元測試么? 
     單元測試要有的。不過沒有單元測試也不是不可以,我做過沒有單元測試的項目,也做成功了--可能是僥幸,可能是大家都是熟手的關系。還是那句話,軟件工程是非常實踐、非常工程、非常靈活的一套方法,某些方法在某些情況下會比另一些方法好,反之亦然。

    57. 你們的程序員是寫完代碼就扔過墻的么? 
     大忌。寫好一塊程序以后,即便不做單元測試,也應該自己先跑一跑。雖然有了專門的測試人員,做開發的人也不可以一點測試都不做。微軟還有Test Release Document的說法,程序太爛的話,測試有權踢回去。
    ?
    58. 你們的程序中所有的函數都有輸入檢查么? 
     不要。雖然說做輸入檢查是write secure code的要點,但不要做太多的輸入檢查,有些內部函數之間的參數傳遞就不必檢查輸入了,省點功夫。同樣的道理,未必要給所有的函數都寫注釋。寫一部分主要的就夠了。
    ?
    59. 產品有統一的錯誤處理機制和報錯界面么? 
     要有。最好能有統一的error message,然后每個error message都帶一個error number。這樣,用戶可以自己根據error number到user manual里面去看看錯誤的具體描述和可能原因,就像SQL Server的錯誤那樣。同樣,ASP.NET也要有統一的Exception處理。可以參考有關的Application Block。
    ?
    60. 你們有統一的代碼書寫規范么? 
     要有。Code Convention很多,搞一份來發給大家就可以了。當然,要是有FxCop這種工具來檢查代碼就更好了。
    ?
    61. 你們的每個人都了解項目的商業意義么? 
     要。這是Vision的意思。別把項目只當成工作。有時候要想著自己是在為中國某某行業的信息化作先驅者,或者時不時的告訴team member,這個項目能夠為某某某國家部門每年節省多少多少百萬的納稅人的錢,這樣就有動力了。平凡的事情也是可以有個崇高的目標的。
    ?
    62. 產品各部分的界面和操作習慣一致么? 
     要這樣。要讓用戶覺得整個程序好像是一個人寫出來的那樣。
    ?
    63. 有可以作為宣傳亮點的Cool Feature么? 
     要。這是增強團隊凝聚力、信心的。而且,"一俊遮百丑",有亮點就可以掩蓋一些問題。這樣,對于客戶來說,會感覺產品從質量角度來說還是acceptable的。或者說,cool feature或者說亮點可以作為質量問題的一個事后彌補措施。
    ?
    64. 盡可能縮短產品的啟動時間要這樣。 
     軟件啟動時間(Start-Up time)是客戶對性能好壞的第一印象。
    ?
    65. 不要過于注重內在品質而忽視了第一眼的外在印象程序員容易犯這個錯誤:太看重性能、穩定性、存儲效率,但忽視了外在感受。而高層經理、客戶正相反。這兩方面要兼顧,協調這些是PM的工作。
    ?
    66. 你們根據詳細產品功能說明書做開發么? 
     要這樣。要有設計才能開發,這是必須的。設計文檔,應該說清楚這個產品會怎么運行,應該采取一些講故事的方法。設計的時候千萬別鉆細節,別鉆到數據庫、代碼等具體實現里面去,那些是后面的事情,一步步來不能著急。
    ?
    67. 開始開發和測試之前每個人都仔細審閱功能設計么? 
     要做。Function Spec review是用來統一思想的。而且,review過以后形成了一致意見,將來再也沒有人可以說"你看,當初我就是反對這么設計的,現在吃苦頭了吧"

    68. 所有人都始終想著The Whole Image么?
    要這樣。項目里面每個人雖然都只是在制造一片葉子,但每個人都應該知道自己在制造的那片葉子所在的樹是怎么樣子的。我反對軟件藍領,反對過分的把軟件制造看成流水線、車間。參見第61條。
    ?
    69. Dev工作的劃分是單純縱向或橫向的么? 
     不能單純的根據功能模塊分,或者單純根據表現層、中間層、數據庫層分。我推薦這么做:首先根據功能模塊分,然后每個"層"都有一個Owner來Review所有人的設計和代碼,保證consistency。
    ?
    70. 你們的程序員寫程序設計說明文檔么? 
     要。不過我聽說微軟的程序員1999年以前也不寫。所以說,寫不寫也不是絕對的,偷懶有時候也是可以的。參見第56條。
    ?
    71. 你在招人面試時讓他寫一段程序么? 
     要的。我最喜歡讓人做字符串和鏈表一類的題目。這種題目有很多循環、判斷、指針、遞歸等,既不偏向過于考算法,也不偏向過于考特定的API。
    ?
    72. 你們有沒有技術交流講座? 
     要的。每一兩個禮拜搞一次內部的Tech Talk或者Chalk Talk吧。讓組員之間分享技術心得,這筆花錢送到外面去培訓劃算。

    73. 你們的程序員都能專注于一件事情么? 
     要讓程序員專注一件事。例如說,一個部門有兩個項目和10個人,一種方法是讓10個人同時參加兩個項目,每個項目上每個人都花50%時間;另一種方法是5個人去項目A,5個人去項目B,每個人都100%在某一個項目上。我一定選后面一種。這個道理很多人都懂,但很多領導實踐起來就把屬下當成可以任意拆分的資源了。
    ?
    74. 你們的程序員會夸大完成某項工作所需要的時間么? 
     會的,這是常見的,尤其會在項目后期夸大做某個change所需要的時間,以次來抵制change。解決的方法是坐下來慢慢磨,磨掉程序員的逆反心理,一起分析,并把估算時間的顆粒度變小。
    ?
    75. 盡量不要用Virtual Heads 最好不要用Virtual Heads。  Virtual heads意味著resource is not secure,shared resource會降低resource的工作效率,容易增加出錯的機會,會讓一心二用的人沒有太多時間去review spec、review design。一個dedicated的人,要強過兩個只能投入50%時間和精力的人。我是吃過虧的:7個part time的tester,發現的Bug和干的活,加起來還不如兩個full-time的。參見第73條。73條是針對程序員的,75條是針對Resource Manager的。
    ?

    記住建議就是建議!!!
    posted @ 2006-07-02 19:30 安德爾斯 閱讀(603) | 評論 (0)編輯 收藏

    我們在www.sync4j.org網站上下載Sync4j的源代碼,解壓到任一目錄,其中只有sync4j這個子目錄的元源碼是我們需要的,其他的目錄中的代碼是一些工具代碼什么的,暫時用不到,用到的時候在拷貝進工程就可以了。

    我們的目的是在eclipse中創建一個工程,把Sync4j主要的代碼放進去,能夠編譯通過,哈哈其實這個沒什么,只是有一些注意事項:
    1.在eclipse中新建一個工程,通過模板“java project”創建;
    2.在這個新的工程中,新建一個“source folder”,通過文件系統導入Sync4j的源代碼,并把工程的源代碼目錄指向Sync4j/src/java子目錄;
    3.定義一個新的“external tool”生成ant build.xml file;
    4.在工程的“build path”中添加所有需要的jar包到lib目錄;
    5.添加ant.jar和j2ee.jar包到lib中;
    6.定義一個新的jre并且配置好assert,assert(俗稱斷言)是在JDK1.4引入的關鍵字,用于判斷值是否為真,當不為真時,就拋出異常,eclipse默認時assert是關閉的,你需要在eclipse中的設置界面里的java->compiler里把assert打開;
    7.編輯sync4j-build.properties文件從你的home目錄;

    文件的內容可能這樣:
    sunj2eesdk-rootdir=c:/Sun/AppServer
    jdom.jar=c:/jar/jdom.jar
    junit.jar=c:/jar/junit.jar
    cactus.home=c:/jar/jakarta-cactus

    dbms=mysql
    jdbc.classpath=c:/jar/mysql-connector-java-3.0.14-production-bin.jar
    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost/sync4j
    jdbc.user=sync4j
    jdbc.password=sync4j

    server=jboss
    server.jboss.deploy=c:/dev/jboss-3.0.8/server/default/deploy

    最后你可以用ant編譯并部署到你的應用服務器上,就可以進行手機,pda,windows mbile等移動設備的數據同步開發。

    posted @ 2005-11-26 00:33 安德爾斯 閱讀(1281) | 評論 (7)編輯 收藏

    前言:

    在我們學習Java的過程中,掌握其中的基本概念對我們的學習無論是J2SE,J2EE,J2ME都是很重要的,J2SE是Java的基礎,所以有必要對其中的基本概念做以歸納,以便大家在以后的學習過程中更好的理解java的精髓,在此我總結了30條基本的概念.

    Java概述:

    目前Java主要應用于中間件的開發(middleware)---處理客戶機于服務器之間的通信技術,早期的實踐證明,Java不適合pc應用程序的開發,其發展逐漸變成在開發手持設備,互聯網信息站,及車載計算機的開發.Java于其他語言所不同的是程序運行時提供了平臺的獨立性,稱許可以在windows,solaris,linux其他操作系統上使用完全相同的代碼.Java的語法與C++語法類似,C++/C程序員很容易掌握,而且Java是完全的徹底的面向對象的,其中提出了很好的GC(Garbage Collector)垃圾處理機制,防止內存溢出.

    Java的白皮書為我們提出了Java語言的關鍵特性.

    (1)Easy:Java的語法比C++的相對簡單,另一個方面就是Java能使軟件在很小的機器上運行,基礎解釋其和類庫的支持的大小約為40kb,增加基本的標準庫和線程支持的內存需要增加125kb.
    (2)分布式:Java帶有很強大的TCP/IP協議族的例程庫,Java應用程序能夠通過URL來穿過網絡來訪問遠程對象,由于servlet機制的出現,使Java編程非常的高效,現在許多的大的web server都支持servlet.
    (3)OO:面向對象設計是把重點放在對象及對象的接口上的一個編程技術.其面向對象和C++有很多不同,在與多重繼承的處理及Java的原類模型.
    (4)健壯特性:Java采取了一個安全指針模型,能減小重寫內存和數據崩潰的可能型
    (5)安全:Java用來設計網路和分布系統,這帶來了新的安全問題,Java可以用來構建防病毒和防攻擊的System.事實證明Java在防毒這一方面做的比較好.
    (6)中立體系結構:Java編譯其生成體系結構中立的目標文件格式可以在很多處理器上執行,編譯器產生的指令字節碼(Javabytecode)實現此特性,此字節碼可以在任何機器上解釋執行.
    (7)可移植性:Java中對基本數據結構類型的大小和算法都有嚴格的規定所以可移植性很好.
    (8)多線程:Java處理多線程的過程很簡單,Java把多線程實現交給底下操作系統或線程程序完成.所以多線程是Java作為服務器端開發語言的流行原因之一
    (9)Applet和servlet:能夠在網頁上執行的程序叫Applet,需要支持Java的瀏覽器很多,而applet支持動態的網頁,這是很多其他語言所不能做到的.

    基本概念:

    1.OOP中惟一關系的是對象的接口是什么,就像計算機的銷售商她不管電源內部結構是怎樣的,他只關系能否給你提供電就行了,也就是只要知道can or not而不是how and why.所有的程序是由一定的屬性和行為對象組成的,不同的對象的訪問通過函數調用來完成,對象間所有的交流都是通過方法調用,通過對封裝對象數據,很大限度上提高復用率.

    2.OOP中最重要的思想是類,類是模板是藍圖,從類中構造一個對象,即創建了這個類的一個實例(instance)

    3.封裝:就是把數據和行為結合起在一個包中)并對對象使用者隱藏數據的實現過程,一個對象中的數據叫他的實例字段(instance field)

    4.通過擴展一個類來獲得一個新類叫繼承(inheritance),而所有的類都是由Object根超類擴展而得,根超類下文會做介紹.

    5.對象的3個主要特性
    behavior---說明這個對象能做什么.
    state---當對象施加方法時對象的反映.

    identity---與其他相似行為對象的區分標志.
    每個對象有惟一的indentity 而這3者之間相互影響.

    6.類之間的關系:
    use-a :依賴關系
    has-a :聚合關系
    is-a :繼承關系--例:A類繼承了B類,此時A類不僅有了B類的方法,還有其自己的方法.(個性存在于共性中)

    7.構造對象使用構造器:構造器的提出,構造器是一種特殊的方法,構造對象并對其初始化.
    例:Data類的構造器叫Data
    new Data()---構造一個新對象,且初始化當前時間.
    Data happyday=new Data()---把一個對象賦值給一個變量happyday,從而使該對象能夠多次使用,此處要聲明的使變量與對象變量二者是不同的.new返回的值是一個引用.
    構造器特點:構造器可以有0個,一個或多個參數構造器和類有相同的名字一個類可以有多個構造器,構造器沒有返回值,構造器總是和new運算符一起使用.

    8.重載:當多個方法具有相同的名字而含有不同的參數時,便發生重載.編譯器必須挑選出調用哪個方法.

    9.包(package)Java允許把一個或多個類收集在一起成為一組,稱作包,以便于組織任務,標準Java庫分為許多包.java.lang java.util java,net等,包是分層次的所有的java包都在java和javax包層次內.

    10.繼承思想:允許在已經存在的類的基礎上構建新的類,當你繼承一個已經存在的類時,那么你就復用了這個類的方法和字段,同時你可以在新類中添加新的方法和字段.

    11.擴展類:擴展類充分體現了is-a的繼承關系. 形式為:class (子類) extends (基類).

    12.多態:在java中,對象變量是多態的.而java中不支持多重繼承.

    13.動態綁定:調用對象方法的機制.
    (1)編譯器檢查對象聲明的類型和方法名.
    (2)編譯器檢查方法調用的參數類型.
    (3)靜態綁定:若方法類型為priavte static final 編譯器會準確知道該調用哪個方法.
    (4)當程序運行并且使用動態綁定來調用一個方法時,那么虛擬機必須調用x所指向的對象的實際類型相匹配的方法版本.
    (5)動態綁定:是很重要的特性,它能使程序變得可擴展而不需要重編譯已存代碼.

    14.final類:為防止他人從你的類上派生新類,此類是不可擴展的.

    15.動態調用比靜態調用花費的時間要長,

    16.抽象類:規定一個或多個抽象方法的類本身必須定義為abstract
    例: public abstract string getDescripition

    17.Java中的每一個類都是從Object類擴展而來的.

    18.object類中的equal和toString方法.equal用于測試一個對象是否同另一個對象相等.toString返回一個代表該對象的字符串,幾乎每一個類都會重載該方法,以便返回當前狀態的正確表示.(toString 方法是一個很重要的方法)

    19.通用編程:任何類類型的所有值都可以同object類性的變量來代替.

    20.數組列表:ArrayList動態數組列表,是一個類庫,定義在java.uitl包中,可自動調節數組的大小.

    21.class類 object類中的getClass方法返回class類型的一個實例,程序啟動時包含在main方法的類會被加載,虛擬機要加載他需要的所有類,每一個加載的類都要加載它需要的類.

    22.class類為編寫可動態操縱java代碼的程序提供了強大的功能:反射,這項功能為JavaBeans特別有用,使用反射Java能支持VB程序員習慣使用的工具.

    能夠分析類能力的程序叫反射器,Java中提供此功能的包叫Java.lang.reflect反射機制十分強大.
    1.在運行時分析類的能力.
    2.在運行時探察類的對象.
    3.實現通用數組操縱代碼.
    4.提供方法對象.

    而此機制主要針對是工具者而不是應用及程序.

    反射機制中的最重要的部分是允許你檢查類的結構.用到的API有:
    java.lang.reflect.Field 返回字段.
    java.reflect.Method 返回方法.
    java.lang.reflect.Constructor 返回參數.

    方法指針:java沒有方法指針,把一個方法的地址傳給另一個方法,可以在后面調用它,而接口是更好的解決方案.

    23.接口(Interface)說明類該做什么而不指定如何去做,一個類可以實現一個或多個interface.

    24.接口不是一個類,而是對符合接口要求的類的一套規范.

    若實現一個接口需要2個步驟:
    1.聲明類需要實現的指定接口.
    2.提供接口中的所有方法的定義.
    聲明一個類實現一個接口需要使用implements 關鍵字
    class actionB implements Comparable 其actionb需要提供CompareTo方法,接口不是類,不能用new實例化一個接口.

    25.一個類只有一個超類,但一個類能實現多個接口.Java中的一個重要接口Cloneable

    26.接口和回調.編程一個常用的模式是回調模式,在這種模式中你可以指定當一個特定時間發生時回調對象上的方法.
    例:ActionListener 接口監聽.
    類似的API有:java.swing.JOptionPane
    java.swing.Timer
    java.awt.Tookit

    27.對象clone:clone方法是object一個保護方法,這意味著你的代碼不能簡單的調用它.

    28.內部類:一個內部類的定義是定義在另一個類的內部,原因是:1.一個內部類的對象能夠訪問創建它的對象的實現,包括私有數據
    2.對于同一個包中的其他類來說,內部類能夠隱藏起來.
    3.匿名內部類可以很方便的定義回調.
    4.使用內部類可以非常方便的編寫事件驅動程序.

    29.代理類(proxy):1.指定接口要求所有代碼
    2.object類定義的所有的方法(toString equals)

    30.數據類型:Java是強調類型的語言,每個變量都必須先申明它都類型,java中總共有8個基本類型.4種是整型,2種是浮點型,一種是字符型,被用于Unicode編碼中的字符,布爾型.(T111)

    posted @ 2005-11-17 10:51 安德爾斯 閱讀(276) | 評論 (0)編輯 收藏

    文件I/O:文件流→序列化

    ★文件流
        文件操作是最簡單最直接也是最容易想到的一種方式,我們說的文件操作不僅僅是通過FileInputStream/FileOutputStream這么“裸”的方式直接把數據寫入到本地文件(像我以前寫的一個掃雷的小游戲JavaMine就是這樣保存一局的狀態的),這樣就比較“底層”了。

    主要類與方法和描述

    FileInputStream.read() //從本地文件讀取二進制格式的數據 
    FileReader.read() //從本地文件讀取字符(文本)數據 
    FileOutputStream.write() //保存二進制數據到本地文件 
    FileWriter.write() //保存字符數據到本地文件
     
    ★XML
        和上面的單純的I/O方式相比,XML就顯得“高檔”得多,以至于成為一種數據交換的標準。以DOM方式為例,它關心的是首先在內存中構造文檔樹,數據保存在某個結點上(可以是葉子結點,也可以是標簽結點的屬性),構造好了以后一次性的寫入到外部文件,但我們只需要知道文件的位置,并不知道I/O是怎么操作的,XML操作方式可能多數人也實踐過,所以這里也只列出相關的方法,供初學者預先了解一下。主要的包是javax.xml.parsers,org.w3c.dom,javax.xml.transform。

    主要類與方法和描述

    DocumentBuilderFactory.newDocumentBuilder().parse() //解析一個外部的XML文件,得到一個Document對象的DOM樹 
    DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument() //初始化一棵DOM樹 
    Document.getDocumentElement().appendChild() //為一個標簽結點添加一個子結點 
    Document.createTextNode() //生成一個字符串結點 
    Node.getChildNodes() //取得某個結點的所有下一層子結點 
    Node.removeChild()  //刪除某個結點的子結點 
    Document.getElementsByTagName() 查找所有指定名稱的標簽結點 
    Document.getElementById() //查找指定名稱的一個標簽結點,如果有多個符合,則返回某一個,通常是第一個 
    Element.getAttribute() //取得一個標簽的某個屬性的的值 
    Element.setAttribute() //設置一個標簽的某個屬性的的值 
    Element.removeAttribute() //刪除一個標簽的某個屬性 
    TransformerFactory.newInstance().newTransformer().transform() //將一棵DOM樹寫入到外部XML文件

    ★序列化
        使用基本的文件讀寫方式存取數據,如果我們僅僅保存相同類型的數據,則可以用同一種格式保存,譬如在我的JavaMine中保存一個盤局時,需要保存每一個方格的坐標、是否有地雷,是否被翻開等,這些信息組合成一個“復合類型”;相反,如果有多種不同類型的數據,那我們要么把它分解成若干部分,以相同類型(譬如String)保存,要么我們需要在程序中添加解析不同類型數據格式的邏輯,這就很不方便。于是我們期望用一種比較“高”的層次上處理數據,程序員應該花盡可能少的時間和代碼對數據進行解析,事實上,序列化操作為我們提供了這樣一條途徑。
        序列化(Serialization)大家可能都有所接觸,它可以把對象以某種特定的編碼格式寫入或從外部字節流(即ObjectInputStream/ObjectOutputStream)中讀取。序列化一個對象非常之簡單,僅僅實現一下Serializable接口即可,甚至都不用為它專門添加任何方法:

    public class MySerial implements java.io.Serializable
    {
      //...
    }
     
    但有一個條件:即你要序列化的類當中,它的每個屬性都必須是是“可序列化”的。這句話說起來有點拗口,其實所有基本類型(就是int,char,boolean之類的)都是“可序列化”的,而你可以看看JDK文檔,會發現很多類其實已經實現了Serializable(即已經是“可序列化”的了),于是這些類的對象以及基本數據類型都可以直接作為你需要序列化的那個類的內部屬性。如果碰到了不是“可序列化”的屬性怎么辦?對不起,那這個屬性的類還需要事先實現Serializable接口,如此遞歸,直到所有屬性都是“可序列化”的。

    主要類與方法和描述

    ObjectOutputStream.writeObject() //將一個對象序列化到外部字節流 
    ObjectInputStream.readObject() //從外部字節流讀取并重新構造對象
     
        從實際應用上看來,“Serializable”這個接口并沒有定義任何方法,仿佛它只是一個標記(或者說像是Java的關鍵字)而已,一旦虛擬機看到這個“標記”,就會嘗試調用自身預定義的序列化機制,除非你在實現Serializable接口的同時還定義了私有的readObject()或writeObject()方法。這一點很奇怪。不過你要是不愿意讓系統使用缺省的方式進行序列化,那就必須定義上面提到的兩個方法:

    public class MySerial implements java.io.Serializable
    {
      private void writeObject(java.io.ObjectOutputStream out) throws IOException
      {
        //...
      }
      private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
      {
        //...
      }
      //...

        譬如你可以在上面的writeObject()里調用默認的序列化方法ObjectOutputStream.defaultWriteObject();譬如你不愿意將某些敏感的屬性和信息序列化,你也可以調用ObjectOutputStream.writeObject()方法明確指定需要序列化那些屬性。關于用戶可定制的序列化方法,我們將在后面提到。

    ★Bean
        上面的序列化只是一種基本應用,你把一個對象序列化到外部文件以后,用notepad打開那個文件,只能從為數不多的一些可讀字符中猜到這是有關這個類的信息文件,這需要你熟悉序列化文件的字節編碼方式,那將是比較痛苦的(在《Core Java 2》第一卷里提到了相關編碼方式,有興趣的話可以查看參考資料),某些情況下我們可能需要被序列化的文件具有更好的可讀性。另一方面,作為Java組件的核心概念“JavaBeans”,從JDK 1.4開始,其規范里也要求支持文本方式的“長期的持久化”(long-term persistence)。
        打開JDK文檔,java.beans包里的有一個名為“Encoder”的類,這就是一個可以序列化bean的實用類。和它相關的兩個主要類有XMLEcoder和XMLDecoder,顯然,這是以XML文件的格式保存和讀取bean的工具。他們的用法也很簡單,和上面ObjectOutputStream/ObjectInputStream比較類似。

    主要類與方法和描述

    XMLEncoder.writeObject() //將一個對象序列化到外部字節流 
    XMLDecoder.readObject() //從外部字節流讀取并重新構造對象 

        如果一個bean是如下格式:

    public class MyBean
    {
      int i;
      char[] c;
      String s;
      //...(get和set操作省略)...

    那么通過XMLEcoder序列化出來的XML文件具有這樣的形式:

    <?xml version="1.0" encoding="UTF-8"?>
    <java version="1.4.0" class="java.beans.XMLDecoder">
      <object class="MyBean">
        <void property="i">
          <int>1</int>
        </void>
        <void property="c">
          <array class="char" length="3">
            <void index="0">
              <int>a</int>
            </void>
            <void index="1">
              <int>b</int>
            </void>
            <void index="2">
              <int>c</int>
            </void>
          </array>
        </void>
        <void property="s">
          <string>fox jump!</string>
        </void>
      </object>
    </java>

        像AWT和Swing中很多可視化組件都是bean,當然也是可以用這種方式序列化的,下面就是從JDK文檔中摘錄的一個JFrame序列化以后的XML文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <java version="1.0" class="java.beans.XMLDecoder">
      <object class="javax.swing.JFrame">
        <void property="name">
          <string>frame1</string>
        </void>
        <void property="bounds">
          <object class="java.awt.Rectangle">
            <int>0</int>
            <int>0</int>
            <int>200</int>
            <int>200</int>
          </object>
        </void>
        <void property="contentPane">
          <void method="add">
            <object class="javax.swing.JButton">
              <void property="label">
                <string>Hello</string>
              </void>
            </object>
          </void>
        </void>
        <void property="visible">
          <boolean>true</boolean>
        </void>
      </object>
    </java>

        因此但你想要保存的數據是一些不是太復雜的類型的話,把它做成bean再序列化也不失為一種方便的選擇。

    ★Properties
        在以前我總結的一篇關于集合框架的小文章里提到過,Properties是歷史集合類的一個典型的例子,這里主要不是介紹它的集合特性。大家可能都經常接觸一些配置文件,如Windows的ini文件,Apache的conf文件,還有Java里的properties文件等,這些文件當中的數據以“關鍵字-值”對的方式保存。“環境變量”這個概念都知道吧,它也是一種“key-value”對,以前也常常看到版上問“如何取得系統某某信息”之類的問題,其實很多都保存在環境變量里,只要用一條

    System.getProperties().list(System.out); 

    就能獲得全部環境變量的列表:

    -- listing properties --
    java.runtime.name=Java(TM) 2 Runtime Environment, Stand...
    sun.boot.library.path=C:\Program Files\Java\j2re1.4.2_05\bin
    java.vm.version=1.4.2_05-b04
    java.vm.vendor=Sun Microsystems Inc.
    java.vendor.url=http://java.sun.com/
    path.separator=;
    java.vm.name=Java HotSpot(TM) Client VM
    file.encoding.pkg=sun.io
    user.country=CN
    sun.os.patch.level=Service Pack 1
    java.vm.specification.name=Java Virtual Machine Specification
    user.dir=d:\my documents\項目\eclipse\SWTDemo
    java.runtime.version=1.4.2_05-b04
    java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment
    java.endorsed.dirs=C:\Program Files\Java\j2re1.4.2_05\li...
    os.arch=x86
    java.io.tmpdir=C:\DOCUME~1\cn2lx0q0\LOCALS~1\Temp\
    line.separator=

    java.vm.specification.vendor=Sun Microsystems Inc.
    user.variant=
    os.name=Windows XP
    sun.java2d.fontpath=
    java.library.path=C:\Program Files\Java\j2re1.4.2_05\bi...
    java.specification.name=Java Platform API Specification
    java.class.version=48.0
    java.util.prefs.PreferencesFactory=java.util.prefs.WindowsPreferencesFac...
    os.version=5.1
    user.home=D:\Users\cn2lx0q0
    user.timezone=
    java.awt.printerjob=sun.awt.windows.WPrinterJob
    file.encoding=GBK
    java.specification.version=1.4
    user.name=cn2lx0q0
    java.class.path=d:\my documents\項目\eclipse\SWTDemo\bi...
    java.vm.specification.version=1.0
    sun.arch.data.model=32
    java.home=C:\Program Files\Java\j2re1.4.2_05
    java.specification.vendor=Sun Microsystems Inc.
    user.language=zh
    awt.toolkit=sun.awt.windows.WToolkit
    java.vm.info=mixed mode
    java.version=1.4.2_05
    java.ext.dirs=C:\Program Files\Java\j2re1.4.2_05\li...
    sun.boot.class.path=C:\Program Files\Java\j2re1.4.2_05\li...
    java.vendor=Sun Microsystems Inc.
    file.separator=\
    java.vendor.url.bug=http://java.sun.com/cgi-bin/bugreport...
    sun.cpu.endian=little
    sun.io.unicode.encoding=UnicodeLittle
    sun.cpu.isalist=pentium i486 i386
     

    主要類與方法和描述

    load() //從一個外部流讀取屬性 
    store() //將屬性保存到外部流(特別是文件) 
    getProperty() //取得一個指定的屬性 
    setProperty() //設置一個指定的屬性 
    list() //列出這個Properties對象包含的全部“key-value”對 
    System.getProperties() //取得系統當前的環境變量 


        你可以這樣保存一個properties文件:


    Properties prop = new Properties();
    prop.setProperty("key1", "value1");
    ...
    FileOutputStream out = new FileOutputStream("config.properties");
    prop.store(out, "--這里是文件頭,可以加入注釋--"); 

    ★Preferences
        如果我說Java里面可以不使用JNI的手段操作Windows的注冊表你信不信?很多軟件的菜單里都有“Setting”或“Preferences”這樣的選項用來設定或修改軟件的配置,這些配置信息可以保存到一個像上面所述的配置文件當中,如果是Windows平臺下,也可能會保存到系統注冊表中。從JDK 1.4開始,Java在java.util下加入了一個專門處理用戶和系統配置信息的java.util.prefs包,其中一個類Preferences是一種比較“高級”的玩意。從本質上講,Preferences本身是一個與平臺無關的東西,但不同的OS對它的SPI(Service Provider Interface)的實現卻是與平臺相關的,因此,在不同的系統中你可能看到首選項保存為本地文件、LDAP目錄項、數據庫條目等,像在Windows平臺下,它就保存到了系統注冊表中。不僅如此,你還可以把首選項導出為XML文件或從XML文件導入。

    主要類與方法和描述

    systemNodeForPackage() //根據指定的Class對象得到一個Preferences對象,這個對象的注冊表路徑是從“HKEY_LOCAL_MACHINE\”開始的 
    systemRoot() //得到以注冊表路徑HKEY_LOCAL_MACHINE\SOFTWARE\Javasoft\Prefs 為根結點的Preferences對象 
    userNodeForPackage() //根據指定的Class對象得到一個Preferences對象,這個對象的注冊表路徑是從“HKEY_CURRENT_USER\”開始的 
    userRoot() //得到以注冊表路徑HKEY_CURRENT_USER\SOFTWARE\Javasoft\Prefs 為根結點的Preferences對象 
    putXXX() //設置一個屬性的值,這里XXX可以為基本數值型類型,如int、long等,但首字母大寫,表示參數為相應的類型,也可以不寫而直接用put,參數則為字符串 
    getXXX() //得到一個屬性的值 
    exportNode() //將全部首選項導出為一個XML文件 
    exportSubtree() //將部分首選項導出為一個XML文件 
    importPreferences() //從XML文件導入首選項 

        你可以按如下步驟保存數據:

    Preferences myPrefs1 = Preferences.userNodeForPackage(this);// 這種方法是在“HKEY_CURRENT_USER\”下按當前類的路徑建立一個注冊表項
    Preferences myPrefs2 = Preferences.systemNodeForPackage(this);// 這種方法是在“HKEY_LOCAL_MACHINE\”下按當前類的路徑建立一個注冊表項
    Preferences myPrefs3 = Preferences.userRoot().node("com.jungleford.demo");// 這種方法是在“HKEY_CURRENT_USER\SOFTWARE\Javasoft\Prefs\”下按“com\jungleford\demo”的路徑建立一個注冊表項
    Preferences myPrefs4 = Preferences.systemRoot().node("com.jungleford.demo");// 這種方法是在“HKEY_LOCAL_MACHINE\SOFTWARE\Javasoft\Prefs\”下按“com\jungleford\demo”的路徑建立一個注冊表項
    myPrefs1.putInt("key1", 10);
    myPrefs1.putDouble("key2", -7.15);
    myPrefs1.put("key3", "value3");
    FileOutputStream out = new FileOutputStream("prefs.xml");
    myPrefs1.exportNode(out);


    網絡I/O:Socket→RMI

    ★Socket
        Socket編程可能大家都很熟,所以就不多討論了,只是說通過socket把數據保存到遠端服務器或從網絡socket讀取數據也不失為一種值得考慮的方式。

    ★RMI
        RMI機制其實就是RPC(遠程過程調用)的Java版本,它使用socket作為基本傳輸手段,同時也是序列化最重要的一個應用。現在網絡傳輸從編程的角度來看基本上都是以流的方式操作,socket就是一個例子,將對象轉換成字節流的一個重要目標就是為了方便網絡傳輸。
        想象一下傳統的單機環境下的程序設計,對于Java語言的函數(方法)調用(注意與C語言函數調用的區別)的參數傳遞,會有兩種情況:如果是基本數據類型,這種情況下和C語言是一樣的,采用值傳遞方式;如果是對象,則傳遞的是對象的引用,包括返回值也是引用,而不是一個完整的對象拷貝!試想一下在不同的虛擬機之間進行方法調用,即使是兩個完全同名同類型的對象他們也很可能是不同的引用!此外對于方法調用過程,由于被調用過程的壓棧,內存“現場”完全被被調用者占有,當被調用方法返回時,才將調用者的地址寫回到程序計數器(PC),恢復調用者的狀態,如果是兩個虛擬機,根本不可能用簡單壓棧的方式來保存調用者的狀態。因為種種原因,我們才需要建立RMI通信實體之間的“代理”對象,譬如“存根”就相當于遠程服務器對象在客戶機上的代理,stub就是這么來的,當然這是后話了。
        本地對象與遠程對象(未必是物理位置上的不同機器,只要不是在同一個虛擬機內皆為“遠程”)之間傳遞參數和返回值,可能有這么幾種情形:

    值傳遞:這又包括兩種子情形:如果是基本數據類型,那么都是“可序列化”的,統統序列化成可傳輸的字節流;如果是對象,而且不是“遠程對象”(所謂“遠程對象”是實現了java.rmi.Remote接口的對象),本來對象傳遞的應該是引用,但由于上述原因,引用是不足以證明對象身份的,所以傳遞的仍然是一個序列化的拷貝(當然這個對象也必須滿足上述“可序列化”的條件)。

    引用傳遞:可以引用傳遞的只能是“遠程對象”。這里所謂的“引用”不要理解成了真的只是一個符號,它其實是一個留在(客戶機)本地stub中的,和遠端服務器上那個真實的對象張得一模一樣的鏡像而已!只是因為它有點“特權”(不需要經過序列化),在本地內存里已經有了一個實例,真正引用的其實是這個“孿生子”。
     
        由此可見,序列化在RMI當中占有多么重要的地位。

    數據庫I/O:CMP、Hibernate

    ★什么是“Persistence”
        用過VMWare的朋友大概都知道當一個guest OS正在運行的時候點擊“Suspend”將虛擬OS掛起,它會把整個虛擬內存的內容保存到磁盤上,譬如你為虛擬OS分配了128M的運行內存,那掛起以后你會在虛擬OS所在的目錄下找到一個同樣是128M的文件,這就是虛擬OS內存的完整鏡像!這種內存的鏡像手段其實就是“Persistence”(持久化)概念的由來。

    ★CMP和Hibernate
        因為我對J2EE的東西不是太熟悉,隨便找了點材料看看,所以擔心說的不到位,這次就不作具體總結了,人要學習……真是一件痛苦的事情

    序列化再探討

        從以上技術的討論中我們不難體會到,序列化是Java之所以能夠出色地實現其鼓吹的兩大賣點??分布式(distributed)和跨平臺(OS independent)的一個重要基礎。TIJ(即“Thinking in Java”)談到I/O系統時,把序列化稱為“lightweight persistence”??“輕量級的持久化”,這確實很有意思。

    ★為什么叫做“序列”化?
        開場白里我說更習慣于把“Serialization”稱為“序列化”而不是“串行化”,這是有原因的。介紹這個原因之前先回顧一些計算機基本的知識,我們知道現代計算機的內存空間都是線性編址的(什么是“線性”知道吧,就是一個元素只有一個唯一的“前驅”和唯一的“后繼”,當然頭尾元素是個例外;對于地址來說,它的下一個地址當然不可能有兩個,否則就亂套了),“地址”這個概念推廣到數據結構,就相當于“指針”,這個在本科低年級大概就知道了。注意了,既然是線性的,那“地址”就可以看作是內存空間的“序號”,說明它的組織是有順序的,“序號”或者說“序列號”正是“Serialization”機制的一種體現。為什么這么說呢?譬如我們有兩個對象a和b,分別是類A和B的實例,它們都是可序列化的,而A和B都有一個類型為C的屬性,根據前面我們說過的原則,C當然也必須是可序列化的。

    import java.io.*;
    ...
    class A implements Serializable
    {
      C c;
      ...
    }

    class B implements Serializable
    {
      C c;
      ...
    }

    class C implements Serializable
    {
      ...
    }

    A a;
    B b;
    C c1;
    ...

        注意,這里我們在實例化a和b的時候,有意讓他們的c屬性使用同一個C類型對象的引用,譬如c1,那么請試想一下,但我們序列化a和b的時候,它們的c屬性在外部字節流(當然可以不僅僅是文件)里保存的是一份拷貝還是兩份拷貝呢?序列化在這里使用的是一種類似于“指針”的方案:它為每個被序列化的對象標上一個“序列號”(serial number),但序列化一個對象的時候,如果其某個屬性對象是已經被序列化的,那么這里只向輸出流寫入該屬性的序列號;從字節流恢復被序列化的對象時,也根據序列號找到對應的流來恢復。這就是“序列化”名稱的由來!這里我們看到“序列化”和“指針”是極相似的,只不過“指針”是內存空間的地址鏈,而序列化用的是外部流中的“序列號鏈”。
        使用“序列號”而不是內存地址來標識一個被序列化的對象,是因為從流中恢復對象到內存,其地址可能就未必是原來的地址了??我們需要的只是這些對象之間的引用關系,而不是死板的原始位置,這在RMI中就更是必要,在兩臺不同的機器之間傳遞對象(流),根本就不可能指望它們在兩臺機器上都具有相同的內存地址。

    ★更靈活的“序列化”:transient屬性和Externalizable
        Serializable確實很方便,方便到你幾乎不需要做任何額外的工作就可以輕松將內存中的對象保存到外部。但有兩個問題使得Serializable的威力收到束縛:
        一個是效率問題,《Core Java 2》中指出,Serializable使用系統默認的序列化機制會影響軟件的運行速度,因為需要為每個屬性的引用編號和查號,再加上I/O操作的時間(I/O和內存讀寫差的可是一個數量級的大小),其代價當然是可觀的。
        另一個困擾是“裸”的Serializable不可定制,傻乎乎地什么都給你序列化了,不管你是不是想這么做。其實你可以有至少三種定制序列化的選擇。其中一種前面已經提到了,就是在implements Serializable的類里面添加私有的writeObject()和readObject()方法(這種Serializable就不裸了,),在這兩個方法里,該序列化什么,不該序列化什么,那就由你說了算了,你當然可以在這兩個方法體里面分別調用ObjectOutputStream.defaultWriteObject()和ObjectInputStream.defaultReadObject()仍然執行默認的序列化動作(那你在代碼上不就做無用功了?呵呵),也可以用ObjectOutputStream.writeObject()和ObjectInputStream.readObject()方法對你中意的屬性進行序列化。但虛擬機一看到你定義了這兩個方法,它就不再用默認的機制了。
        如果僅僅為了跳過某些屬性不讓它序列化,上面的動作似乎顯得麻煩,更簡單的方法是對不想序列化的屬性加上transient關鍵字,說明它是個“暫態變量”,默認序列化的時候就不會把這些屬性也塞到外部流里了。當然,你如果定義writeObject()和readObject()方法的化,仍然可以把暫態變量進行序列化。題外話,像transient、violate、finally這樣的關鍵字初學者可能會不太重視,而現在有的公司招聘就偏偏喜歡問這樣的問題 :(
        再一個方案就是不實現Serializable而改成實現Externalizable接口。我們研究一下這兩個接口的源代碼,發現它們很類似,甚至容易混淆。我們要記住的是:Externalizable默認并不保存任何對象相關信息!任何保存和恢復對象的動作都是你自己定義的。Externalizable包含兩個public的方法:

    public void writeExternal(ObjectOutput out) throws IOException;
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;

        乍一看這和上面的writeObject()和readObject()幾乎差不多,但Serializable和Externalizable走的是兩個不同的流程:Serializable在對象不存在的情況下,就可以僅憑外部的字節序列把整個對象重建出來;但Externalizable在重建對象時,先是調用該類的默認構造函數(即不含參數的那個構造函數)使得內存中先有這么一個實例,然后再調用readExternal方法對實例中的屬性進行恢復,因此,如果默認構造函數中和readExternal方法中都沒有賦值的那些屬性,特別他們是非基本類型的話,將會是空(null)。在這里需要注意的是,transient只能用在對Serializable而不是Externalizable的實現里面。

    ★序列化與克隆
        從“可序列化”的遞歸定義來看,一個序列化的對象貌似對象內存映象的外部克隆,如果沒有共享引用的屬性的化,那么應該是一個深度克隆。關于克隆的話題有可以談很多,這里就不細說了,有興趣的話可以參考IBM developerWorks上的一篇文章:JAVA中的指針,引用及對象的clone

    一點啟示

        作為一個實際的應用,我在寫那個簡易的郵件客戶端JExp的時候曾經對比過好幾種保存Message對象(主要是幾個關鍵屬性和郵件的內容)到本地的方法,譬如XML、Properties等,最后還是選擇了用序列化的方式,因為這種方法最簡單, 大約可算是“學以致用”罷。這里“存取程序狀態”其實只是一個引子話題罷了,我想說的是??就如同前面我們討論的關于logging的話題一樣??在Java面前對同一個問題你可以有很多種solution:熟悉文件操作的,你可能會覺得Properties、XML或Bean比較方便,然后又發現了還有Preferences這么一個東東,大概又會感慨“天外有天”了,等到你接觸了很多種新方法以后,結果又會“殊途同歸”,重新反省Serialization機制本身。這不僅是Java,科學也是同樣的道理。

    參考資料


    Core Java 2. by Cay S. Horstmann, Gary Cornell

    J2SE進階. by JavaResearch.org

    Thinking in Java. by Bruce Eckel

    J2SE 1.4.2 Documentation. by java.sun.com

    Java Network Programming. by Elliotte R. Harold

    Java分布式對象:RMI和CORBA. by IBM developerWorks

    posted @ 2005-11-17 10:37 安德爾斯 閱讀(300) | 評論 (0)編輯 收藏
    1.document.write(""); 輸出語句
    2.JS中的注釋為//
    3.傳統的HTML文檔順序是:document->html->(head,body)
    4.一個瀏覽器窗口中的DOM順序是:window->(navigator,screen,history,location,document)
    5.得到表單中元素的名稱和值:document.getElementById("表單中元素的ID號").name(或value)
    6.一個小寫轉大寫的JS: document.getElementById("output").value = document.getElementById("input").value.toUpperCase();
    7.JS中的值類型:String,Number,Boolean,Null,Object,Function
    8.JS中的字符型轉換成數值型:parseInt(),parseFloat()
    9.JS中的數字轉換成字符型:(""+變量)
    10.JS中的取字符串長度是:(length)
    11.JS中的字符與字符相連接使用+號.
    12.JS中的比較操作符有:==等于,!=不等于,>,>=,<.<=
    13.JS中聲明變量使用:var來進行聲明
    14.JS中的判斷語句結構:if(condition){}else{}
    15.JS中的循環結構:for([initial expression];[condition];[upadte expression]) {inside loop}
    16.循環中止的命令是:break
    17.JS中的函數定義:function functionName([parameter],...){statement[s]}
    18.當文件中出現多個form表單時.可以用document.forms[0],document.forms[1]來代替.
    19.窗口:打開窗口window.open(), 關閉一個窗口:window.close(), 窗口本身:self
    20.狀態欄的設置:window.status="字符";
    21.彈出提示信息:window.alert("字符");
    22.彈出確認框:window.confirm();
    23.彈出輸入提示框:window.prompt();
    24.指定當前顯示鏈接的位置:window.location.href="URL"
    25.取出窗體中的所有表單的數量:document.forms.length
    26.關閉文檔的輸出流:document.close();
    27.字符串追加連接符:+=
    28.創建一個文檔元素:document.createElement(),document.createTextNode()
    29.得到元素的方法:document.getElementById()
    30.設置表單中所有文本型的成員的值為空:
    var form = window.document.forms[0]
    for (var i = 0; i<form.elements.length;i++){
        if (form.elements.type == "text"){
            form.elements.value = "";
        }
    }
    31.復選按鈕在JS中判斷是否選中:document.forms[0].checkThis.checked (checked屬性代表為是否選中返回TRUE或FALSE)
    32.單選按鈕組(單選按鈕的名稱必須相同):取單選按鈕組的長度document.forms[0].groupName.length
    33.單選按鈕組判斷是否被選中也是用checked.
    34.下拉列表框的值:document.forms[0].selectName.options[n].value (n有時用下拉列表框名稱加上.selectedIndex來確定被選中的值)
    35.字符串的定義:var myString = new String("This is lightsword");
    36.字符串轉成大寫:string.toUpperCase(); 字符串轉成小寫:string.toLowerCase();
    37.返回字符串2在字符串1中出現的位置:String1.indexOf("String2")!=-1則說明沒找到.
    38.取字符串中指定位置的一個字符:StringA.charAt(9);
    39.取出字符串中指定起點和終點的子字符串:stringA.substring(2,6);
    40.數學函數:Math.PI(返回圓周率),Math.SQRT2(返回開方),Math.max(value1,value2)返回兩個數中的最在值,Math.pow(value1,10)返回value1的十次方,Math.round(value1)四舍五入函數,Math.floor(Math.random()*(n+1))返回隨機數
    41.定義日期型變量:var today = new Date();
    42.日期函數列表:dateObj.getTime()得到時間,dateObj.getYear()得到年份,dateObj.getFullYear()得到四位的年份,dateObj.getMonth()得到月份,dateObj.getDate()得到日,dateObj.getDay()得到日期幾,dateObj.getHours()得到小時,dateObj.getMinutes()得到分,dateObj.getSeconds()得到秒,dateObj.setTime(value)設置時間,dateObj.setYear(val)設置年,dateObj.setMonth(val)設置月,dateObj.setDate(val)設置日,dateObj.setDay(val)設置星期幾,dateObj.setHours設置小時,dateObj.setMinutes(val)設置分,dateObj.setSeconds(val)設置秒  [注意:此日期時間從0開始計]
    43.FRAME的表示方式: [window.]frames[n].ObjFuncVarName,frames["frameName"].ObjFuncVarName,frameName.ObjFuncVarName
    44.parent代表父親對象,top代表最頂端對象
    45.打開子窗口的父窗口為:opener
    46.表示當前所屬的位置:this
    47.當在超鏈接中調用JS函數時用:(javascript :)來開頭后面加函數名
    48.在老的瀏覽器中不執行此JS:<!--      //-->
    49.引用一個文件式的JS:<script type="text/javascript" src="aaa.js"></script>
    50.指定在不支持腳本的瀏覽器顯示的HTML:<noscript></noscript>
    51.當超鏈和onCLICK事件都有時,則老版本的瀏覽器轉向a.html,否則轉向b.html.例:<a href="a.html" onclick="location.href='b.html';return false">dfsadf</a>
    52.JS的內建對象有:Array,Boolean,Date,Error,EvalError,Function,Math,Number,Object,RangeError,ReferenceError,RegExp,String,SyntaxError,TypeError,URIError
    53.JS中的換行:\n
    54.窗口全屏大小:<script>function fullScreen(){ this.moveTo(0,0);this.outerWidth=screen.availWidth;this.outerHeight=screen.availHeight;}window.maximize=fullScreen;</script>
    55.JS中的all代表其下層的全部元素
    56.JS中的焦點順序:document.getElementByid("表單元素").tabIndex = 1
    57.innerHTML的值是表單元素的值:如<p id="para">"how are <em>you</em>"</p>,則innerHTML的值就是:how are <em>you</em>
    58.innerTEXT的值和上面的一樣,只不過不會把<em>這種標記顯示出來.
    59.contentEditable可設置元素是否可被修改,isContentEditable返回是否可修改的狀態.
    60.isDisabled判斷是否為禁止狀態.disabled設置禁止狀態
    61.length取得長度,返回整型數值
    62.addBehavior()是一種JS調用的外部函數文件其擴展名為.htc
    63.window.focus()使當前的窗口在所有窗口之前.
    64.blur()指失去焦點.與FOCUS()相反.
    65.select()指元素為選中狀態.
    66.防止用戶對文本框中輸入文本:onfocus="this.blur()"
    67.取出該元素在頁面中出現的數量:document.all.tags("div(或其它HTML標記符)").length
    68.JS中分為兩種窗體輸出:模態和非模態.window.showModaldialog(),window.showModeless()
    69.狀態欄文字的設置:window.status='文字',默認的狀態欄文字設置:window.defaultStatus = '文字.';
    70.添加到收藏夾:external.AddFavorite("http://www.dannyg.com";,"jaskdlf");
    71.JS中遇到腳本錯誤時不做任何操作:window.onerror = doNothing; 指定錯誤句柄的語法為:window.onerror = handleError;
    72.JS中指定當前打開窗口的父窗口:window.opener,支持opener.opener...的多重繼續.
    73.JS中的self指的是當前的窗口
    74.JS中狀態欄顯示內容:window.status="內容"
    75.JS中的top指的是框架集中最頂層的框架
    76.JS中關閉當前的窗口:window.close();
    77.JS中提出是否確認的框:if(confirm("Are you sure?")){alert("ok");}else{alert("Not Ok");}
    78.JS中的窗口重定向:window.navigate("http://www.sina.com.cn";);
    79.JS中的打印:window.print()
    80.JS中的提示輸入框:window.prompt("message","defaultReply");
    81.JS中的窗口滾動條:window.scroll(x,y)
    82.JS中的窗口滾動到位置:window.scrollby
    83.JS中設置時間間隔:setInterval("expr",msecDelay)或setInterval(funcRef,msecDelay)或setTimeout
    84.JS中的模態顯示在IE4+行,在NN中不行:showModalDialog("URL"[,arguments][,features]);
    85.JS中的退出之前使用的句柄:function verifyClose(){event.returnValue="we really like you and hope you will stay longer.";}}  window.onbeforeunload=verifyClose;
    86.當窗體第一次調用時使用的文件句柄:onload()
    87.當窗體關閉時調用的文件句柄:onunload()
    88.window.location的屬性: protocol(http:),hostname(www.example.com),port(80),host(www.example.com:80),pathname("/a/a.html"),hash("#giantGizmo",指跳轉到相應的錨記),href(全部的信息)
    89.window.location.reload()刷新當前頁面.
    90.window.history.back()返回上一頁,window.history.forward()返回下一頁,window.history.go(返回第幾頁,也可以使用訪問過的URL)
    91.document.write()不換行的輸出,document.writeln()換行輸出
    92.document.body.noWrap=true;防止鏈接文字折行.
    93.變量名.charAt(第幾位),取該變量的第幾位的字符.
    94."abc".charCodeAt(第幾個),返回第幾個字符的ASCii碼值.
    95.字符串連接:string.concat(string2),或用+=進行連接
    96.變量.indexOf("字符",起始位置),返回第一個出現的位置(從0開始計算)
    97.string.lastIndexOf(searchString[,startIndex])最后一次出現的位置.
    98.string.match(regExpression),判斷字符是否匹配.
    99.string.replace(regExpression,replaceString)替換現有字符串.
    100.string.split(分隔符)返回一個數組存儲值.
    101.string.substr(start[,length])取從第幾位到指定長度的字符串.
    102.string.toLowerCase()使字符串全部變為小寫.
    103.string.toUpperCase()使全部字符變為大寫.
    104.parseInt(string[,radix(代表進制)])強制轉換成整型.
    105.parseFloat(string[,radix])強制轉換成浮點型.
    106.isNaN(變量):測試是否為數值型.
    107.定義常量的關鍵字:const,定義變量的關鍵字:var 
    posted @ 2005-11-17 09:54 安德爾斯 閱讀(471) | 評論 (0)編輯 收藏
    Java Reflection (JAVA反射)    

    Reflection 是 Java 程序開發語言的特征之一,它允許運行中的 Java 程序對自身進行檢查,或者說“自審”,并能直接操作程序的內部屬性。例如,使用它能獲得 Java 類中各成員的名稱并顯示出來。

    Java 的這一能力在實際應用中也許用得不是很多,但是在其它的程序設計語言中根本就不存在這一特性。例如,Pascal、C 或者 C++ 中就沒有辦法在程序中獲得函數定義相關的信息。

    JavaBean 是 reflection 的實際應用之一,它能讓一些工具可視化的操作軟件組件。這些工具通過 reflection 動態的載入并取得 Java 組件(類) 的屬性。



    1. 一個簡單的例子

    考慮下面這個簡單的例子,讓我們看看 reflection 是如何工作的。

    import java.lang.reflect.*;
    public class DumpMethods {
       public static void main(String args[]) {
           try {
               Class c = Class.forName(args[0]);
               Method m[] = c.getDeclaredMethods();
               for (int i = 0; i < m.length; i++)
                   System.out.println(m[i].toString());
           } catch (Throwable e) {
               System.err.println(e);
           }
       }
    }

    按如下語句執行:

    java DumpMethods java.util.Stack

    它的結果輸出為:

    public java.lang.Object java.util.Stack.push(java.lang.Object)

    public synchronized java.lang.Object java.util.Stack.pop()

    public synchronized java.lang.Object java.util.Stack.peek()

    public boolean java.util.Stack.empty()

    public synchronized int java.util.Stack.search(java.lang.Object)

    這樣就列出了java.util.Stack 類的各方法名以及它們的限制符和返回類型。

    這個程序使用 Class.forName 載入指定的類,然后調用 getDeclaredMethods 來獲取這個類中定義了的方法列表。java.lang.reflect.Methods 是用來描述某個類中單個方法的一個類。

    2.開始使用 Reflection

    用于 reflection 的類,如 Method,可以在 java.lang.relfect 包中找到。使用這些類的時候必須要遵循三個步驟:第一步是獲得你想操作的類的 java.lang.Class 對象。在運行中的 Java 程序中,用 java.lang.Class 類來描述類和接口等。

    下面就是獲得一個 Class 對象的方法之一:

    Class c = Class.forName("java.lang.String");

    這條語句得到一個 String 類的類對象。還有另一種方法,如下面的語句:

    Class c = int.class;

    或者

    Class c = Integer.TYPE;

    它們可獲得基本類型的類信息。其中后一種方法中訪問的是基本類型的封裝類 (如 Integer) 中預先定義好的 TYPE 字段。

    第二步是調用諸如 getDeclaredMethods 的方法,以取得該類中定義的所有方法的列表。

    一旦取得這個信息,就可以進行第三步了??使用 reflection API 來操作這些信息,如下面這段代碼:

    Class c = Class.forName("java.lang.String");

    Method m[] = c.getDeclaredMethods();

    System.out.println(m[0].toString());

    它將以文本方式打印出 String 中定義的第一個方法的原型。

    在下面的例子中,這三個步驟將為使用 reflection 處理特殊應用程序提供例證。

    模擬 instanceof 操作符

    得到類信息之后,通常下一個步驟就是解決關于 Class 對象的一些基本的問題。例如,Class.isInstance 方法可以用于模擬 instanceof 操作符:

    class A {
    }

    public class instance1 {
       public static void main(String args[]) {
           try {
               Class cls = Class.forName("A");
               boolean b1 = cls.isInstance(new Integer(37));
               System.out.println(b1);
               boolean b2 = cls.isInstance(new A());
               System.out.println(b2);
           } catch (Throwable e) {
               System.err.println(e);
           }
       }
    }

    在這個例子中創建了一個 A 類的 Class 對象,然后檢查一些對象是否是 A 的實例。Integer(37) 不是,但 new A() 是。

    3.找出類的方法

    找出一個類中定義了些什么方法,這是一個非常有價值也非常基礎的 reflection 用法。下面的代碼就實現了這一用法:

    import java.lang.reflect.*;

    public class method1 {
       private int f1(Object p, int x) throws NullPointerException {
           if (p == null)
               throw new NullPointerException();
           return x;
       }

       public static void main(String args[]) {
           try {
               Class cls = Class.forName("method1");
               Method methlist[] = cls.getDeclaredMethods();
               for (int i = 0; i < methlist.length; i++) {
                   Method m = methlist[i];
                   System.out.println("name = " + m.getName());
                   System.out.println("decl class = " + m.getDeclaringClass());
                   Class pvec[] = m.getParameterTypes();
                   for (int j = 0; j < pvec.length; j++)
                       System.out.println("param #" + j + " " + pvec[j]);
                   Class evec[] = m.getExceptionTypes();
                   for (int j = 0; j < evec.length; j++)
                       System.out.println("exc #" + j + " " + evec[j]);
                   System.out.println("return type = " + m.getReturnType());
                   System.out.println("-----");
               }
           } catch (Throwable e) {
               System.err.println(e);
           }
       }
    }

    這個程序首先取得 method1 類的描述,然后調用 getDeclaredMethods 來獲取一系列的 Method 對象,它們分別描述了定義在類中的每一個方法,包括 public 方法、protected 方法、package 方法和 private 方法等。如果你在程序中使用 getMethods 來代替 getDeclaredMethods,你還能獲得繼承來的各個方法的信息。

    取得了 Method 對象列表之后,要顯示這些方法的參數類型、異常類型和返回值類型等就不難了。這些類型是基本類型還是類類型,都可以由描述類的對象按順序給出。

    輸出的結果如下:

    name = f1

    decl class = class method1

    param #0 class java.lang.Object

    param #1 int

    exc #0 class java.lang.NullPointerException

    return type = int

    -----

    name = main

    decl class = class method1

    param #0 class [Ljava.lang.String;

    return type = void

    -----


    4.獲取構造器信息

    獲取類構造器的用法與上述獲取方法的用法類似,如:

    import java.lang.reflect.*;

    public class constructor1 {
       public constructor1() {
       }

       protected constructor1(int i, double d) {
       }

       public static void main(String args[]) {
           try {
               Class cls = Class.forName("constructor1");
               Constructor ctorlist[] = cls.getDeclaredConstructors();
               for (int i = 0; i < ctorlist.length; i++) {
                   Constructor ct = ctorlist[i];
                   System.out.println("name = " + ct.getName());
                   System.out.println("decl class = " + ct.getDeclaringClass());
                   Class pvec[] = ct.getParameterTypes();
                   for (int j = 0; j < pvec.length; j++)
                       System.out.println("param #" + j + " " + pvec[j]);
                   Class evec[] = ct.getExceptionTypes();
                   for (int j = 0; j < evec.length; j++)
                       System.out.println("exc #" + j + " " + evec[j]);
                   System.out.println("-----");
               }
           } catch (Throwable e) {
               System.err.println(e);
           }
       }
    }

    這個例子中沒能獲得返回類型的相關信息,那是因為構造器沒有返回類型。

    這個程序運行的結果是:

    name = constructor1

    decl class = class constructor1

    -----

    name = constructor1

    decl class = class constructor1

    param #0 int

    param #1 double

    -----

    5.獲取類的字段(域)

    找出一個類中定義了哪些數據字段也是可能的,下面的代碼就在干這個事情:


    import java.lang.reflect.*;

    public class field1 {
       private double d;
       public static final int i = 37;
       String s = "testing";

       public static void main(String args[]) {
           try {
               Class cls = Class.forName("field1");
               Field fieldlist[] = cls.getDeclaredFields();
               for (int i = 0; i < fieldlist.length; i++) {
                   Field fld = fieldlist[i];
                   System.out.println("name = " + fld.getName());
                   System.out.println("decl class = " + fld.getDeclaringClass());
                   System.out.println("type = " + fld.getType());
                   int mod = fld.getModifiers();
                   System.out.println("modifiers = " + Modifier.toString(mod));
                   System.out.println("-----");
               }
           } catch (Throwable e) {
               System.err.println(e);
           }
       }
    }

    這個例子和前面那個例子非常相似。例中使用了一個新東西 Modifier,它也是一個 reflection 類,用來描述字段成員的修飾語,如“private int”。這些修飾語自身由整數描述,而且使用 Modifier.toString 來返回以“官方”順序排列的字符串描述 (如“static”在“final”之前)。這個程序的輸出是:

    name = d

    decl class = class field1

    type = double

    modifiers = private

    -----

    name = i

    decl class = class field1

    type = int

    modifiers = public static final

    -----

    name = s

    decl class = class field1

    type = class java.lang.String

    modifiers =

    -----

    和獲取方法的情況一下,獲取字段的時候也可以只取得在當前類中申明了的字段信息 (getDeclaredFields),或者也可以取得父類中定義的字段 (getFields) 。


    6.根據方法的名稱來執行方法

    文本到這里,所舉的例子無一例外都與如何獲取類的信息有關。我們也可以用 reflection 來做一些其它的事情,比如執行一個指定了名稱的方法。下面的示例演示了這一操作:

    import java.lang.reflect.*;
    public class method2 {
       public int add(int a, int b) {
           return a + b;
       }
       public static void main(String args[]) {
           try {
               Class cls = Class.forName("method2");
               Class partypes[] = new Class[2];
               partypes[0] = Integer.TYPE;
               partypes[1] = Integer.TYPE;
               Method meth = cls.getMethod("add", partypes);
               method2 methobj = new method2();
               Object arglist[] = new Object[2];
               arglist[0] = new Integer(37);
               arglist[1] = new Integer(47);
               Object retobj = meth.invoke(methobj, arglist);
               Integer retval = (Integer) retobj;
               System.out.println(retval.intvalue());
           } catch (Throwable e) {
               System.err.println(e);
           }
       }
    }

    假如一個程序在執行的某處的時候才知道需要執行某個方法,這個方法的名稱是在程序的運行過程中指定的 (例如,JavaBean 開發環境中就會做這樣的事),那么上面的程序演示了如何做到。

    上例中,getMethod 用于查找一個具有兩個整型參數且名為 add 的方法。找到該方法并創建了相應的 Method 對象之后,在正確的對象實例中執行它。執行該方法的時候,需要提供一個參數列表,這在上例中是分別包裝了整數 37 和 47 的兩個 Integer 對象。執行方法的返回的同樣是一個 Integer 對象,它封裝了返回值 84。

    7.創建新的對象

    對于構造器,則不能像執行方法那樣進行,因為執行一個構造器就意味著創建了一個新的對象 (準確的說,創建一個對象的過程包括分配內存和構造對象)。所以,與上例最相似的例子如下:

    import java.lang.reflect.*;

    public class constructor2 {
       public constructor2() {
       }

       public constructor2(int a, int b) {
           System.out.println("a = " + a + " b = " + b);
       }

       public static void main(String args[]) {
           try {
               Class cls = Class.forName("constructor2");
               Class partypes[] = new Class[2];
               partypes[0] = Integer.TYPE;
               partypes[1] = Integer.TYPE;
               Constructor ct = cls.getConstructor(partypes);
               Object arglist[] = new Object[2];
               arglist[0] = new Integer(37);
               arglist[1] = new Integer(47);
               Object retobj = ct.newInstance(arglist);
           } catch (Throwable e) {
               System.err.println(e);
           }
       }
    }

    根據指定的參數類型找到相應的構造函數并執行它,以創建一個新的對象實例。使用這種方法可以在程序運行時動態地創建對象,而不是在編譯的時候創建對象,這一點非常有價值。

    8.改變字段(域)的值

    reflection 的還有一個用處就是改變對象數據字段的值。reflection 可以從正在運行的程序中根據名稱找到對象的字段并改變它,下面的例子可以說明這一點:

    import java.lang.reflect.*;

    public class field2 {
       public double d;

       public static void main(String args[]) {
           try {
               Class cls = Class.forName("field2");
               Field fld = cls.getField("d");
               field2 f2obj = new field2();
               System.out.println("d = " + f2obj.d);
               fld.setDouble(f2obj, 12.34);
               System.out.println("d = " + f2obj.d);
           } catch (Throwable e) {
               System.err.println(e);
           }
       }
    }

    這個例子中,字段 d 的值被變為了 12.34。

    9.使用數組

    本文介紹的 reflection 的最后一種用法是創建的操作數組。數組在 Java 語言中是一種特殊的類類型,一個數組的引用可以賦給 Object 引用。觀察下面的例子看看數組是怎么工作的:

    import java.lang.reflect.*;

    public class array1 {
       public static void main(String args[]) {
           try {
               Class cls = Class.forName("java.lang.String");
               Object arr = Array.newInstance(cls, 10);
               Array.set(arr, 5, "this is a test");
               String s = (String) Array.get(arr, 5);
               System.out.println(s);
           } catch (Throwable e) {
               System.err.println(e);
           }
       }
    }

    例中創建了 10 個單位長度的 String 數組,為第 5 個位置的字符串賦了值,最后將這個字符串從數組中取得并打印了出來。

    下面這段代碼提供了一個更復雜的例子:

    import java.lang.reflect.*;

    public class array2 {
       public static void main(String args[]) {
           int dims[] = new int[]{5, 10, 15};
           Object arr = Array.newInstance(Integer.TYPE, dims);
           Object arrobj = Array.get(arr, 3);
           Class cls = arrobj.getClass().getComponentType();
           System.out.println(cls);
           arrobj = Array.get(arrobj, 5);
           Array.setInt(arrobj, 10, 37);
           int arrcast[][][] = (int[][][]) arr;
           System.out.println(arrcast[3][5][10]);
       }
    }
    例中創建了一個 5 x 10 x 15 的整型數組,并為處于 [3][5][10] 的元素賦了值為 37。注意,多維數組實際上就是數組的數組,例如,第一個 Array.get 之后,arrobj 是一個 10 x 15 的數組。進而取得其中的一個元素,即長度為 15 的數組,并使用 Array.setInt 為它的第 10 個元素賦值。

    注意創建數組時的類型是動態的,在編譯時并不知道其類型。



    posted @ 2005-11-17 09:53 安德爾斯 閱讀(642) | 評論 (2)編輯 收藏
    1.對象的復制
    2.clone()的使用
    3.對象實例的比較
    ////////////////////////////

    1.對象的復制


    1.    
    2. String str1 = "This is a string!"  //這里是 "對象引用" 的復制
    3. String str2 = new String(str1);  //這里是 "對象實例" 的復制

    淺復制: 只復制復合對象本身.
    深復制: 除了復制復合對象本身, 還復制了復合對象的引用的對象實例.

    例如:
    1. class Pupil{
    2.     public Pupil(String sno, String name, int age){
    3.         this.sno = new String(sno);
    4.         this.name = new String(name);
    5.         this.age = age;
    6.     }
    7.     public String getSno() {
    8.         return sno;
    9.     }
    10.     public String getName() {
    11.         return name;
    12.     }
    13.     public int getAge() {
    14.         return age;
    15.     }
    16.     public void setAge(int age) {
    17.         this.age = age;
    18.     }
    19.     private String sno;
    20.     private String name;
    21.     private int age;
    22. }
    23. public class CopyDemo {
    24.     public static Pupil[] shallowCopy(Pupil[] aClass) {
    25.         Pupil[] newClass = new Pupil[aClass.length];
    26.         //此時newClass 與aClass 指向同一塊內存
    27.         for(int i=0; i<aClass.length; i++)
    28.             newClass[i] = aClass[i];
    29.         return newClass;
    30.     }
    31.     
    32.     public static Pupil[] deepCopy(Pupil[] aClass) {
    33.         Pupil[] newClass = new Pupil[aClass.length];
    34.         //此時newClass 與aClass 的相應sno , name 指向同一塊內存
    35.         for(int i=0; i<aClass.length; i++) {
    36.             String sno = aClass[i].getSno();
    37.             String name = aClass[i].getName();
    38.             int age = aClass[i].getAge();
    39.             newClass[i] = new Pupil(sno, name, age);
    40.         }
    41.         return newClass;
    42.     }
    43.     public static Pupil[] deeperCopy(Pupil[] aClass) {
    44.         Pupil[] newClass = new Pupil[aClass.length];
    45.         //完全的復制
    46.         for(int i=0; i<aClass.length; i++) {
    47.             String sno = new String(aClass[i].getSno());
    48.             String name = new String(aClass[i].getName());
    49.             int age = aClass[i].getAge();
    50.             newClass[i] = new Pupil(sno, name, age);
    51.         }
    52.         return newClass;
    53.     }
    54. }


    2.clone()的使用


    * Object.clone()
    * Cloneable 接口
    * CloneNotSupportedException

    a. 使用Object.clone 進行復制
    兩個必須條件:
    1.一定要將重定義后的clone() 方法定義為公有方法(在Object 類中, 它是受保護的成員,    不能直接使用)
    2.該后代類聲明實現接口 Cloneable 接口(當類實現該接口, 其任何子類也會繼承該接口), 該接口實際上沒有任何
      內容, 只是一個標識, 標志實現該接口的類提供clone() 方法.(這是接口的一種非典型用法)
    1. public class Fraction implements Cloneable {
    2.     public Object clone() {
    3.         try{
    4.             return super.clone();  //call protected method
    5.         } catch (CloneNotSupportedException e) {
    6.             return null;
    7.         }
    8.     }
    9.     //other methods ...
    10. }


    b.重寫Object.clone()
    例如對   private char[] cb; character buffer 進行復制
      
    1. // add in class Cirbuf
    2.         public Object clone() {
    3.         try{
    4.             Cirbuf copy = (Cirbuf)super.clone();
    5.             copy.cb = (char[])cb.clone();
    6.             return copy;
    7.         }catch (CloneNotSupportedException e){
    8.             throw new InternalError(e.toString());
    9.         }
    10.     }

    c.復制數組
      數組是在方法調用重以引用的形式傳遞的對象. 下述情況下非常適合引用來傳遞數組:
      *正在接收的方法不修改數組
      *正在調用的方法不必關心是否修改數組
      *正在調用的方法想要得到數組中的修改結果 
      否則, 就應該在方法調用中傳遞數組對象的副本. 只需調用 arrObj.clone() 方法即可完成數組arrObj 的復制操作. 隨后將該數組副本強制轉換為其正確類型:
          (type[])arrObj.clone();
       System.arraycopy 方法提供一種用于在數組間復制多個元素的有效方式.
            System.arraycopy(source, i, target, j, len)

    3.對象實例的比較


    例如:
    1.     Pupil p1 = new Pupil("99184001""zhang3", 18);
    2.     Pupil p2 = new Pupil("99184001""zhang3", 18);

    a. "==" 
       if(p1 == p2)...
      此次測試的是對象引用, 其結果肯定是false, 只要兩個對象引用不是互為別名就不會相等.
    b. 淺比較  false
    1.    if(p1.getSno() == p2.getSno() && p1.getName() == p2.getName()
    2.      && p1.getAge() == p2.getAge()) ...;

    c. 深比較   true[/code]   
      if(p1.getSno().equals(p2.getSno()) && p1.getName().equals(p2.getName())
         && p1.getAge() == p2.getAge()) ...;[/code]
        JAVA API 的跟類Object 也提供了equals() 方法, 但它只是比較兩個對象引用, 而非比較兩個對象實例.
        不管怎樣, 如果需要比較Pupil 類的對象(例如要將它們放入對象容器), 應該為Pupil 類重定義equals() 方法:
    1.    
    2.     public boolean equals(Object otherobj) {
    3.         //檢查otherobj 是否為空
    4.         if(otherobj == nullreturn false;
    5.         //檢查otherobj 是否就是當前對象
    6.         if(otherobj == thisreturn true;
    7.         //檢查otherobj 是否具有正確的類型, 即檢查是否可與當前對象比較
    8.         if(!(otherobj instanceof Pupil)) return false;
    9.         //將otherobj 轉換為Pupil 類的對象引用
    10.         Pupil tmpObj = (Pupil)otherobj;
    11.         //關于學生是否相等的邏輯檢查
    12.         if(sno.equals(tmpObj.sno) && name.equals(tmpObj.name)
    13.              && age == tmpObj.age) return true;
    14.         
    15.         return false;
    16.     }

       JAVA API 所提供的每個類幾乎都提供了采用深比較策略的equals() 方法, 例如String 類equals() 方法. 一般來說, 用戶自己定義的類也應當提供合適的equals() 方法, 特別是當程序要將其對象放入JAVA API 所提供的對象容器類的時候.  
       按照約定, 任何類所提供的equals() 方法所實現的相等比較應該是等價關系, 即滿足自反性, 對稱性和傳遞性. 另外一個類重定義了equals() 方法, 也應該重定義相應hashCode() 方法, 否則將這個類的對象放入映射對象容器時也會發生以外.
    posted @ 2005-11-17 09:47 安德爾斯 閱讀(962) | 評論 (1)編輯 收藏
    ??Java反射機制
    侯捷觀點
    Java反射機制
    摘要
    Reflection 是Java被視為動態(或準動態)語言的一個關鍵性質。這個機制允許程序在運行時
    透過Reflection APIs取得任何一個已知名稱的class的內部信息,包括其modifiers(諸如
    public, static 等等)、superclass(例如Object)、實現之interfaces(例如
    Cloneable),也包括fields和methods的所有信息,并可于運行時改變fields內容或喚起
    methods。本文借由實例,大面積示范Reflection APIs。
    關于本文:
    讀者基礎:具備Java 語言基礎。
    本文適用工具:JDK1.5
    關鍵詞:
    Introspection(內省、內觀)
    Reflection(反射)
    有時候我們說某個語言具有很強的動態性,有時候我們會區分動態和靜態的不同技術與作法。我們
    朗朗上口動態綁定(dynamic binding)、動態鏈接(dynamic linking)、動態加載
    (dynamic loading)等。然而“動態”一詞其實沒有絕對而普遍適用的嚴格定義,有時候甚至
    像對象導向當初被導入編程領域一樣,一人一把號,各吹各的調。
    一般而言,開發者社群說到動態語言,大致認同的一個定義是:“程序運行時,允許改變程序結構
    或變量類型,這種語言稱為動態語言”。從這個觀點看,Perl,Python,Ruby是動態語言,C+
    +,Java,C#不是動態語言。
    盡管在這樣的定義與分類下Java不是動態語言,它卻有著一個非常突出的動態相關機制:
    Reflection。這個字的意思是“反射、映象、倒影”,用在Java身上指的是我們可以于運行時加
    載、探知、使用編譯期間完全未知的classes。換句話說,Java程序可以加載一個運行時才得知名
    稱的class,獲悉其完整構造(但不包括methods定義),并生成其對象實體、或對其fields設
    值、或喚起其methods1。這種“看透class”的能力(the ability of the program to
    examine itself)被稱為introspection(內省、內觀、反省)。Reflection和
    introspection是常被并提的兩個術語。
    Java如何能夠做出上述的動態特性呢?這是一個深遠話題,本文對此只簡單介紹一些概念。整個篇
    幅最主要還是介紹Reflection APIs,也就是讓讀者知道如何探索class的結構、如何對某
    個“運行時才獲知名稱的class”生成一份實體、為其fields設值、調用其methods。本文將談到
    file:///H|/download/806.html(第 1/12 頁)2005-9-8 12:03:22
    侯捷觀點??Java反射機制
    java.lang.Class,以及java.lang.reflect中的Method、Field、Constructor等等
    classes。
    “Class”class
    眾所周知Java有個Object class,是所有Java classes的繼承根源,其內聲明了數個應該在所
    有Java class中被改寫的methods:hashCode()、equals()、clone()、toString()、
    getClass()等。其中getClass()返回一個Class object。
    Class class十分特殊。它和一般classes一樣繼承自Object,其實體用以表達Java程序運行時
    的classes和interfaces,也用來表達enum、array、primitive Java types
    (boolean, byte, char, short, int, long, float, double)以及關鍵詞void。當一
    個class被加載,或當加載器(class loader)的defineClass()被JVM調用,JVM 便自動產
    生一個Class object。如果您想借由“修改Java標準庫源碼”來觀察Class object的實際生成
    時機(例如在Class的constructor內添加一個println()),不能夠!因為Class并沒有
    public constructor(見圖1)。本文最后我會撥一小塊篇幅順帶談談Java標準庫源碼的改動辦
    法。
    Class是Reflection故事起源。針對任何您想探勘的class,唯有先為它產生一個Class
    object,接下來才能經由后者喚起為數十多個的Reflection APIs。這些APIs將在稍后的探險
    活動中一一亮相。
    #001 public final
    #002 class Class<T> implements java.io.Serializable,
    #003 java.lang.reflect.GenericDeclaration,
    #004 java.lang.reflect.Type,
    #005 java.lang.reflect.AnnotatedElement {
    #006 private Class() {}
    #007 public String toString() {
    #008 return ( isInterface() ? "interface " :
    #009 (isPrimitive() ? "" : "class "))
    #010 + getName();
    #011 }
    ...
    圖1:Class class片段。注意它的private empty ctor,意指不允許任何人經由編程方式產生Class object。是的,其object 只能由
    JVM 產生。
    “Class” object的取得途徑
    Java允許我們從多種管道為一個class生成對應的Class object。圖2是一份整理。
    Class object 誕生管道示例
    運用getClass()
    注:每個class 都有此函數
    String str = "abc";
    Class c1 = str.getClass();
    file:///H|/download/806.html(第 2/12 頁)2005-9-8 12:03:22
    侯捷觀點??Java反射機制
    運用
    Class.getSuperclass()2
    Button b = new Button();
    Class c1 = b.getClass();
    Class c2 = c1.getSuperclass();
    運用static method
    Class.forName()
    (最常被使用)
    Class c1 = Class.forName ("java.lang.
    String");
    Class c2 = Class.forName ("java.awt.Button");
    Class c3 = Class.forName ("java.util.
    LinkedList$Entry");
    Class c4 = Class.forName ("I");
    Class c5 = Class.forName ("[I");
    運用
    .class 語法
    Class c1 = String.class;
    Class c2 = java.awt.Button.class;
    Class c3 = Main.InnerClass.class;
    Class c4 = int.class;
    Class c5 = int[].class;
    運用
    primitive wrapper
    classes
    的TYPE 語法
    Class c1 = Boolean.TYPE;
    Class c2 = Byte.TYPE;
    Class c3 = Character.TYPE;
    Class c4 = Short.TYPE;
    Class c5 = Integer.TYPE;
    Class c6 = Long.TYPE;
    Class c7 = Float.TYPE;
    Class c8 = Double.TYPE;
    Class c9 = Void.TYPE;
    圖2:Java 允許多種管道生成Class object。
    Java classes 組成分析
    首先容我以圖3的java.util.LinkedList為例,將Java class的定義大卸八塊,每一塊分別對
    應圖4所示的Reflection API。圖5則是“獲得class各區塊信息”的程序示例及執行結果,它們
    都取自本文示例程序的對應片段。
    package java.util; //(1)
    import java.lang.*; //(2)
    public class LinkedList<E> //(3)(4)(5)
    extends AbstractSequentialList<E> //(6)
    implements List<E>, Queue<E>,
    Cloneable, java.io.Serializable //(7)
    {
    private static class Entry<E> { … }//(8)
    public LinkedList() { … } //(9)
    public LinkedList(Collection<? extends E> c) { … }
    public E getFirst() { … } //(10)
    public E getLast() { … }
    private transient Entry<E> header = …; //(11)
    private transient int size = 0;
    }
    file:///H|/download/806.html(第 3/12 頁)2005-9-8 12:03:22
    侯捷觀點??Java反射機制
    圖3:將一個Java class 大卸八塊,每塊相應于一個或一組Reflection APIs(圖4)。
    Java classes 各成份所對應的Reflection APIs
    圖3的各個Java class成份,分別對應于圖4的Reflection API,其中出現的Package、
    Method、Constructor、Field等等classes,都定義于java.lang.reflect。
    Java class 內
    部模塊(參見圖
    3)
    Java class 內部模塊說明相應之Reflection
    API,多半為Class
    methods。
    返回值類型
    (return type)
    (1) package class隸屬哪個package getPackage() Package
    (2) import class導入哪些classes 無直接對應之API。
    解決辦法見圖5-2。
    (3) modifier class(或methods,
    fields)的屬性
    int getModifiers()
    Modifier.toString
    (int)
    Modifier.
    isInterface(int)
    int
    String
    bool
    (4) class
    name or
    interface
    name
    class/interface 名稱getName() String
    (5) type
    parameters
    參數化類型的名稱getTypeParameters
    ()
    TypeVariable
    <Class>[]
    (6) base
    class
    base class(只可能一個) getSuperClass() Class
    (7)
    implemented
    interfaces
    實現有哪些interfaces getInterfaces() Class[]
    (8) inner
    classes
    內部classes getDeclaredClasses
    ()
    Class[]
    (8') outer
    class
    如果我們觀察的class 本身是
    inner classes,那么相對它
    就會有個outer class。
    getDeclaringClass() Class
    (9)
    constructors
    構造函數
    getDeclaredConstructors
    ()
    不論 public 或
    private 或其它
    access level,皆可獲
    得。另有功能近似之取得
    函數。
    Constructor[]
    file:///H|/download/806.html(第 4/12 頁)2005-9-8 12:03:22
    侯捷觀點??Java反射機制
    (10) methods 操作函數
    getDeclaredMethods()
    不論 public 或
    private 或其它
    access level,皆可獲
    得。另有功能近似之取得
    函數。
    Method[]
    (11) fields 字段(成員變量) getDeclaredFields()
    不論 public 或
    private 或其它
    access level,皆可獲
    得。另有功能近似之取得
    函數。
    Field[]
    圖4:Java class大卸八塊后(如圖3),每一塊所對應的Reflection API。本表并非
    Reflection APIs 的全部。
    Java Reflection API 運用示例
    圖5示范圖4提過的每一個Reflection API,及其執行結果。程序中出現的tName()是個輔助函
    數,可將其第一自變量所代表的“Java class完整路徑字符串”剝除路徑部分,留下class名
    稱,儲存到第二自變量所代表的一個hashtable去并返回(如果第二自變量為null,就不儲存而只
    是返回)。
    #001 Class c = null;
    #002 c = Class.forName(args[0]);
    #003
    #004 Package p;
    #005 p = c.getPackage();
    #006
    #007 if (p != null)
    #008 System.out.println("package "+p.getName()+";");
    執行結果(例):
    package java.util;
    圖5-1:找出class 隸屬的package。其中的c將繼續沿用于以下各程序片段。
    #001 ff = c.getDeclaredFields();
    #002 for (int i = 0; i < ff.length; i++)
    #003 x = tName(ff[i].getType().getName(), classRef);
    #004
    #005 cn = c.getDeclaredConstructors();
    #006 for (int i = 0; i < cn.length; i++) {
    #007 Class cx[] = cn[i].getParameterTypes();
    #008 for (int j = 0; j < cx.length; j++)
    #009 x = tName(cx[j].getName(), classRef);
    #010 }
    #011
    #012 mm = c.getDeclaredMethods();
    #013 for (int i = 0; i < mm.length; i++) {
    #014 x = tName(mm[i].getReturnType().getName(), classRef);
    #015 Class cx[] = mm[i].getParameterTypes();
    file:///H|/download/806.html(第 5/12 頁)2005-9-8 12:03:22
    侯捷觀點??Java反射機制
    #016 for (int j = 0; j < cx.length; j++)
    #017 x = tName(cx[j].getName(), classRef);
    #018 }
    #019 classRef.remove(c.getName()); //不必記錄自己(不需import 自己)
    執行結果(例):
    import java.util.ListIterator;
    import java.lang.Object;
    import java.util.LinkedList$Entry;
    import java.util.Collection;
    import java.io.ObjectOutputStream;
    import java.io.ObjectInputStream;
    圖5-2:找出導入的classes,動作細節詳見內文說明。
    #001 int mod = c.getModifiers();
    #002 System.out.print(Modifier.toString(mod)); //整個modifier
    #003
    #004 if (Modifier.isInterface(mod))
    #005 System.out.print(" "); //關鍵詞 "interface" 已含于modifier
    #006 else
    #007 System.out.print(" class "); //關鍵詞 "class"
    #008 System.out.print(tName(c.getName(), null)); //class 名稱
    執行結果(例):
    public class LinkedList
    圖5-3:找出class或interface 的名稱,及其屬性(modifiers)。
    #001 TypeVariable<Class>[] tv;
    #002 tv = c.getTypeParameters(); //warning: unchecked conversion
    #003 for (int i = 0; i < tv.length; i++) {
    #004 x = tName(tv[i].getName(), null); //例如 E,K,V...
    #005 if (i == 0) //第一個
    #006 System.out.print("<" + x);
    #007 else //非第一個
    #008 System.out.print("," + x);
    #009 if (i == tv.length-1) //最后一個
    #010 System.out.println(">");
    #011 }
    執行結果(例):
    public abstract interface Map<K,V>
    或 public class LinkedList<E>
    圖5-4:找出parameterized types 的名稱
    #001 Class supClass;
    #002 supClass = c.getSuperclass();
    #003 if (supClass != null) //如果有super class
    #004 System.out.print(" extends" +
    #005 tName(supClass.getName(),classRef));
    執行結果(例):
    public class LinkedList<E>
    extends AbstractSequentialList,
    file:///H|/download/806.html(第 6/12 頁)2005-9-8 12:03:22
    侯捷觀點??Java反射機制
    圖5-5:找出base class。執行結果多出一個不該有的逗號于尾端。此非本處重點,為簡化計,不多做處理。
    #001 Class cc[];
    #002 Class ctmp;
    #003 //找出所有被實現的interfaces
    #004 cc = c.getInterfaces();
    #005 if (cc.length != 0)
    #006 System.out.print(", \r\n" + " implements "); //關鍵詞
    #007 for (Class cite : cc) //JDK1.5 新式循環寫法
    #008 System.out.print(tName(cite.getName(), null)+", ");
    執行結果(例):
    public class LinkedList<E>
    extends AbstractSequentialList,
    implements List, Queue, Cloneable, Serializable,
    圖5-6:找出implemented interfaces。執行結果多出一個不該有的逗號于尾端。此非本處重點,為簡化計,不多做處
    理。
    #001 cc = c.getDeclaredClasses(); //找出inner classes
    #002 for (Class cite : cc)
    #003 System.out.println(tName(cite.getName(), null));
    #004
    #005 ctmp = c.getDeclaringClass(); //找出outer classes
    #006 if (ctmp != null)
    #007 System.out.println(ctmp.getName());
    執行結果(例):
    LinkedList$Entry
    LinkedList$ListItr
    圖5-7:找出inner classes 和outer class
    #001 Constructor cn[];
    #002 cn = c.getDeclaredConstructors();
    #003 for (int i = 0; i < cn.length; i++) {
    #004 int md = cn[i].getModifiers();
    #005 System.out.print(" " + Modifier.toString(md) + " " +
    #006 cn[i].getName());
    #007 Class cx[] = cn[i].getParameterTypes();
    #008 System.out.print("(");
    #009 for (int j = 0; j < cx.length; j++) {
    #010 System.out.print(tName(cx[j].getName(), null));
    #011 if (j < (cx.length - 1)) System.out.print(", ");
    #012 }
    #013 System.out.print(")");
    #014 }
    執行結果(例):
    public java.util.LinkedList(Collection)
    public java.util.LinkedList()
    圖5-8a:找出所有constructors
    #004 System.out.println(cn[i].toGenericString());
    執行結果(例):
    file:///H|/download/806.html(第 7/12 頁)2005-9-8 12:03:22
    侯捷觀點??Java反射機制
    public java.util.LinkedList(java.util.Collection<? extends E>)
    public java.util.LinkedList()
    圖5-8b:找出所有constructors。本例在for 循環內使用toGenericString(),省事。
    #001 Method mm[];
    #002 mm = c.getDeclaredMethods();
    #003 for (int i = 0; i < mm.length; i++) {
    #004 int md = mm[i].getModifiers();
    #005 System.out.print(" "+Modifier.toString(md)+" "+
    #006 tName(mm[i].getReturnType().getName(), null)+" "+
    #007 mm[i].getName());
    #008 Class cx[] = mm[i].getParameterTypes();
    #009 System.out.print("(");
    #010 for (int j = 0; j < cx.length; j++) {
    #011 System.out.print(tName(cx[j].getName(), null));
    #012 if (j < (cx.length - 1)) System.out.print(", ");
    #013 }
    #014 System.out.print(")");
    #015 }
    執行結果(例):
    public Object get(int)
    public int size()
    圖5-9a:找出所有methods
    #004 System.out.println(mm[i].toGenericString());
    public E java.util.LinkedList.get(int)
    public int java.util.LinkedList.size()
    圖5-9b:找出所有methods。本例在for 循環內使用toGenericString(),省事。
    #001 Field ff[];
    #002 ff = c.getDeclaredFields();
    #003 for (int i = 0; i < ff.length; i++) {
    #004 int md = ff[i].getModifiers();
    #005 System.out.println(" "+Modifier.toString(md)+" "+
    #006 tName(ff[i].getType().getName(), null) +" "+
    #007 ff[i].getName()+";");
    #008 }
    執行結果(例):
    private transient LinkedList$Entry header;
    private transient int size;
    圖5-10a:找出所有fields
    #004 System.out.println("G: " + ff[i].toGenericString());
    private transient java.util.LinkedList.java.util.LinkedList$Entry<E> ??
    java.util.LinkedList.header
    private transient int java.util.LinkedList.size
    圖5-10b:找出所有fields。本例在for 循環內使用toGenericString(),省事。
    找出class參用(導入)的所有classes
    file:///H|/download/806.html(第 8/12 頁)2005-9-8 12:03:22
    侯捷觀點??Java反射機制
    沒有直接可用的Reflection API可以為我們找出某個class參用的所有其它classes。要獲得這
    項信息,必須做苦工,一步一腳印逐一記錄。我們必須觀察所有fields的類型、所有methods(包
    括constructors)的參數類型和回返類型,剔除重復,留下唯一。這正是為什么圖5-2程序代碼
    要為tName()指定一個hashtable(而非一個null)做為第二自變量的緣故:hashtable可為我
    們儲存元素(本例為字符串),又保證不重復。
    本文討論至此,幾乎可以還原一個class的原貌(唯有methods 和ctors的定義無法取得)。接下
    來討論Reflection 的另三個動態性質:(1) 運行時生成instances,(2) 執
    行期喚起methods,(3) 運行時改動fields。
    運行時生成instances
    欲生成對象實體,在Reflection 動態機制中有兩種作法,一個針對“無自變量ctor”,
    一個針對“帶參數ctor”。圖6是面對“無自變量ctor”的例子。如果欲調用的是“帶參數
    ctor“就比較麻煩些,圖7是個例子,其中不再調用Class的newInstance(),而是調用
    Constructor 的newInstance()。圖7首先準備一個Class[]做為ctor的參數類型(本例指定
    為一個double和一個int),然后以此為自變量調用getConstructor(),獲得一個專屬ctor。
    接下來再準備一個Object[] 做為ctor實參值(本例指定3.14159和125),調用上述專屬ctor
    的newInstance()。
    #001 Class c = Class.forName("DynTest");
    #002 Object obj = null;
    #003 obj = c.newInstance(); //不帶自變量
    #004 System.out.println(obj);
    圖6:動態生成“Class object 所對應之class”的對象實體;無自變量。
    #001 Class c = Class.forName("DynTest");
    #002 Class[] pTypes = new Class[] { double.class, int.class };
    #003 Constructor ctor = c.getConstructor(pTypes);
    #004 //指定parameter list,便可獲得特定之ctor
    #005
    #006 Object obj = null;
    #007 Object[] arg = new Object[] {3.14159, 125}; //自變量
    #008 obj = ctor.newInstance(arg);
    #009 System.out.println(obj);
    圖7:動態生成“Class object 對應之class”的對象實體;自變量以Object[]表示。
    運行時調用methods
    這個動作和上述調用“帶參數之ctor”相當類似。首先準備一個Class[]做為ctor的參數類型
    (本例指定其中一個是String,另一個是Hashtable),然后以此為自變量調用getMethod(),
    獲得特定的Method object。接下來準備一個Object[]放置自變量,然后調用上述所得之特定
    Method object的invoke(),如圖8。知道為什么索取Method object時不需指定回返類型
    嗎?因為method overloading機制要求signature(署名式)必須唯一,而回返類型并非
    signature的一個成份。換句話說,只要指定了method名稱和參數列,就一定指出了一個獨一無
    二的method。
    file:///H|/download/806.html(第 9/12 頁)2005-9-8 12:03:22
    侯捷觀點??Java反射機制
    #001 public String func(String s, Hashtable ht)
    #002 {
    #003 …System.out.println("func invoked"); return s;
    #004 }
    #005 public static void main(String args[])
    #006 {
    #007 Class c = Class.forName("Test");
    #008 Class ptypes[] = new Class[2];
    #009 ptypes[0] = Class.forName("java.lang.String");
    #010 ptypes[1] = Class.forName("java.util.Hashtable");
    #011 Method m = c.getMethod("func",ptypes);
    #012 Test obj = new Test();
    #013 Object args[] = new Object[2];
    #014 arg[0] = new String("Hello,world");
    #015 arg[1] = null;
    #016 Object r = m.invoke(obj, arg);
    #017 Integer rval = (String)r;
    #018 System.out.println(rval);
    #019 }
    圖8:動態喚起method
    運行時變更fields內容
    與先前兩個動作相比,“變更field內容”輕松多了,因為它不需要參數和自變量。首先調用
    Class的getField()并指定field名稱。獲得特定的Field object之后便可直接調用Field的
    get()和set(),如圖9。
    #001 public class Test {
    #002 public double d;
    #003
    #004 public static void main(String args[])
    #005 {
    #006 Class c = Class.forName("Test");
    #007 Field f = c.getField("d"); //指定field 名稱
    #008 Test obj = new Test();
    #009 System.out.println("d= " + (Double)f.get(obj));
    #010 f.set(obj, 12.34);
    #011 System.out.println("d= " + obj.d);
    #012 }
    #013 }
    圖9:動態變更field 內容
    Java 源碼改動辦法
    先前我曾提到,原本想借由“改動Java標準庫源碼”來測知Class object的生成,但由于其
    ctor原始設計為private,也就是說不可能透過這個管道生成Class object(而是由class
    loader負責生成),因此“在ctor中打印出某種信息”的企圖也就失去了意義。
    這里我要談點題外話:如何修改Java標準庫源碼并讓它反應到我們的應用程序來。假設我想修改
    java.lang.Class,讓它在某些情況下打印某種信息。首先必須找出標準源碼!當你下載JDK 套
    file:///H|/download/806.html(第 10/12 頁)2005-9-8 12:03:22
    侯捷觀點??Java反射機制
    件并安裝妥當,你會發現jdk150\src\java\lang 目錄(見圖10)之中有Class.java,這就是
    我們此次行動的標準源碼。備份后加以修改,編譯獲得Class.class。接下來準備將.class 搬移
    到jdk150\jre\lib\endorsed(見圖10)。
    這是一個十分特別的目錄,class loader將優先從該處讀取內含classes的.jar文件??成功的
    條件是.jar內的classes壓縮路徑必須和Java標準庫的路徑完全相同。為此,我們可以將剛才做
    出的Class.class先搬到一個為此目的而刻意做出來的\java\lang目錄中,壓縮為foo.zip(任
    意命名,唯需夾帶路徑java\lang),再將這個foo.zip搬到jdk150\jre\lib\endorsed并改
    名為foo.jar。此后你的應用程序便會優先用上這里的java.lang.Class。整個過程可寫成一個
    批處理文件(batch file),如圖11,在DOS Box中使用。
    圖10
    圖10:JDK1.5 安裝后的目錄組織。其中的endorsed 是我新建。
    del e:\java\lang\*.class //清理干凈
    del c:\jdk150\jre\lib\endorsed\foo.jar //清理干凈
    c:
    cd c:\jdk150\src\java\lang
    javac -Xlint:unchecked Class.java //編譯源碼
    javac -Xlint:unchecked ClassLoader.java //編譯另一個源碼(如有必要)
    move *.class e:\java\lang //搬移至刻意制造的目錄中
    e:
    cd e:\java\lang //以下壓縮至適當目錄
    pkzipc -add -path=root c:\jdk150\jre\lib\endorsed\foo.jar *.class
    cd e:\test //進入測試目錄
    javac -Xlint:unchecked Test.java //編譯測試程序
    java Test //執行測試程序
    圖11:一個可在DOS Box中使用的批處理文件(batch file),用以自動化java.lang.Class
    的修改動作。Pkzipc(.exe)是個命令列壓縮工具,add和path都是其命令。
    更多信息
    以下是視野所及與本文主題相關的更多討論。這些信息可以彌補因文章篇幅限制而帶來的不足,或
    帶給您更多視野。
    l "Take an in-depth look at the Java Reflection API -- Learn about
    the new Java 1.1 tools forfinding out information about classes", by
    Chuck McManis。此篇文章所附程序代碼是本文示例程序的主要依據(本文示例程序示范了更
    多Reflection APIs,并采用JDK1.5 新式的for-loop 寫法)。
    l "Take a look inside Java classes -- Learn to deduce properties of
    a Java class from inside aJava program", by Chuck McManis。
    l "The basics of Java class loaders -- The fundamentals of this key
    component of the Javaarchitecture", by Chuck McManis。
    l 《The Java Tutorial Continued》, Sun microsystems. Lesson58-61,
    "Reflection".
    file:///H|/download/806.html(第 11/12 頁)2005-9-8 12:03:22
    侯捷觀點??Java反射機制
    注1用過諸如MFC這類所謂 Application Framework的程序員也許知道,MFC有所謂的
    dynamic creation。但它并不等同于Java的動態加載或動態辨識;所有能夠在MFC程序中起作用
    的classes,都必須先在編譯期被編譯器“看見”。
    注2如果操作對象是Object,Class.getSuperClass()會返回null。
    本文程序源碼可至侯捷網站下載:
    http://www.jjhou.com/javatwo-2004-reflection-and-generics-in-jdk15-sample.ZIP
    發表于 2004年10月27日 11:30 AM
    posted @ 2005-11-17 09:45 安德爾斯 閱讀(785) | 評論 (0)編輯 收藏

    傳值?還是傳引用?

    (Wang hailong)

     

    關于編程的參數傳遞問題,總是存在著這樣的爭論。傳值?還是傳引用?(還是傳指針?還是傳地址?)這些提法,經常出現在C++, java, C#的編程技術文檔里面。這個問題也經常引起開發人員的爭論,徒耗人力物力。實際上,這根本不成為問題,只是由于人為加入的概念,混淆了人們的視聽。

    從程序運行的角度來看,參數傳遞,只有傳值,從不傳遞其它的東西。只不過,值的內容有可能是數據,也有可能是一個內存地址

    開發人員應該了解程序的編譯結果是怎樣在計算機中運行的。程序運行的時候,使用的空間可以分為兩個部分,棧和堆。棧是指運行棧,局部變量,參數,都分配在棧上。程序運行的時候,新生成的對象,都分配在堆里,堆里分配的對象,棧里的數據參數,或局部變量。

    下面舉一個C++的例子。

    public class Object{

      int i;

      public Object(int i){

           this.i = i;

      }

     

           public int getValue(){

                  return i;

           }

     

           public void setValue(int i){

                  this.i = i;

           }

    };

     

    class A {

           Void func1(int a, Object b){

                  Object * c = new Object( a );

                 

    b = c;

           }

     

    public      void main(){

           Object * param = new Object( 1 );

     

                  func1( 2,  param )

                 

                  // what is value of parram now ?

                  // it is still 1.

        }

    };

     

    我們來看一下,當調用到func1函數時,運行到Object * c = new Object( a ); 棧和堆的狀態。不同編譯器生成的代碼運行的結果可能會稍有不同。但參數和局部變量的大致排放順序都是相同的。

     

    這時候,我們來看,param變量被壓入運行棧的時候,只是進行了簡單的復制。把param里面的內容拷貝到b里面。這時候,b就指向了Object(1)。這里的參數傳遞,是把param的值傳遞給b

    下面我們來看,程序執行到b = c;時候的堆棧狀態。

     

    我們可以看到,b現在指向了Object(2)。但是對param的值毫無影響。param的值還是Object(1)

    所以,我們說,對參數的賦值不會影響到外層函數的數據,但是,調用參數的操作方法,卻等于直接操作外層函數的數據。比如,如果我們在func1()函數中,不調用b=c;而調用b.setValue(3),那么Object(1)的數據就會變為3param的數據也會改變為3

    javaC#中的情況,也都是一樣。

    C++還有一種變量定義方法,表面上看起來,不符合上面的說明,這里進行說明。

    Object * a = new Object(1);

    Object & * b = a;

    這里的b就等于是a的另外一個別名,b就是a。對b賦值就等于對a賦值。甚至作為參數傳遞時,也是如此。對這種類型的參數的賦值,就等于對外層函數數據的賦值。

    public class B{

    void func1(Object & * b){

           b = new Object(4);

    }

     

    public void main(){

           Object * a = new Object(1);

     

           func1(a);

     

           // a is changed to Object(4) now.

    }

     

    }

    當運行完func1(a);時,a的值變化為Object(4)。這是因為編譯器實際把參數Object & * b編譯為Object ** b_addrb_addr的值是b的地址,也就是a的地址。

    當調用func1()的時候,實際上是把b_addr作為參數壓到棧里,b_addr的值是a的地址。

    當執行b = new Object(4); 時,實際執行了 b_addr->b = new Object(4); 也就是執行了 b_addr->a = new Object(4); a的值當然變化了。

     

    還有一點需要說明,當使用COMCORBA等中間件規范進行開發時,我們需要定義IDL語言。參數的類型分為,[in][out][in, out],其中的RPC遠程調用的參數打包規范,就更復雜了,但原理卻是一樣的。

    posted @ 2005-11-17 09:43 安德爾斯 閱讀(388) | 評論 (0)編輯 收藏
    /* 
    * Created on 2004-8-4 

    * To change the template for this generated file go to 
    * Window>Preferences>Java>Code Generation>Code and Comments 
    */ 
    package myclass.test; 

    import java.awt.*; 
    import java.awt.image.*; 
    import java.util.*; 

    /** 
    * @author 

    * To change the template for this generated type comment go to 
    * Window>Preferences>Java>Code Generation>Code and Comments 
    */ 
    public class Image { 

    public String sRand=""; 

    public Color getRandColor(int fc,int bc){//給定范圍獲得隨機顏色 
    Random random = new Random(); 
    if(fc>255) fc=255; 
    if(bc>255) bc=255; 
    int r=fc+random.nextInt(bc-fc); 
    int g=fc+random.nextInt(bc-fc); 
    int b=fc+random.nextInt(bc-fc); 
    return new Color(r,g,b); 

    public BufferedImage creatImage(){ 

    // 在內存中創建圖象 
    int width=60, height=20; 
    BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 

    // 獲取圖形上下文 
    Graphics g = image.getGraphics(); 

    //生成隨機類 
    Random random = new Random(); 

    // 設定背景色 
    g.setColor(getRandColor(200,250)); 
    g.fillRect(0, 0, width, height); 

    //設定字體 
    g.setFont(new Font("Times New Roman",Font.PLAIN,18)); 

    //畫邊框 
    //g.setColor(new Color()); 
    //g.drawRect(0,0,width-1,height-1); 


    // 隨機產生155條干擾線,使圖象中的認證碼不易被其它程序探測到 
    g.setColor(getRandColor(160,200)); 
    for (int i=0;i<155;i++) 

    int x = random.nextInt(width); 
    int y = random.nextInt(height); 
    int xl = random.nextInt(12); 
    int yl = random.nextInt(12); 
    g.drawLine(x,y,x+xl,y+yl); 


    // 取隨機產生的認證碼(4位數字) 
    //String rand = request.getParameter("rand"); 
    //rand = rand.substring(0,rand.indexOf(".")); 

    for (int i=0;i<4;i++){ 
    String rand=String.valueOf(random.nextInt(10)); 
    sRand+=rand; 
    // 將認證碼顯示到圖象中 
    g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110)));//調用函數出來的顏色相同,可能是因為種子太接近,所以只能直接生成 
    g.drawString(rand,13*i+6,16); 

    // 圖象生效 
    g.dispose(); 
    return image; 


    ====================================================================== 
    image.jsp(對bean的引用) 

    <%@ page contentType="image/jpeg" import="javax.imageio.*" %> 
    <jsp:useBean id="image" scope="session" class="myclass.test.Image"/> 

    <% 
    //設置頁面不緩存 
    response.setHeader("Pragma","No-cache"); 
    response.setHeader("Cache-Control","no-cache"); 
    response.setDateHeader("Expires", 0); 

    // 將認證碼存入SESSION 
    session.setAttribute("rand",image.sRand); 

    // 輸出圖象到頁面 
    ImageIO.write(image.creatImage(), "JPEG", response.getOutputStream()); 


    %> 
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     大家經常在網上登陸的時候經常會看到讓你輸入驗證碼,有的是文字的,有的呢是圖片,比如chinaren.com校友錄中留言的時候,我們就會看到數字圖片驗證碼;網上關于數字文字驗證碼實現方法的相關資料很多,而我們這里介紹的是數字和字母隨機組成的并且生成圖片的驗證碼的實現方法。看起來很復雜、其實很簡單的,大家跟著我往下看:

      首先,我們先介紹一下設計思路,數字和字母的隨機組合生成驗證碼,然后將驗證碼生成圖片,這里“數字和字母的組合”應該是隨機取出來的;如果是專門的數字驗證碼,我們可以這樣實現:

      ycodenum=4 '驗證碼的位數,或者說成個數
      for i=1 to ycodenum
        Randomize '初始化隨機數發生器
        ycode=ycode&Int((9*Rnd)) 'rnd是隨機數,從0到1之間的任意實數,這里獲得0到9之間的整數
      next

      response.write ycode '就可以輸出數字驗證碼(4位)

      然而,我們要讓數字和字母同樣隨機生成,這里我們可以用到數組來實現這種效果,如下:

      ychar="0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z" '將數字和大寫字母組成一個字符串
      yc=split(char,",") '將字符串生成數組
      ycodenum=4
      for i=1 to ycodenum
        Randomize
        ycode=ycode&yc(Int((35*Rnd))) '數組一般從0開始讀取,所以這里為35*Rnd
      next

      response.write ycode 
      
      現在看看輸出結果是不是數字和字母隨機組合的呢?

      下面看看怎樣生成圖片,這個也許有些朋友知道:asp不能生成圖片,必須使用asp組件。不錯,我們這里使用的是ASP圖象組件shotgraph。有一點大家注意,服務器不是自己的不能用哦,因為你裝不了這組件。

      組件的下載地址:  Response.BinaryWrite (img)

      針對以上代碼也就是說shotgraph普通的畫圖的原理請參考:
    http://www.pconline.com.cn/pcedu/empolder/wz/asp/10204/45207.html
    posted @ 2005-11-17 09:40 安德爾斯 閱讀(1755) | 評論 (3)編輯 收藏
    僅列出標題  下一頁
    主站蜘蛛池模板: 亚洲一区二区三区首页| 亚洲人成色777777在线观看| 国产特黄一级一片免费| 亚洲狠狠成人综合网| 亚洲情XO亚洲色XO无码| 啊v在线免费观看| 成人免费淫片在线费观看| 青青草无码免费一二三区| 国产精品永久免费视频| 亚洲av永久无码精品秋霞电影秋 | 亚洲男人天堂2022| 亚洲视频中文字幕| 亚洲va无码va在线va天堂| 亚洲狠狠爱综合影院婷婷| 永久久久免费浮力影院| 九九精品免费视频| 91香焦国产线观看看免费| 97人妻精品全国免费视频| 一区二区三区免费电影| 美女啪啪网站又黄又免费| 亚洲精品无码一区二区| 亚洲 欧洲 日韩 综合在线| 亚洲成在人线中文字幕| 7777久久亚洲中文字幕蜜桃| 亚洲AV无码成人网站久久精品大 | 日韩亚洲人成网站| 亚洲码和欧洲码一码二码三码 | 天天看免费高清影视| 免费观看的毛片大全| 麻豆视频免费观看| 7723日本高清完整版免费| 91精品国产免费久久国语麻豆| 久久久久久免费一区二区三区| 两个人看的www免费视频| 一个人免费视频在线观看www| 久久国产精品免费一区二区三区| 国产精品九九久久免费视频| 中文字幕无码免费久久9一区9| 91成人免费福利网站在线| 国产一二三四区乱码免费| 精品国产污污免费网站|