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

          3. 你們的測(cè)試組還在用Word寫測(cè)試用例么? 
          ?不要用Word寫測(cè)試用例(Test Case)。應(yīng)該用一個(gè)專門的系統(tǒng),可以是Test Manager,也可以是自己開(kāi)發(fā)一個(gè)ASP.NET的小網(wǎng)站。主要目的是Track和Browse。
          ?
          4. 你們的項(xiàng)目組有沒(méi)有建立一個(gè)門戶網(wǎng)站? 
           要有一個(gè)門戶網(wǎng)站,用來(lái)放Contact Info、Baselined Schedule、News等等。推薦Sharepoint Portal Server 2003來(lái)實(shí)現(xiàn),15分鐘就搞定。買不起SPS 2003可以用WSS (Windows Sharepoint Service)。

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

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

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

          32. 你們公司有沒(méi)有積累一個(gè)項(xiàng)目風(fēng)險(xiǎn)列表? 
           要。Risk Inventory。否則,下個(gè)項(xiàng)目開(kāi)始的時(shí)候,又只能拍腦袋分析Risk了。
          ?
          33. 設(shè)計(jì)越簡(jiǎn)單越好越簡(jiǎn)單越好。 
           設(shè)計(jì)時(shí)候多一句話,將來(lái)可能就帶來(lái)無(wú)窮無(wú)盡的煩惱。應(yīng)該從一開(kāi)始就勇敢的砍。這叫scope management。

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

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

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

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

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

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

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

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

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

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

          記住建議就是建議!!!
          posted @ 2006-07-02 19:30 安德?tīng)査?閱讀(605) | 評(píng)論 (0)編輯 收藏

          我們?cè)?A >www.sync4j.org網(wǎng)站上下載Sync4j的源代碼,解壓到任一目錄,其中只有sync4j這個(gè)子目錄的元源碼是我們需要的,其他的目錄中的代碼是一些工具代碼什么的,暫時(shí)用不到,用到的時(shí)候在拷貝進(jìn)工程就可以了。

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

          文件的內(nèi)容可能這樣:
          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編譯并部署到你的應(yīng)用服務(wù)器上,就可以進(jìn)行手機(jī),pda,windows mbile等移動(dòng)設(shè)備的數(shù)據(jù)同步開(kāi)發(fā)。

          posted @ 2005-11-26 00:33 安德?tīng)査?閱讀(1283) | 評(píng)論 (7)編輯 收藏

          前言:

          在我們學(xué)習(xí)Java的過(guò)程中,掌握其中的基本概念對(duì)我們的學(xué)習(xí)無(wú)論是J2SE,J2EE,J2ME都是很重要的,J2SE是Java的基礎(chǔ),所以有必要對(duì)其中的基本概念做以歸納,以便大家在以后的學(xué)習(xí)過(guò)程中更好的理解java的精髓,在此我總結(jié)了30條基本的概念.

          Java概述:

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

          Java的白皮書為我們提出了Java語(yǔ)言的關(guān)鍵特性.

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

          基本概念:

          1.OOP中惟一關(guān)系的是對(duì)象的接口是什么,就像計(jì)算機(jī)的銷售商她不管電源內(nèi)部結(jié)構(gòu)是怎樣的,他只關(guān)系能否給你提供電就行了,也就是只要知道can or not而不是how and why.所有的程序是由一定的屬性和行為對(duì)象組成的,不同的對(duì)象的訪問(wèn)通過(guò)函數(shù)調(diào)用來(lái)完成,對(duì)象間所有的交流都是通過(guò)方法調(diào)用,通過(guò)對(duì)封裝對(duì)象數(shù)據(jù),很大限度上提高復(fù)用率.

          2.OOP中最重要的思想是類,類是模板是藍(lán)圖,從類中構(gòu)造一個(gè)對(duì)象,即創(chuàng)建了這個(gè)類的一個(gè)實(shí)例(instance)

          3.封裝:就是把數(shù)據(jù)和行為結(jié)合起在一個(gè)包中)并對(duì)對(duì)象使用者隱藏?cái)?shù)據(jù)的實(shí)現(xiàn)過(guò)程,一個(gè)對(duì)象中的數(shù)據(jù)叫他的實(shí)例字段(instance field)

          4.通過(guò)擴(kuò)展一個(gè)類來(lái)獲得一個(gè)新類叫繼承(inheritance),而所有的類都是由Object根超類擴(kuò)展而得,根超類下文會(huì)做介紹.

          5.對(duì)象的3個(gè)主要特性
          behavior---說(shuō)明這個(gè)對(duì)象能做什么.
          state---當(dāng)對(duì)象施加方法時(shí)對(duì)象的反映.

          identity---與其他相似行為對(duì)象的區(qū)分標(biāo)志.
          每個(gè)對(duì)象有惟一的indentity 而這3者之間相互影響.

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

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

          8.重載:當(dāng)多個(gè)方法具有相同的名字而含有不同的參數(shù)時(shí),便發(fā)生重載.編譯器必須挑選出調(diào)用哪個(gè)方法.

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

          10.繼承思想:允許在已經(jīng)存在的類的基礎(chǔ)上構(gòu)建新的類,當(dāng)你繼承一個(gè)已經(jīng)存在的類時(shí),那么你就復(fù)用了這個(gè)類的方法和字段,同時(shí)你可以在新類中添加新的方法和字段.

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

          12.多態(tài):在java中,對(duì)象變量是多態(tài)的.而java中不支持多重繼承.

          13.動(dòng)態(tài)綁定:調(diào)用對(duì)象方法的機(jī)制.
          (1)編譯器檢查對(duì)象聲明的類型和方法名.
          (2)編譯器檢查方法調(diào)用的參數(shù)類型.
          (3)靜態(tài)綁定:若方法類型為priavte static final 編譯器會(huì)準(zhǔn)確知道該調(diào)用哪個(gè)方法.
          (4)當(dāng)程序運(yùn)行并且使用動(dòng)態(tài)綁定來(lái)調(diào)用一個(gè)方法時(shí),那么虛擬機(jī)必須調(diào)用x所指向的對(duì)象的實(shí)際類型相匹配的方法版本.
          (5)動(dòng)態(tài)綁定:是很重要的特性,它能使程序變得可擴(kuò)展而不需要重編譯已存代碼.

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

          15.動(dòng)態(tài)調(diào)用比靜態(tài)調(diào)用花費(fèi)的時(shí)間要長(zhǎng),

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

          17.Java中的每一個(gè)類都是從Object類擴(kuò)展而來(lái)的.

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

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

          20.數(shù)組列表:ArrayList動(dòng)態(tài)數(shù)組列表,是一個(gè)類庫(kù),定義在java.uitl包中,可自動(dòng)調(diào)節(jié)數(shù)組的大小.

          21.class類 object類中的getClass方法返回class類型的一個(gè)實(shí)例,程序啟動(dòng)時(shí)包含在main方法的類會(huì)被加載,虛擬機(jī)要加載他需要的所有類,每一個(gè)加載的類都要加載它需要的類.

          22.class類為編寫可動(dòng)態(tài)操縱java代碼的程序提供了強(qiáng)大的功能:反射,這項(xiàng)功能為JavaBeans特別有用,使用反射Java能支持VB程序員習(xí)慣使用的工具.

          能夠分析類能力的程序叫反射器,Java中提供此功能的包叫Java.lang.reflect反射機(jī)制十分強(qiáng)大.
          1.在運(yùn)行時(shí)分析類的能力.
          2.在運(yùn)行時(shí)探察類的對(duì)象.
          3.實(shí)現(xiàn)通用數(shù)組操縱代碼.
          4.提供方法對(duì)象.

          而此機(jī)制主要針對(duì)是工具者而不是應(yīng)用及程序.

          反射機(jī)制中的最重要的部分是允許你檢查類的結(jié)構(gòu).用到的API有:
          java.lang.reflect.Field 返回字段.
          java.reflect.Method 返回方法.
          java.lang.reflect.Constructor 返回參數(shù).

          方法指針:java沒(méi)有方法指針,把一個(gè)方法的地址傳給另一個(gè)方法,可以在后面調(diào)用它,而接口是更好的解決方案.

          23.接口(Interface)說(shuō)明類該做什么而不指定如何去做,一個(gè)類可以實(shí)現(xiàn)一個(gè)或多個(gè)interface.

          24.接口不是一個(gè)類,而是對(duì)符合接口要求的類的一套規(guī)范.

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

          25.一個(gè)類只有一個(gè)超類,但一個(gè)類能實(shí)現(xiàn)多個(gè)接口.Java中的一個(gè)重要接口Cloneable

          26.接口和回調(diào).編程一個(gè)常用的模式是回調(diào)模式,在這種模式中你可以指定當(dāng)一個(gè)特定時(shí)間發(fā)生時(shí)回調(diào)對(duì)象上的方法.
          例:ActionListener 接口監(jiān)聽(tīng).
          類似的API有:java.swing.JOptionPane
          java.swing.Timer
          java.awt.Tookit

          27.對(duì)象clone:clone方法是object一個(gè)保護(hù)方法,這意味著你的代碼不能簡(jiǎn)單的調(diào)用它.

          28.內(nèi)部類:一個(gè)內(nèi)部類的定義是定義在另一個(gè)類的內(nèi)部,原因是:1.一個(gè)內(nèi)部類的對(duì)象能夠訪問(wèn)創(chuàng)建它的對(duì)象的實(shí)現(xiàn),包括私有數(shù)據(jù)
          2.對(duì)于同一個(gè)包中的其他類來(lái)說(shuō),內(nèi)部類能夠隱藏起來(lái).
          3.匿名內(nèi)部類可以很方便的定義回調(diào).
          4.使用內(nèi)部類可以非常方便的編寫事件驅(qū)動(dòng)程序.

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

          30.數(shù)據(jù)類型:Java是強(qiáng)調(diào)類型的語(yǔ)言,每個(gè)變量都必須先申明它都類型,java中總共有8個(gè)基本類型.4種是整型,2種是浮點(diǎn)型,一種是字符型,被用于Unicode編碼中的字符,布爾型.(T111)

          posted @ 2005-11-17 10:51 安德?tīng)査?閱讀(278) | 評(píng)論 (0)編輯 收藏

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

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

          主要類與方法和描述

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

          主要類與方法和描述

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

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

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

          主要類與方法和描述

          ObjectOutputStream.writeObject() //將一個(gè)對(duì)象序列化到外部字節(jié)流 
          ObjectInputStream.readObject() //從外部字節(jié)流讀取并重新構(gòu)造對(duì)象
           
              從實(shí)際應(yīng)用上看來(lái),“Serializable”這個(gè)接口并沒(méi)有定義任何方法,仿佛它只是一個(gè)標(biāo)記(或者說(shuō)像是Java的關(guān)鍵字)而已,一旦虛擬機(jī)看到這個(gè)“標(biāo)記”,就會(huì)嘗試調(diào)用自身預(yù)定義的序列化機(jī)制,除非你在實(shí)現(xiàn)Serializable接口的同時(shí)還定義了私有的readObject()或writeObject()方法。這一點(diǎn)很奇怪。不過(guò)你要是不愿意讓系統(tǒng)使用缺省的方式進(jìn)行序列化,那就必須定義上面提到的兩個(gè)方法:

          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()里調(diào)用默認(rèn)的序列化方法ObjectOutputStream.defaultWriteObject();譬如你不愿意將某些敏感的屬性和信息序列化,你也可以調(diào)用ObjectOutputStream.writeObject()方法明確指定需要序列化那些屬性。關(guān)于用戶可定制的序列化方法,我們將在后面提到。

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

          主要類與方法和描述

          XMLEncoder.writeObject() //將一個(gè)對(duì)象序列化到外部字節(jié)流 
          XMLDecoder.readObject() //從外部字節(jié)流讀取并重新構(gòu)造對(duì)象 

              如果一個(gè)bean是如下格式:

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

          那么通過(guò)XMLEcoder序列化出來(lái)的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,當(dāng)然也是可以用這種方式序列化的,下面就是從JDK文檔中摘錄的一個(gè)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>

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

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

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

          就能獲得全部環(huán)境變量的列表:

          -- 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\項(xiàng)目\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\項(xiàng)目\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() //從一個(gè)外部流讀取屬性 
          store() //將屬性保存到外部流(特別是文件) 
          getProperty() //取得一個(gè)指定的屬性 
          setProperty() //設(shè)置一個(gè)指定的屬性 
          list() //列出這個(gè)Properties對(duì)象包含的全部“key-value”對(duì) 
          System.getProperties() //取得系統(tǒng)當(dāng)前的環(huán)境變量 


              你可以這樣保存一個(gè)properties文件:


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

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

          主要類與方法和描述

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

              你可以按如下步驟保存數(shù)據(jù):

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


          網(wǎng)絡(luò)I/O:Socket→RMI

          ★Socket
              Socket編程可能大家都很熟,所以就不多討論了,只是說(shuō)通過(guò)socket把數(shù)據(jù)保存到遠(yuǎn)端服務(wù)器或從網(wǎng)絡(luò)socket讀取數(shù)據(jù)也不失為一種值得考慮的方式。

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

          值傳遞:這又包括兩種子情形:如果是基本數(shù)據(jù)類型,那么都是“可序列化”的,統(tǒng)統(tǒng)序列化成可傳輸?shù)淖止?jié)流;如果是對(duì)象,而且不是“遠(yuǎn)程對(duì)象”(所謂“遠(yuǎn)程對(duì)象”是實(shí)現(xiàn)了java.rmi.Remote接口的對(duì)象),本來(lái)對(duì)象傳遞的應(yīng)該是引用,但由于上述原因,引用是不足以證明對(duì)象身份的,所以傳遞的仍然是一個(gè)序列化的拷貝(當(dāng)然這個(gè)對(duì)象也必須滿足上述“可序列化”的條件)。

          引用傳遞:可以引用傳遞的只能是“遠(yuǎn)程對(duì)象”。這里所謂的“引用”不要理解成了真的只是一個(gè)符號(hào),它其實(shí)是一個(gè)留在(客戶機(jī))本地stub中的,和遠(yuǎn)端服務(wù)器上那個(gè)真實(shí)的對(duì)象張得一模一樣的鏡像而已!只是因?yàn)樗悬c(diǎn)“特權(quán)”(不需要經(jīng)過(guò)序列化),在本地內(nèi)存里已經(jīng)有了一個(gè)實(shí)例,真正引用的其實(shí)是這個(gè)“孿生子”。
           
              由此可見(jiàn),序列化在RMI當(dāng)中占有多么重要的地位。

          數(shù)據(jù)庫(kù)I/O:CMP、Hibernate

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

          ★CMP和Hibernate
              因?yàn)槲覍?duì)J2EE的東西不是太熟悉,隨便找了點(diǎn)材料看看,所以擔(dān)心說(shuō)的不到位,這次就不作具體總結(jié)了,人要學(xué)習(xí)……真是一件痛苦的事情

          序列化再探討

              從以上技術(shù)的討論中我們不難體會(huì)到,序列化是Java之所以能夠出色地實(shí)現(xiàn)其鼓吹的兩大賣點(diǎn)??分布式(distributed)和跨平臺(tái)(OS independent)的一個(gè)重要基礎(chǔ)。TIJ(即“Thinking in Java”)談到I/O系統(tǒng)時(shí),把序列化稱為“l(fā)ightweight persistence”??“輕量級(jí)的持久化”,這確實(shí)很有意思。

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

          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;
          ...

              注意,這里我們?cè)趯?shí)例化a和b的時(shí)候,有意讓他們的c屬性使用同一個(gè)C類型對(duì)象的引用,譬如c1,那么請(qǐng)?jiān)囅胍幌拢覀冃蛄谢痑和b的時(shí)候,它們的c屬性在外部字節(jié)流(當(dāng)然可以不僅僅是文件)里保存的是一份拷貝還是兩份拷貝呢?序列化在這里使用的是一種類似于“指針”的方案:它為每個(gè)被序列化的對(duì)象標(biāo)上一個(gè)“序列號(hào)”(serial number),但序列化一個(gè)對(duì)象的時(shí)候,如果其某個(gè)屬性對(duì)象是已經(jīng)被序列化的,那么這里只向輸出流寫入該屬性的序列號(hào);從字節(jié)流恢復(fù)被序列化的對(duì)象時(shí),也根據(jù)序列號(hào)找到對(duì)應(yīng)的流來(lái)恢復(fù)。這就是“序列化”名稱的由來(lái)!這里我們看到“序列化”和“指針”是極相似的,只不過(guò)“指針”是內(nèi)存空間的地址鏈,而序列化用的是外部流中的“序列號(hào)鏈”。
              使用“序列號(hào)”而不是內(nèi)存地址來(lái)標(biāo)識(shí)一個(gè)被序列化的對(duì)象,是因?yàn)閺牧髦谢謴?fù)對(duì)象到內(nèi)存,其地址可能就未必是原來(lái)的地址了??我們需要的只是這些對(duì)象之間的引用關(guān)系,而不是死板的原始位置,這在RMI中就更是必要,在兩臺(tái)不同的機(jī)器之間傳遞對(duì)象(流),根本就不可能指望它們?cè)趦膳_(tái)機(jī)器上都具有相同的內(nèi)存地址。

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

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

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

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

          一點(diǎn)啟示

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

          參考資料


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

          J2SE進(jìn)階. 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分布式對(duì)象:RMI和CORBA. by IBM developerWorks

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

          Reflection 是 Java 程序開(kāi)發(fā)語(yǔ)言的特征之一,它允許運(yùn)行中的 Java 程序?qū)ψ陨磉M(jìn)行檢查,或者說(shuō)“自審”,并能直接操作程序的內(nèi)部屬性。例如,使用它能獲得 Java 類中各成員的名稱并顯示出來(lái)。

          Java 的這一能力在實(shí)際應(yīng)用中也許用得不是很多,但是在其它的程序設(shè)計(jì)語(yǔ)言中根本就不存在這一特性。例如,Pascal、C 或者 C++ 中就沒(méi)有辦法在程序中獲得函數(shù)定義相關(guān)的信息。

          JavaBean 是 reflection 的實(shí)際應(yīng)用之一,它能讓一些工具可視化的操作軟件組件。這些工具通過(guò) reflection 動(dòng)態(tài)的載入并取得 Java 組件(類) 的屬性。



          1. 一個(gè)簡(jiǎn)單的例子

          考慮下面這個(gè)簡(jiǎn)單的例子,讓我們看看 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);
                 }
             }
          }

          按如下語(yǔ)句執(zhí)行:

          java DumpMethods java.util.Stack

          它的結(jié)果輸出為:

          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 類的各方法名以及它們的限制符和返回類型。

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

          2.開(kāi)始使用 Reflection

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

          下面就是獲得一個(gè) Class 對(duì)象的方法之一:

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

          這條語(yǔ)句得到一個(gè) String 類的類對(duì)象。還有另一種方法,如下面的語(yǔ)句:

          Class c = int.class;

          或者

          Class c = Integer.TYPE;

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

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

          一旦取得這個(gè)信息,就可以進(jìn)行第三步了??使用 reflection API 來(lái)操作這些信息,如下面這段代碼:

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

          Method m[] = c.getDeclaredMethods();

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

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

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

          模擬 instanceof 操作符

          得到類信息之后,通常下一個(gè)步驟就是解決關(guān)于 Class 對(duì)象的一些基本的問(wèn)題。例如,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);
                 }
             }
          }

          在這個(gè)例子中創(chuàng)建了一個(gè) A 類的 Class 對(duì)象,然后檢查一些對(duì)象是否是 A 的實(shí)例。Integer(37) 不是,但 new A() 是。

          3.找出類的方法

          找出一個(gè)類中定義了些什么方法,這是一個(gè)非常有價(jià)值也非常基礎(chǔ)的 reflection 用法。下面的代碼就實(shí)現(xiàn)了這一用法:

          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);
                 }
             }
          }

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

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

          輸出的結(jié)果如下:

          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.獲取構(gòu)造器信息

          獲取類構(gòu)造器的用法與上述獲取方法的用法類似,如:

          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);
                 }
             }
          }

          這個(gè)例子中沒(méi)能獲得返回類型的相關(guān)信息,那是因?yàn)闃?gòu)造器沒(méi)有返回類型。

          這個(gè)程序運(yùn)行的結(jié)果是:

          name = constructor1

          decl class = class constructor1

          -----

          name = constructor1

          decl class = class constructor1

          param #0 int

          param #1 double

          -----

          5.獲取類的字段(域)

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


          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);
                 }
             }
          }

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

          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 =

          -----

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


          6.根據(jù)方法的名稱來(lái)執(zhí)行方法

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

          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);
                 }
             }
          }

          假如一個(gè)程序在執(zhí)行的某處的時(shí)候才知道需要執(zhí)行某個(gè)方法,這個(gè)方法的名稱是在程序的運(yùn)行過(guò)程中指定的 (例如,JavaBean 開(kāi)發(fā)環(huán)境中就會(huì)做這樣的事),那么上面的程序演示了如何做到。

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

          7.創(chuàng)建新的對(duì)象

          對(duì)于構(gòu)造器,則不能像執(zhí)行方法那樣進(jìn)行,因?yàn)閳?zhí)行一個(gè)構(gòu)造器就意味著創(chuàng)建了一個(gè)新的對(duì)象 (準(zhǔn)確的說(shuō),創(chuàng)建一個(gè)對(duì)象的過(guò)程包括分配內(nèi)存和構(gòu)造對(duì)象)。所以,與上例最相似的例子如下:

          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);
                 }
             }
          }

          根據(jù)指定的參數(shù)類型找到相應(yīng)的構(gòu)造函數(shù)并執(zhí)行它,以創(chuàng)建一個(gè)新的對(duì)象實(shí)例。使用這種方法可以在程序運(yùn)行時(shí)動(dòng)態(tài)地創(chuàng)建對(duì)象,而不是在編譯的時(shí)候創(chuàng)建對(duì)象,這一點(diǎn)非常有價(jià)值。

          8.改變字段(域)的值

          reflection 的還有一個(gè)用處就是改變對(duì)象數(shù)據(jù)字段的值。reflection 可以從正在運(yùn)行的程序中根據(jù)名稱找到對(duì)象的字段并改變它,下面的例子可以說(shuō)明這一點(diǎn):

          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);
                 }
             }
          }

          這個(gè)例子中,字段 d 的值被變?yōu)榱?nbsp;12.34。

          9.使用數(shù)組

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

          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);
                 }
             }
          }

          例中創(chuàng)建了 10 個(gè)單位長(zhǎng)度的 String 數(shù)組,為第 5 個(gè)位置的字符串賦了值,最后將這個(gè)字符串從數(shù)組中取得并打印了出來(lái)。

          下面這段代碼提供了一個(gè)更復(fù)雜的例子:

          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]);
             }
          }
          例中創(chuàng)建了一個(gè) 5 x 10 x 15 的整型數(shù)組,并為處于 [3][5][10] 的元素賦了值為 37。注意,多維數(shù)組實(shí)際上就是數(shù)組的數(shù)組,例如,第一個(gè) Array.get 之后,arrobj 是一個(gè) 10 x 15 的數(shù)組。進(jìn)而取得其中的一個(gè)元素,即長(zhǎng)度為 15 的數(shù)組,并使用 Array.setInt 為它的第 10 個(gè)元素賦值。

          注意創(chuàng)建數(shù)組時(shí)的類型是動(dòng)態(tài)的,在編譯時(shí)并不知道其類型。



          posted @ 2005-11-17 09:53 安德?tīng)査?閱讀(647) | 評(píng)論 (2)編輯 收藏
          1.對(duì)象的復(fù)制
          2.clone()的使用
          3.對(duì)象實(shí)例的比較
          ////////////////////////////

          1.對(duì)象的復(fù)制


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

          淺復(fù)制: 只復(fù)制復(fù)合對(duì)象本身.
          深復(fù)制: 除了復(fù)制復(fù)合對(duì)象本身, 還復(fù)制了復(fù)合對(duì)象的引用的對(duì)象實(shí)例.

          例如:
          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.         //此時(shí)newClass 與aClass 指向同一塊內(nèi)存
          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.         //此時(shí)newClass 與aClass 的相應(yīng)sno , name 指向同一塊內(nèi)存
          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.         //完全的復(fù)制
          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 進(jìn)行復(fù)制
          兩個(gè)必須條件:
          1.一定要將重定義后的clone() 方法定義為公有方法(在Object 類中, 它是受保護(hù)的成員,    不能直接使用)
          2.該后代類聲明實(shí)現(xiàn)接口 Cloneable 接口(當(dāng)類實(shí)現(xiàn)該接口, 其任何子類也會(huì)繼承該接口), 該接口實(shí)際上沒(méi)有任何
            內(nèi)容, 只是一個(gè)標(biāo)識(shí), 標(biāo)志實(shí)現(xiàn)該接口的類提供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()
          例如對(duì)   private char[] cb; character buffer 進(jìn)行復(fù)制
            
          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.復(fù)制數(shù)組
            數(shù)組是在方法調(diào)用重以引用的形式傳遞的對(duì)象. 下述情況下非常適合引用來(lái)傳遞數(shù)組:
            *正在接收的方法不修改數(shù)組
            *正在調(diào)用的方法不必關(guān)心是否修改數(shù)組
            *正在調(diào)用的方法想要得到數(shù)組中的修改結(jié)果 
            否則, 就應(yīng)該在方法調(diào)用中傳遞數(shù)組對(duì)象的副本. 只需調(diào)用 arrObj.clone() 方法即可完成數(shù)組arrObj 的復(fù)制操作. 隨后將該數(shù)組副本強(qiáng)制轉(zhuǎn)換為其正確類型:
                (type[])arrObj.clone();
             System.arraycopy 方法提供一種用于在數(shù)組間復(fù)制多個(gè)元素的有效方式.
                  System.arraycopy(source, i, target, j, len)

          3.對(duì)象實(shí)例的比較


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

          a. "==" 
             if(p1 == p2)...
            此次測(cè)試的是對(duì)象引用, 其結(jié)果肯定是false, 只要兩個(gè)對(duì)象引用不是互為別名就不會(huì)相等.
          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() 方法, 但它只是比較兩個(gè)對(duì)象引用, 而非比較兩個(gè)對(duì)象實(shí)例.
              不管怎樣, 如果需要比較Pupil 類的對(duì)象(例如要將它們放入對(duì)象容器), 應(yīng)該為Pupil 類重定義equals() 方法:
          1.    
          2.     public boolean equals(Object otherobj) {
          3.         //檢查otherobj 是否為空
          4.         if(otherobj == nullreturn false;
          5.         //檢查otherobj 是否就是當(dāng)前對(duì)象
          6.         if(otherobj == thisreturn true;
          7.         //檢查otherobj 是否具有正確的類型, 即檢查是否可與當(dāng)前對(duì)象比較
          8.         if(!(otherobj instanceof Pupil)) return false;
          9.         //將otherobj 轉(zhuǎn)換為Pupil 類的對(duì)象引用
          10.         Pupil tmpObj = (Pupil)otherobj;
          11.         //關(guān)于學(xué)生是否相等的邏輯檢查
          12.         if(sno.equals(tmpObj.sno) && name.equals(tmpObj.name)
          13.              && age == tmpObj.age) return true;
          14.         
          15.         return false;
          16.     }

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

          傳值?還是傳引用?

          (Wang hailong)

           

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

          從程序運(yùn)行的角度來(lái)看,參數(shù)傳遞,只有傳值,從不傳遞其它的東西。只不過(guò),值的內(nèi)容有可能是數(shù)據(jù),也有可能是一個(gè)內(nèi)存地址

          開(kāi)發(fā)人員應(yīng)該了解程序的編譯結(jié)果是怎樣在計(jì)算機(jī)中運(yùn)行的。程序運(yùn)行的時(shí)候,使用的空間可以分為兩個(gè)部分,棧和堆。棧是指運(yùn)行棧,局部變量,參數(shù),都分配在棧上。程序運(yùn)行的時(shí)候,新生成的對(duì)象,都分配在堆里,堆里分配的對(duì)象,棧里的數(shù)據(jù)參數(shù),或局部變量。

          下面舉一個(gè)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.

              }

          };

           

          我們來(lái)看一下,當(dāng)調(diào)用到func1函數(shù)時(shí),運(yùn)行到Object * c = new Object( a ); 棧和堆的狀態(tài)。不同編譯器生成的代碼運(yùn)行的結(jié)果可能會(huì)稍有不同。但參數(shù)和局部變量的大致排放順序都是相同的。

           

          這時(shí)候,我們來(lái)看,param變量被壓入運(yùn)行棧的時(shí)候,只是進(jìn)行了簡(jiǎn)單的復(fù)制。把param里面的內(nèi)容拷貝到b里面。這時(shí)候,b就指向了Object(1)。這里的參數(shù)傳遞,是把param的值傳遞給b

          下面我們來(lái)看,程序執(zhí)行到b = c;時(shí)候的堆棧狀態(tài)。

           

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

          所以,我們說(shuō),對(duì)參數(shù)的賦值不會(huì)影響到外層函數(shù)的數(shù)據(jù),但是,調(diào)用參數(shù)的操作方法,卻等于直接操作外層函數(shù)的數(shù)據(jù)。比如,如果我們?cè)?/SPAN>func1()函數(shù)中,不調(diào)用b=c;而調(diào)用b.setValue(3),那么Object(1)的數(shù)據(jù)就會(huì)變?yōu)?/SPAN>3param的數(shù)據(jù)也會(huì)改變?yōu)?/SPAN>3

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

          C++還有一種變量定義方法,表面上看起來(lái),不符合上面的說(shuō)明,這里進(jìn)行說(shuō)明。

          Object * a = new Object(1);

          Object & * b = a;

          這里的b就等于是a的另外一個(gè)別名,b就是a。對(duì)b賦值就等于對(duì)a賦值。甚至作為參數(shù)傳遞時(shí),也是如此。對(duì)這種類型的參數(shù)的賦值,就等于對(duì)外層函數(shù)數(shù)據(jù)的賦值。

          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.

          }

           

          }

          當(dāng)運(yùn)行完func1(a);時(shí),a的值變化為Object(4)。這是因?yàn)榫幾g器實(shí)際把參數(shù)Object & * b編譯為Object ** b_addrb_addr的值是b的地址,也就是a的地址。

          當(dāng)調(diào)用func1()的時(shí)候,實(shí)際上是把b_addr作為參數(shù)壓到棧里,b_addr的值是a的地址。

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

           

          還有一點(diǎn)需要說(shuō)明,當(dāng)使用COMCORBA等中間件規(guī)范進(jìn)行開(kāi)發(fā)時(shí),我們需要定義IDL語(yǔ)言。參數(shù)的類型分為,[in][out][in, out],其中的RPC遠(yuǎn)程調(diào)用的參數(shù)打包規(guī)范,就更復(fù)雜了,但原理卻是一樣的。

          posted @ 2005-11-17 09:43 安德?tīng)査?閱讀(391) | 評(píng)論 (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){//給定范圍獲得隨機(jī)顏色 
          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(){ 

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

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

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

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

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

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


          // 隨機(jī)產(chǎn)生155條干擾線,使圖象中的認(rèn)證碼不易被其它程序探測(cè)到 
          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); 


          // 取隨機(jī)產(chǎn)生的認(rèn)證碼(4位數(shù)字) 
          //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; 
          // 將認(rèn)證碼顯示到圖象中 
          g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110)));//調(diào)用函數(shù)出來(lái)的顏色相同,可能是因?yàn)榉N子太接近,所以只能直接生成 
          g.drawString(rand,13*i+6,16); 

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


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

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

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

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

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


          %> 
          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           大家經(jīng)常在網(wǎng)上登陸的時(shí)候經(jīng)常會(huì)看到讓你輸入驗(yàn)證碼,有的是文字的,有的呢是圖片,比如chinaren.com校友錄中留言的時(shí)候,我們就會(huì)看到數(shù)字圖片驗(yàn)證碼;網(wǎng)上關(guān)于數(shù)字文字驗(yàn)證碼實(shí)現(xiàn)方法的相關(guān)資料很多,而我們這里介紹的是數(shù)字和字母隨機(jī)組成的并且生成圖片的驗(yàn)證碼的實(shí)現(xiàn)方法。看起來(lái)很復(fù)雜、其實(shí)很簡(jiǎn)單的,大家跟著我往下看:

            首先,我們先介紹一下設(shè)計(jì)思路,數(shù)字和字母的隨機(jī)組合生成驗(yàn)證碼,然后將驗(yàn)證碼生成圖片,這里“數(shù)字和字母的組合”應(yīng)該是隨機(jī)取出來(lái)的;如果是專門的數(shù)字驗(yàn)證碼,我們可以這樣實(shí)現(xiàn):

            ycodenum=4 '驗(yàn)證碼的位數(shù),或者說(shuō)成個(gè)數(shù)
            for i=1 to ycodenum
              Randomize '初始化隨機(jī)數(shù)發(fā)生器
              ycode=ycode&Int((9*Rnd)) 'rnd是隨機(jī)數(shù),從0到1之間的任意實(shí)數(shù),這里獲得0到9之間的整數(shù)
            next

            response.write ycode '就可以輸出數(shù)字驗(yàn)證碼(4位)

            然而,我們要讓數(shù)字和字母同樣隨機(jī)生成,這里我們可以用到數(shù)組來(lái)實(shí)現(xiàn)這種效果,如下:

            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" '將數(shù)字和大寫字母組成一個(gè)字符串
            yc=split(char,",") '將字符串生成數(shù)組
            ycodenum=4
            for i=1 to ycodenum
              Randomize
              ycode=ycode&yc(Int((35*Rnd))) '數(shù)組一般從0開(kāi)始讀取,所以這里為35*Rnd
            next

            response.write ycode 
            
            現(xiàn)在看看輸出結(jié)果是不是數(shù)字和字母隨機(jī)組合的呢?

            下面看看怎樣生成圖片,這個(gè)也許有些朋友知道:asp不能生成圖片,必須使用asp組件。不錯(cuò),我們這里使用的是ASP圖象組件shotgraph。有一點(diǎn)大家注意,服務(wù)器不是自己的不能用哦,因?yàn)槟阊b不了這組件。

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

            針對(duì)以上代碼也就是說(shuō)shotgraph普通的畫圖的原理請(qǐng)參考:
          http://www.pconline.com.cn/pcedu/empolder/wz/asp/10204/45207.html
          posted @ 2005-11-17 09:40 安德?tīng)査?閱讀(1760) | 評(píng)論 (3)編輯 收藏
          1.關(guān)于在靜態(tài)方法中訪問(wèn)非靜態(tài)內(nèi)部類的問(wèn)題
          public class Outer{
          public String name = "Outer";
          public static void main(String argv[]){
          //Inner myinner = new Inner(); //直接用這句話創(chuàng)建會(huì)編譯錯(cuò)誤
          Outer myouter=new Outer(); //先創(chuàng)建外部類的對(duì)象
          Outer.Inner myinner=myouter.new Inner();
          myinner.showName();
          }//End of main
          //下面這段代碼用來(lái)測(cè)試這種n煩的辦法
          public void amethod(){
          Outer myouter=new Outer();
          Outer.Inner myinner=myouter.new Inner();
          myinner.showName();
          }
          //非靜態(tài)方法訪問(wèn)非靜態(tài)內(nèi)部類
          private class Inner{
          String name =new String("Inner");
          void showName(){
          System.out.println(name);
          }
          }//End of Inner class
          }
          在非靜態(tài)方法訪問(wèn)非靜態(tài)內(nèi)部類直接創(chuàng)建該內(nèi)部類的對(duì)象:new Inner().showName();當(dāng)然也可以采取這種n煩的辦法假設(shè)private class Inner改成static private class Inner, 那么在靜態(tài)方法中訪問(wèn)靜態(tài)內(nèi)部類也是直接創(chuàng)建該內(nèi)部類的對(duì)象,即Inner myinner = new Inner(),或者Outer.Inner myinner = new Outer.Inner()也行得通,可見(jiàn)這種n煩的方法在上面三種情況下都是可以用的。
          2.Abstract方法不能用final,static修飾非abstract方法在abstract類中可以用final,static
          抽象類中的抽象方法不能是final,但是非抽象方法前加final可以編譯通過(guò)因?yàn)閍bstract和final相互排斥,前者專用于繼承,后者禁止繼承
          抽象類中的抽象方法不能為static
          非抽象方法可以為static
          包裹類Integer、 String 、Float、 Double等都是final類,不能被繼承!Integer i=new Integer(“6”);如果字符串不是數(shù)字,會(huì)產(chǎn)生運(yùn)行異常(不會(huì)出現(xiàn)編譯錯(cuò)誤)但是對(duì)于boolean,這個(gè)規(guī)則不適用。當(dāng)字符串時(shí)(大小寫無(wú)關(guān)),Boolean對(duì)象代表的數(shù)值為true,其他字符串均為false如:
          Boolean b = new Boolean(“afiwou”); 代表false
          Boolean b = new Boolean(“tRue”); 是true
          3.多態(tài)性、虛擬方法調(diào)用
          public class Test8 {
          public static void main(String [] args){
          Base b = new Subclass();
          System.out.println(b.x);
          System.out.println(b.method());
          }
          }
          class Base{
          int x = 2;
          int method(){
          return x;
          }
          }
          class Subclass extends Base{
          int x = 3;
          int method(){
          return x;
          }
          }
          結(jié)果是2,3,而不是3,3
          Employee e = new Manager();
          e.department = " Finance " ;
          //department 是Manager的一個(gè)特殊屬性
          聲明變量e后,你能訪問(wèn)的對(duì)象部分只是Employee的部分;Manager的特殊部分是隱藏的。這是因?yàn)榫幾g器應(yīng)意識(shí)到,e 是一個(gè)Employee,而不是一個(gè)Manager。但重寫的方法除外
          在你接收父類的一個(gè)引用時(shí),你可以通過(guò)使用instanceof運(yùn)算符判定該對(duì)象實(shí)際上是你所要的子類,并可以用類型轉(zhuǎn)換該引用的辦法來(lái)恢復(fù)對(duì)象的全部功能。為什么說(shuō)“恢復(fù)對(duì)象的全部功能”,就是因?yàn)樯弦桓袼枋龅模宇悓?duì)象賦給父類句柄后,該句柄不能訪問(wèn)子類的那些特殊屬性和方法,要用就要重新造型。這其實(shí)是多態(tài)參數(shù)的后續(xù)應(yīng)用,形成這樣一個(gè)鏈條:傳入多態(tài)參數(shù)??instanceof判斷類型??casting??恢復(fù)功能
          Employee e = new Manager();
          e.getDetails();
          在此例中,Manager 重寫了Employee的getDetail()方法。被執(zhí)行的e.getDetails()方法來(lái)自對(duì)象的真實(shí)類型:Manager。事實(shí)上,執(zhí)行了與變量的運(yùn)行時(shí)類型(即,變量所引用的對(duì)象的類型)相關(guān)的行為,而不是與變量的編譯時(shí)類型相關(guān)的行為。這是面向?qū)ο笳Z(yǔ)言的一個(gè)重要特征。它也是多態(tài)性的一個(gè)特征,并通常被稱作虛擬方法調(diào)用??“動(dòng)態(tài)綁定”
          寫了這么多也不知道對(duì)你有沒(méi)有幫助呢?
          posted @ 2005-11-17 09:34 安德?tīng)査?閱讀(323) | 評(píng)論 (2)編輯 收藏
          1.關(guān)于參數(shù)的傳遞
          class ValHold{
          public int i = 10;
          }
          public class ObParm{
          public void amethod(){
          ValHold v = new ValHold();
          another(v);
          System.out.println(v.i);
          }
          public void another(ValHold v){
          v.i = 20;
          ValHold vh = new ValHold();
          v =vh;
          System.out.println(v.i);
          }
          public static void main(String[] argv){
          ObParm o = new ObParm();
          o.amethod();
          }
          }
          此題的答案是10,20,為什么不是10,10呢?
          這樣解釋吧,按照sun官方的說(shuō)法:當(dāng)一個(gè)引用變量作為參數(shù)傳遞給一個(gè)方法時(shí), 在這個(gè)方法內(nèi)可以改變變量的值,即改變引用指向的對(duì)象,(本題中將vh賦給v)但是方法的調(diào)用結(jié)束后,改變量恢復(fù)原來(lái)的值,即變量仍然指向原來(lái)的對(duì)象。 (即another(v)調(diào)用結(jié)束之后,v又回復(fù)到第一次ValHold v = new ValHold();時(shí)指向的地址空間。) 但是如果在方法內(nèi)改變了引用指向的對(duì)象的數(shù)據(jù)(屬性),那么當(dāng)方法的調(diào)用結(jié)束后,盡管引用仍然指向原來(lái)的對(duì)象,這個(gè)對(duì)象的某個(gè)屬性已經(jīng)被改變了(v的i值在 執(zhí)行v.i=20的時(shí)候就已經(jīng)被改變了,所以調(diào)用another結(jié)束后,v.i已經(jīng)變成了20) .
          2.關(guān)于內(nèi)部類
          public class InOut{
          String s= new String("Between");
          public void amethod(final int iArgs) {
          int iam;
          class Bicycle{
          Bicycle() {
          System.out.println(s); //這兩句話可以,也就是說(shuō)可以訪問(wèn)s
          System.out.println(iArgs); //和final int 常量
          //System.out.println(iOther);
          }
          }
          new Bicycle();
          }
          public void another(){
          int iOther;
          }
          public static void main(String[] args) {
          InOut inout= new InOut();
          inout.amethod(22);
          }
          }
          Inner class能夠存取外部類的所有實(shí)例變量----無(wú)論這些實(shí)例變量有什么樣的存取控制符(比如private),就像類中的方法能夠存取方法所在類的所有變量一樣;如果inner class定義在方法中,則inner class能夠存取方法所在的類中的實(shí)例變量,也能存取該方法中的局部變量,但該局部變量必須是final的,也就是只能訪問(wèn)方法中的常量.(上面所說(shuō)的都是普通內(nèi)部類,不是靜態(tài)內(nèi)部類的情況).
          public class Testinner {
          int t=10;
          public void a() {
          final int u =90;
          class InMethod { //方法中內(nèi)部類
          InMethod() { //內(nèi)部類的構(gòu)造方法
          System.out.println("u="+u); //封裝方法內(nèi)的變量必須是final才能訪問(wèn)到!
          System.out.println("t="+t); //外部類的變量可以任意訪問(wèn)!
          }
          }
          new InMethod();//必須在方法a()中創(chuàng)建內(nèi)部類對(duì)象之后,Testinner對(duì)象才能通過(guò) a()訪問(wèn)到InMethod類
          }
          public static void main (String[] args) {
          Testinner t= new Testinner();
          t.a();
          }
          }
          輸出:u=90 ,t=10
          方法中的內(nèi)部類不可以是static的!如果一個(gè)內(nèi)部類是靜態(tài)的(當(dāng)然只能是類中的內(nèi)部類啦),那么這個(gè)類就自動(dòng)的成為頂級(jí)(top-level)類即普通的類。靜態(tài)內(nèi)部類中的方法(無(wú)論是靜態(tài)的方法還是非靜態(tài)的方法)只能直接訪問(wèn)外部類中的靜態(tài)成員,要訪問(wèn)外部類中的非靜態(tài)成員,則必須創(chuàng)建外部類的對(duì)象。
          posted @ 2005-11-17 09:33 安德?tīng)査?閱讀(209) | 評(píng)論 (0)編輯 收藏
          主站蜘蛛池模板: 长兴县| 德昌县| 柞水县| 拜城县| 宜黄县| 潜山县| 汶上县| 海口市| 阿克| 英超| 扶绥县| 图木舒克市| 阿城市| 高碑店市| 江孜县| 峨眉山市| 界首市| 皮山县| 双流县| 宁蒗| 绥江县| 新泰市| 织金县| 青川县| 银川市| 加查县| 淳安县| 石景山区| 五常市| 杭锦后旗| 衡东县| 六枝特区| 闵行区| 泰和县| 邵阳市| 长垣县| 仙桃市| 苍山县| 威宁| 龙州县| 介休市|