Posted on 2007-07-31 15:49
zht 閱讀(549)
評論(0) 編輯 收藏 所屬分類:
J2se
剛才看到的覺得挺好 備份一下
1. 日期部分
對于像日期、時間和錢這樣的對象來說,不同的國家、地區都有不同的顯示格式。即便是同一地區,也可能存在差異。但是在不考慮國家化,時間格式相對固定的情形下,對于時間的處理還是相對比較簡單的。在我最近所作的一個小程序里面,遇到了一些與日期有關的且不考慮國際化和復雜格式的問題。例如如何求兩個日期所差的天數,所差的月數;將日期類轉化為規定格式的字符串,將規定格式的日期字符串轉成相應的日期類等等。下面我就以源碼的形式逐一介紹上面提到的問題,需要注意的是這些代碼都源于我做的一個名為DateUtil的類,其中獨立的變量都是其中的成員變量,函數都是其成員函數:
1.1.成員變量簡介:
要想對日期進行格式化首先就需要一個DateFormat類的實例,如果沒有特殊需求的話,SimpleDateFormat類就是一個不錯的選擇,它可以將日期類格式化為在其構造函數中指定的日期格式。例如,如果我們想要將日期格式化為類似于2007-07-25這樣的格式,我們就可以如下定義:
/**以yyyy-MM-dd的形式顯示格式 **/
SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat sFullFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
這里需要注意的是指定的日期格式一定要是”yyyy-MM-dd”,而不能是”YYYY-MM-DD”,否則的就不能正常顯示。對于這個問題我沒有深究,如果有對這個問題有研究的朋友歡迎留言。
下面的兩個成員變量分別是日期分隔符字符串和字符串分隔器,專門用來解析字符串格式的日期。
/**
*程序中主要的日期分隔符為"-"和"/",且日期序列為“年/月/日”型,其內容缺一不可
* 例如:09/02/02或2009-02-02
**/
publicstaticfinal String DATE_SEPARATOR ="-/";
/**作日期分析之用*/
static StringTokenizer sToken;
1.2.取得兩個日期之間所差天數的方法
鑒于java.util.Date類的絕大多數方法都不建議使用了,所以我也就不能夠利用Date里面方便的getYear(),getMonth(),getDay()方法來計算日期差了—現在的JRE都可以自動升級,誰知道哪天SUN突然把Date這些API去掉了,那我就欲哭無淚了。不過話又說回來就算是能夠使用這些方法,我們似乎也不太好算,因為每個月的日期數都不一樣,如果單純用兩個日期的年月日信息來計算日期差還真是有些麻煩。基于以上兩點原因,我在我的程序里采用了GregorianCalendar做為日期計算的主力軍。這個類有一些很實用的方法,如get(int),這個方法可以獲得當前日期類的各項日期指標。比如我們有一個日期類名為gcDate,要獲取它所在的年,月,日,至需要這么做:gcDate.get(Calendar.YEAR), gcDate.get(Calendar.MONTH)以及gcDate.get(Calendar.DATE)。不過由于使用這種方法獲得的日期和月份都是日期類所指定的年份的,所以如果我們知道兩個日期在同一年份的話才能使用gcDate1.get(Calendar.DATE)- gcDate2.get(Calendar.DATE)來獲得日期差,否則就不能這么做。
所以如果想要獲得不在同一年份的日期差的話就需要用到另一個有用的方法:GregorianCalendar.add(int, int),這個方法可以讓我們在日期類指定的日期指標(如年,月,日等)上加上一個數字,這個數字可以是正數也可以為負數。 其中第一個參數指定所要增加的指標,第二個參數指定增加的數量。例如我們調用gcDate.add(Calendar.DATE,1)的話,如果gcDate原來代表的時間為2007-07-24,那么調用之后就變成2007-07-25了。于是我們就可以這樣計算日期差:讓日期比較小的那個日期用add函數逐漸“逼近”那個較大的日期,直到兩個日期完全相等為止。計數器中包含的數量即為兩個日期的差值。
下面我給出了多個的計算日期差的方法,主要包含兩個版本,一個版本參數為格式化字符串,另一個版本參數為GregorianCalendar。功能包括計算“今天與未來的某一天之間的日期差”和“給定兩個日期的日期差”。主要的計算集中在最后一個daysBetween函數上,該函數接受兩個GregorianCalendar類作為參數,并可以計算出兩個日期之間的日期差,如果用戶給出的較大的日期和較小的日期順序顛倒的話,該函數會返回一個負數值。
/**
*返回未來的某一天和今天所差的日期數
*注意,這里要clone一個新的日期以免對原始日期類造成的修改。
*而在daysBetween(GregorianCalendarpFormer,GregorianCalendarpLatter)就
*直接處理而不進行clone動作,因為這里已經做了:)
**/
publicstaticint daysBetween(GregorianCalendar pFurtherDay){
GregorianCalendar vToday = new GregorianCalendar();
GregorianCalendar vFurtherDay = (GregorianCalendar) pFurtherDay.clone();
return daysBetween(vToday,vFurtherDay);
}
/**上面函數的String版本 **/
publicstaticint daysBetween(String pFurtherDayStr){
GregorianCalendar vFurtherDay = DateUtil.parse2Cal(pFurtherDayStr);
GregorianCalendar vToday = new GregorianCalendar();
return daysBetween(vToday,vFurtherDay);
}
/**返回較晚的時間(latter)與較早的時間(former)所差的天數**/
publicstaticint daysBetween(String pFormerStr,String pLatterStr){
GregorianCalendar pFormer = DateUtil.parse2Cal(pFormerStr);
GregorianCalendar pLatter = DateUtil.parse2Cal(pLatterStr);
return daysBetween(pFormer,pLatter);
}
/**返回較晚的時間(latter)與較早的時間(former)所差的天數**/
publicstaticint daysBetween(GregorianCalendar pFormer,GregorianCalendar pLatter){
GregorianCalendar vFormer = pFormer,vLatter = pLatter;
boolean vPositive = true;
if( pFormer.before(pLatter) ){
vFormer = pFormer;
vLatter = pLatter;
}else{
vFormer = pLatter;
vLatter = pFormer;
vPositive = false;
}
vFormer.set(Calendar.MILLISECOND,0);
vFormer.set(Calendar.SECOND,0);
vFormer.set(Calendar.MINUTE,0);
vFormer.set(Calendar.HOUR_OF_DAY,0);
vLatter.set(Calendar.MILLISECOND,0);
vLatter.set(Calendar.SECOND,0);
vLatter.set(Calendar.MINUTE,0);
vLatter.set(Calendar.HOUR_OF_DAY,0);
int vCounter = 0;
while(vFormer.before(vLatter)){
vFormer.add(Calendar.DATE, 1);
vCounter++;
}
if( vPositive)
return vCounter;
else
return -vCounter;
}
1.3.兩個日期的月份差
獲得兩個日期的月份差的方法與獲得日期差基本一致。但需要注意的是,計算月份差不能簡單用before()來進行計算。還需要考慮到他們的年份及月份是否同時相等,只有在這種情況下,才能獲得月份差的正確數值。下面同樣給出了月份差的兩個版本的多個函數,與日期差基本一致,這里就不再贅述。
/**
*給定兩個時間相差的月數
*/
//本月和未來一個月的月份差
publicstaticint monthsBetween(GregorianCalendar pFurtherMonth){
GregorianCalendar vToday = new GregorianCalendar();
GregorianCalendar vFurtherMonth = (GregorianCalendar) pFurtherMonth.clone();
return monthsBetween(vToday,vFurtherMonth);
}
/**給定月分和本月的月份差**/
publicstaticint monthsBetween(String pFurtherMonth){
GregorianCalendar vToday = new GregorianCalendar();
GregorianCalendar vFurtherMonth = DateUtil.parse2Cal(pFurtherMonth);
return monthsBetween(vToday,vFurtherMonth);
}
/**給定兩個時間相差的月數,String版**/
publicstaticint monthsBetween(String pFormerStr,String pLatterStr){
GregorianCalendar vFormer = DateUtil.parse2Cal(pFormerStr);
GregorianCalendar vLatter = DateUtil.parse2Cal(pLatterStr);
return monthsBetween(vFormer,vLatter);
}
publicstaticint monthsBetween(GregorianCalendar pFormer,GregorianCalendar pLatter){
GregorianCalendar vFormer = pFormer,vLatter = pLatter;
boolean vPositive = true;
if( pFormer.before(pLatter) ){
vFormer = pFormer;
vLatter = pLatter;
}else{
vFormer = pLatter;
vLatter = pFormer;
vPositive = false;
}
int vCounter = 0;
while(vFormer.get(vFormer.YEAR) != vLatter.get(vLatter.YEAR) ||
vFormer.get(vFormer.MONTH) != vLatter.get(vLatter.MONTH)){
vFormer.add(Calendar.MONTH, 1);
vCounter++;
}
if( vPositive)
return vCounter;
else
return -vCounter;
}
1.4.格式轉換
將日期類轉換成制定格式的字符串只需要調用DateFormat.format()即可。而反過來就比較麻煩,我們需要對字符串進行分析,找出其年,月,日的值分別為何,然后再用GregorianCalendar(vYear,vMonth,vDayOfMonth)構建一個新的日期類。
/** 將日期變為字符串格式 **/
publicstatic String format(GregorianCalendar pCal){
returnsDateFormat.format(pCal.getTime());
}
publicstatic String format(Date pDate){
returnsDateFormat.format(pDate);
}
publicstatic String fullFormat(Date pDate){
returnsFullFormat.format(pDate);
}
/**將字符串格式的日期轉換為Calender**/
publicstatic GregorianCalendar parse2Cal(String pDateStr){
sToken = new StringTokenizer(pDateStr,DATE_SEPARATOR);
int vYear = Integer.parseInt(sToken.nextToken());
//GregorianCalendar的月份是從0開始算起的,變態!!
int vMonth = Integer.parseInt(sToken.nextToken())-1;
int vDayOfMonth = Integer.parseInt(sToken.nextToken());
returnnew GregorianCalendar(vYear,vMonth,vDayOfMonth);
}
/**將字符串類型的日期(yyyy-MM-dd)轉換成Date**/
publicstatic Date parse2Date(String pDate){
try {
returnsDateFormat.parse(pDate);
}catch(ParseException ex) {
returnnull;
}
}
1.5.其他
這部分主要是一些常用方法的包裝,包括獲得今天是本月的第幾天,本月是本年的第幾個月,獲得給定字符串日期的月份數,以及獲得下面這個月份區間[本月,本月+pZoneSize]里的月份列表。即如果本月為6月,pZoneSize=6,則獲得的月份列表應該為{6,7,8,9,10,11,12}
/**返回今天是本月的第幾天**/
publicstaticint dayOfMonthOfToday(){
GregorianCalendar vTodayCal = new GregorianCalendar();
return vTodayCal.get(vTodayCal.DAY_OF_MONTH);
}
/**返回本月是本年的第幾個月**/
publicstaticint monthOfYear(){
GregorianCalendar vTodayCal = new GregorianCalendar();
return vTodayCal.get(vTodayCal.MONTH)+1;
}
//返回給定日期的月份
publicstatic String getMonth(String pFormattedDate){
StringTokenizer vSt = new StringTokenizer(pFormattedDate,"-");
vSt.nextToken();//跳過年份
int val = Integer.parseInt(vSt.nextToken());
return val+"";
}
/**獲得從本月開始到本月+pZoneSize區間內的月數**/
publicstatic String[] monthList(int pZoneSize){
String[] vMonthList = new String[pZoneSize];
GregorianCalendar vTodayCal = new GregorianCalendar();
for(int i = 0; i < pZoneSize; i++){
vMonthList[i] = String.valueOf(vTodayCal.get(vTodayCal.MONTH)+1);
vTodayCal.roll(vTodayCal.MONTH,true);
}
return vMonthList;
}
2. 數字部分:
這一部分我的工作比較簡單,首先是格式化給定的Double變量,讓其只顯示兩位小數。其次是由于我所做的程序付款額都是以萬作為單位的,所以在必要的時候將其轉為以萬為單位的,帶有兩位小數的數字。由于比較簡單,這里就不多說,各位看代碼就好:
/**僅顯示小數點后兩位的Formater**/
publicstatic DecimalFormat formatter = new DecimalFormat("####.##");
/**將給定的數字變成小數點后兩位的字符串**/
publicstatic String format(double pSrcVal){
returnformatter.format(pSrcVal);
}
/**將原始數據除以10000所得的結果**/
publicstatic String fromat2Myriad(double pSrcVal){
returnformatter.format(pSrcVal/10000.0);
}
/**將原始數據除以10000所得的結果**/
publicstatic String fromat2Myriad(String pSrcVal){
returnformatter.format(Double.parseDouble(pSrcVal)/10000.0);
}
/**將給定的數字保留兩位小數返回**/
publicstaticdouble format2Double(double pSrcVal){
return (double)Math.round(pSrcVal*100)/100.0;
}
1. Map的遍歷
有人看了這個標題可能發笑,因為這實在太簡單,難道我在湊字數?可是根據我自己的經驗來說,人的歲數長的時候記憶力卻不跟著長,倒有不進反退的現象。我就是雖然寫過很多遍但仍然用到的時候就忘個干凈,在這里寫一下強化記憶J
SortedMap pDataMap = new TreeMap();
Iterator itor = pDataMap.entrySet().iterator();
while(itor.hasNext()){
Map.Entry vEntry = (Entry) itor.next();
vEntry.getKey();
vEntry.getValue();
//做一些其他的事……
}
2. 文件操作
本文主要關注簡單的對象序列化的相關操作,對于更為詳細的操作過程,請參照《Java編程思想 2e》。
2.1.文件過濾器
文件過濾器主要用在FileChooser或者File.listFiles()方法中,用于過濾掉一些與本程序無關的類型文件。本例給出一個察看給定文件后綴名的文件過濾器,對于給定的文件察看其文件名,如果和給定的后綴名一致則返回true,否則返回false。
publicclass RecorderFileFilter implements FilenameFilter
{
/**后綴名 **/
String postfixStr;
public RecorderFileFilter(String postfix){
this.postfixStr = postfix;
}
publicboolean accept(File dir, String name) {
/**只判斷后文件的綴名是否正確即可 **/
if( name.endsWith(postfixStr) )
returntrue;
else
returnfalse;
}
}
2.2.對象的保存與讀取
首先介紹一下我的程序的存儲結構:我的數據保存在兩種類型的文件中,即“時間字符串.data”和“時間字符串.plan”兩種類型。其中.data文件是一個HashMap<String,String>類的序列化文件,而.plan文件是一個SortedMap<String,String>類的序列化文件。他們都保存在“user.dir/data”目錄下。下面的這些方法的功能是讀取/保存這個目錄下數據文件,如果你想保存你自定義的文件到指定目錄,下面的代碼至需要稍微修改一下即可使用:
/**用以標識讀入數據的類型,也是文件后綴名**/
publicstaticfinal String DATA_TYPE = ".data";
publicstaticfinal String PLAN_TYPE = ".plan";
/**以程序運行目錄作為基目錄**/
publicstatic File appDir = new File(System.getProperty("user.dir"));
publicstatic File imgDir = new File(appDir,"img");
publicstatic File dataDir = new File(appDir,"data");
publicstatic File reportDir = new File(appDir,"report");
publicstatic FileInputStream fIn;
publicstatic FileOutputStream fOut;
publicstatic ObjectInputStream objIn;
publicstatic ObjectOutputStream objOut;
/**---------------功能函數部分----------------------**/
/**
*查找并讀入"user.home/data/"目錄下所有的記錄文件,將其放到一個Map中
*HashMap的Key為文件名,也是欠款人帳戶的ID號。
*關于獲得的文件的具體信息請參照技術說明書。
*如果pType=DATA_TYPE,則dataMap的格式為<String,HashMap>
* pType=PLAN_TYPE,則dataMap的格式為<String,SortedMap>
*@returnSortedMap
*/
publicstatic HashMap loadAll(String pType){
/** 數據文件名(簡歷賬號的時間)即為ID **/
HashMap vDataMap = new HashMap();
/**后綴名為data的即為數據文件**/
File[] dataFiles = dataDir.listFiles(new RecorderFileFilter(pType));
for(int i = 0; i < dataFiles.length; i++){
try{
if( pType.equals(FileUtil.DATA_TYPE) ){
HashMap<String,String> hMap = (HashMap<String,String>)loadObjectFromFile(dataFiles[i]);
/**去掉文件名的后綴名即或得key,也就是欠款帳戶ID**/
vDataMap.put(hMap.get("id"),hMap);
}elseif( pType.equals(FileUtil.PLAN_TYPE) ){
SortedMap<String,SortedMap> hMap = (SortedMap<String,SortedMap>)loadObjectFromFile(dataFiles[i]);
/**去掉文件名的后綴名即或得key,也就是欠款帳戶ID**/
vDataMap.put(StringUtil.clearFileName(dataFiles[i]) ,hMap);
}else
thrownew Exception("讀取了錯誤的文件類型");
}
catch(Exception ex) {
MessageUtil.showErrMsg("讀取數據時出現錯誤,數據有可能丟失!"+
"\n 文件名為:"+dataFiles[i].getName());
}
}
return vDataMap;
}
/**
*將給定內容存入文件中.
*@parampContentMapMap其所裝內容為<id(String), HashMap或SortedMap>
*/
publicstaticvoid saveAll(HashMap<String,String> pDataMap,SortedMap<String,String> pPlanMap){
Iterator itor = pDataMap.entrySet().iterator();
while(itor.hasNext()){
Map.Entry vEntry = (Entry) itor.next();
String id = (String) vEntry.getKey();
saveToFile(id,pDataMap,pPlanMap);
}
}
/**
*將制定欠款賬戶的信息存入文件中
*@paramidString 這個信息在保存欠款協議時不可或缺
*@parampDataMapSortedMap
*@parampPlanMapSortedMap
*/
publicstaticvoid saveToFile(String id, HashMap<String,String> pDataMap,
SortedMap<String,String> pPlanMap){
try{
if( pDataMap != null )
saveObjectToFile(new File(dataDir,id+DATA_TYPE), pDataMap);
if( pPlanMap != null )
saveObjectToFile(new File(dataDir,id+PLAN_TYPE), pPlanMap);
}
catch(Exception ex) {
MessageUtil.showErrMsg("保存數據時出現錯誤,數據沒有被正確保存!");
}
}
/**
*將文件中的序列化對象讀出來
*@parampFileFile
*@returnObject
*@throwsException
*/
publicstatic Object loadObjectFromFile(File pFile) throws Exception {
fIn = new FileInputStream(pFile);
objIn = new ObjectInputStream(fIn);
Object obj = objIn.readObject();
fIn.close();
objIn.close();
return obj;
}
/**
*將對象序列化到相應的文件中去--保存數據時候專用,因為該函數將數據保存在appDir/data目錄下
*@parampFileNameString
*@paramobjObject
*@throwsException
*/
publicstaticvoid saveObjectToFile(File pFile,Object pObj) throws Exception
{
fOut = new FileOutputStream(pFile);
objOut = new ObjectOutputStream(fOut);
objOut.writeObject(pObj);
fOut.close();
objOut.close();
}
2.3.其他
由于每個程序都有圖標文件,所以從制定目錄讀入圖標應該是每個程序都會用到的。其次,刪除指定目錄下的指定類型文件似乎也很常用J
/**從img文件夾中獲取圖標**/
publicstatic ImageIcon getImageIcon(String pImageFileName){
File imgFile = new File(imgDir,pImageFileName);
ImageIcon imageIcon = new ImageIcon(imgFile.getAbsolutePath());
return imageIcon;
}
/**清除reportDir目錄下所有html文件**/
publicstaticvoid clearReportFiles(){
File vReportDir = new File(reportDir.getAbsolutePath());
File[] vReportFiles = vReportDir.listFiles(new FilenameFilter(){
publicboolean accept(File dir, String name){
if( name.endsWith("html"))
returntrue;
returnfalse;
}
});
for(int i = 0; i < vReportFiles.length; i++)
vReportFiles[i].delete();
}