Posted on 2007-07-31 15:49
zht 阅读(543)
评论(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();
}