亚洲精品GV天堂无码男同,亚洲高清国产拍精品青青草原,亚洲国产精品无码久久http://www.tkk7.com/liuzheng/category/25219.htmlzh-cnWed, 02 Apr 2008 14:18:04 GMTWed, 02 Apr 2008 14:18:04 GMT60Date 和 Calender 的轉化http://www.tkk7.com/liuzheng/articles/190386.html劉錚 劉錚 Wed, 02 Apr 2008 07:40:00 GMThttp://www.tkk7.com/liuzheng/articles/190386.htmlhttp://www.tkk7.com/liuzheng/comments/190386.htmlhttp://www.tkk7.com/liuzheng/articles/190386.html#Feedback0http://www.tkk7.com/liuzheng/comments/commentRss/190386.htmlhttp://www.tkk7.com/liuzheng/services/trackbacks/190386.html  1.計算某一月份的最大天數

Calendar time=Calendar.getInstance();
time.clear();
time.set(Calendar.YEAR,year); //year 為 int
time.set(Calendar.MONTH,i-1);//注意,Calendar對象默認一月為0
int day=time.getActualMaximum(Calendar.DAY_OF_MONTH);//本月份的天數
注:在使用set方法之前,必須先clear一下,否則很多信息會繼承自系統當前時間

2.Calendar和Date的轉化

(1) Calendar轉化為Date
Calendar cal=Calendar.getInstance();
Date date=cal.getTime();

(2) Date轉化為Calendar
Date date=new Date();
Calendar cal=Calendar.getInstance();
cal.setTime(date);

3.格式化輸出日期時間 (這個用的比較多)

Date date=new Date();
SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String time=df.format(date);
System.out.println(time);

4.計算一年中的第幾星期

(1)計算某一天是一年中的第幾星期
Calendar cal=Calendar.getInstance();
cal.set(Calendar.YEAR, 2006);
cal.set(Calendar.MONTH, 8);
cal.set(Calendar.DAY_OF_MONTH, 3);
int weekno=cal.get(Calendar.WEEK_OF_YEAR);

(2)計算一年中的第幾星期是幾號
SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd");
Calendar cal=Calendar.getInstance();
cal.set(Calendar.YEAR, 2006);
cal.set(Calendar.WEEK_OF_YEAR, 1);
cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
System.out.println(df.format(cal.getTime()));
輸出:
2006-01-02

5.add()和roll()的用法(不太常用)

(1)add()方法
SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd");
Calendar cal=Calendar.getInstance();
cal.set(Calendar.YEAR, 2006);
cal.set(Calendar.MONTH, 8);
cal.set(Calendar.DAY_OF_MONTH, 3);
cal.add(Calendar.DATE, -4);
Date date=cal.getTime();
System.out.println(df.format(date));
cal.add(Calendar.DATE, 4);
date=cal.getTime();
System.out.println(df.format(date));
輸出:
2006-08-30
2006-09-03
(2)roll方法
cal.set(Calendar.YEAR, 2006);
cal.set(Calendar.MONTH, 8);
cal.set(Calendar.DAY_OF_MONTH, 3);
cal.roll(Calendar.DATE, -4);
date=cal.getTime();
System.out.println(df.format(date));
cal.roll(Calendar.DATE, 4);
date=cal.getTime();
System.out.println(df.format(date));
輸出:
2006-09-29
2006-09-03
可見,roll()方法在本月內循環,一般使用add()方法;

6.計算兩個任意時間中間的間隔天數(這個比較常用)
(1)傳進Calendar對象
public int getIntervalDays(Calendar startday,Calendar endday)...{
if(startday.after(endday))...{
Calendar cal=startday;
startday=endday;
endday=cal;
}
long sl=startday.getTimeInMillis();
long el=endday.getTimeInMillis();

long ei=el-sl;
return (int)(ei/(1000*60*60*24));
}
(2)傳進Date對象

public int getIntervalDays(Date startday,Date endday)...{
if(startday.after(endday))...{
Date cal=startday;
startday=endday;
endday=cal;
}
long sl=startday.getTime();
long el=endday.getTime();
long ei=el-sl;
return (int)(ei/(1000*60*60*24));
}
(3)改進精確計算相隔天數的方法
public int getDaysBetween (Calendar d1, Calendar d2) ...{
if (d1.after(d2)) ...{
java.util.Calendar swap = d1;
d1 = d2;
d2 = swap;
}
int days = d2.get(Calendar.DAY_OF_YEAR) - d1.get(Calendar.DAY_OF_YEAR);
int y2 = d2.get(Calendar.YEAR);
if (d1.get(Calendar.YEAR) != y2) ...{
d1 = (Calendar) d1.clone();
do ...{
days += d1.getActualMaximum(Calendar.DAY_OF_YEAR);//得到當年的實際天數
d1.add(Calendar.YEAR, 1);
} while (d1.get(Calendar.YEAR) != y2);
}
return days;
}
注意:通過上面的方法可以衍生出求任何時間,如要查出郵箱三周之內收到的郵件(得到當前系統時間-再得到三周前時間)用收件的時間去匹配 最好裝化成 long去比較
如:1年前日期(注意毫秒的轉換)
java.util.Date myDate=new java.util.Date();
long myTime=(myDate.getTime()/1000)-60*60*24*365;
myDate.setTime(myTime*1000);
String mDate=formatter.format(myDate);

7. String 和 Date ,Long 之間相互轉換 (最常用)

字符串轉化成時間類型(字符串可以是任意類型,只要和SimpleDateFormat中的格式一致即可)
通常我們取時間跨度的時候,會substring出具體時間--long-比較

java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("M/dd/yyyy hh:mm:ss a",java.util.Locale.US);
java.util.Date d = sdf.parse("5/13/2003 10:31:37 AM");
long dvalue=d.getTime();
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String mDateTime1=formatter.format(d);

8. 通過時間求時間

年月周求日期
SimpleDateFormat formatter2 = new SimpleDateFormat("yyyy-MM F E");
java.util.Date date2= formatter2.parse("2003-05 5 星期五");
SimpleDateFormat formatter3 = new SimpleDateFormat("yyyy-MM-dd");
String mydate2=formatter3.format(date2);

求是星期幾
mydate= myFormatter.parse("2001-1-1");
SimpleDateFormat formatter4 = new SimpleDateFormat("E");
String mydate3=formatter4.format(mydate);

9. java 和 具體的數據庫結合

在開發web應用中,針對不同的數據庫日期類型,我們需要在我們的程序中對日期類型做各種不同的轉換。若對應數據庫數據是oracle的Date類型,即只需要年月日的,可以選擇使用java.sql.Date類型,若對應的是MSsqlserver 數據庫的DateTime類型,即需要年月日時分秒的,選擇java.sql.Timestamp類型
你可以使用dateFormat定義時間日期的格式,轉一個字符串即可

class Datetest{
*method 將字符串類型的日期轉換為一個timestamp(時間戳記java.sql.Timestamp)
*@param dateString 需要轉換為timestamp的字符串
*@return dataTime timestamp

public final static java.sql.Timestamp string2Time(String dateString)
throws java.text.ParseException {
DateFormat dateFormat;
dateFormat = new SimpleDateFormat("yyyy-MM-dd kk:mm:ss.SSS", Locale.ENGLISH);//設定格式
//dateFormat = new SimpleDateFormat("yyyy-MM-dd kk:mm:ss", Locale.ENGLISH);
dateFormat.setLenient(false);
java.util.Date timeDate = dateFormat.parse(dateString);//util類型
java.sql.Timestamp dateTime = new java.sql.Timestamp(timeDate.getTime());//Timestamp類型,timeDate.getTime()返回一個long型
return dateTime;
}

*method 將字符串類型的日期轉換為一個Date(java.sql.Date
*@param dateString 需要轉換為Date的字符串
*@return dataTime Date

public final static java.sql.Date string2Date(String dateString)
throws java.lang.Exception {
DateFormat dateFormat;
dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);
dateFormat.setLenient(false);
java.util.Date timeDate = dateFormat.parse(dateString);//util類型
java.sql.Date dateTime = new java.sql.Date(timeDate.getTime());//sql類型
return dateTime;
}

public static void main(String[] args){
Date da = new Date();
注意:這個地方da.getTime()得到的是一個long型的值
System.out.println(da.getTime());

由日期date轉換為timestamp

第一種方法:使用new Timestamp(long)
Timestamp t = new Timestamp(new Date().getTime());
System.out.println(t);

第二種方法:使用Timestamp(int year,int month,int date,int hour,int minute,int second,int nano)
Timestamp tt = new Timestamp(Calendar.getInstance().get(
Calendar.YEAR) - 1900, Calendar.getInstance().get(
Calendar.MONTH), Calendar.getInstance().get(
Calendar.DATE), Calendar.getInstance().get(
Calendar.HOUR), Calendar.getInstance().get(
Calendar.MINUTE), Calendar.getInstance().get(
Calendar.SECOND), 0);
System.out.println(tt);

try {
String sToDate = "2005-8-18";//用于轉換成java.sql.Date的字符串
String sToTimestamp = "2005-8-18 14:21:12.123";//用于轉換成java.sql.Timestamp的字符串
Date date1 = string2Date(sToDate);
Timestamp date2 = string2Time(sToTimestamp);
System.out.println("Date:"+date1.toString());//結果顯示
System.out.println("Timestamp:"+date2.toString());//結果顯示
}catch(Exception e) {
e.printStackTrace();
}
}
}

劉錚 2008-04-02 15:40 發表評論
]]>
Jakarta Commons HttpClient 學習筆記(轉)http://www.tkk7.com/liuzheng/articles/180506.html劉錚 劉錚 Mon, 18 Feb 2008 09:49:00 GMThttp://www.tkk7.com/liuzheng/articles/180506.htmlhttp://www.tkk7.com/liuzheng/comments/180506.htmlhttp://www.tkk7.com/liuzheng/articles/180506.html#Feedback0http://www.tkk7.com/liuzheng/comments/commentRss/180506.htmlhttp://www.tkk7.com/liuzheng/services/trackbacks/180506.html1、HttpClient的功能

  1. 基于標準,純正java,實現了http1.0和1.1。
  2. 在一個可擴展的OO框架內,實現了HTTP的全部方法(GET, POST,
    PUT, DELETE, HEAD, OPTIONS, and TRACE)
  3. 支持HTTPS(ssl上的HTTP)的加密操作
  4. 透明地穿過HTTP代理建立連接
  5. 通過CONNECT方法,利用通過建立穿過HTTP代理的HTTPS連接
  6. 利用本地Java socket,透明地穿過SOCKS(版本5和4)代理建立連接
  7. 支持利用Basic、Digest和NTLM加密的認證
  8. 支持用于上傳大文件的Multi-Part表單POST方法
  9. 插件式安全socket實現,易于使用第三方的解決方案
  10. 連接管理,支持多線程應用,支持設定單個主機總連接和最高連接數量,自動檢測和關閉失效連接
  11. 直接將請求信息流送到服務器的端口
  12. 直接讀取從服務器的端口送出的應答信息
  13. 支持HTTP/1.0中用KeepAlive和HTTP/1.1中用persistance設置的持久連接
  14. 直接訪問由服務器送出的應答代碼和頭部信息
  15. 可設置連接超時時間

  16. HttpMethods 實現Command Pattern,以允許并行請求或高效連接復用
  17. 遵循the Apache Software License協議,源碼免費可得

2、預備工作


  對jre1.3.*,如果要HttpClient支持https,則需要下載并安裝java.sun.com/products/jsse/">jsse和java.sun.com/products/jce/">jce.安裝的步驟如下:
1)下載jsse和jce.
2)檢查CLASSPATH中沒有與jsse和jce相關的jar包
3)將 US_export_policy.jar、local_policy.jar、jsse.jar、jnet.jar、jce1_2_x.jar、sunjce_provider.jar、jcert.jar復制到目錄:
UNIX:$JDK_HOME/jre/lib/ext
Windows:%JDK_HOME%\jre\lib\ext
4)修改下述目錄下的java.security文件
UNIX:$JDK_HOME/jre/lib/security/
Windows:%JDK_HOME%\jre\lib\security\
5)

#
# List of providers and their preference orders:
#
security.provider.1=sun.security.provider.Sun
security.provider.2=com.sun.rsajca.Provider
改為:
#
# List of providers and their preference orders:
#
security.provider.1=com.sun.crypto.provider.SunJCE
security.provider.2=sun.security.provider.Sun
security.provider.3=com.sun.rsajca.Provider
security.provider.4=com.sun.net.ssl.internal.ssl.Provider

  HttpClient還要求安裝commons-logging,下面跟httpclient一塊安裝。

3、取得源碼


cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic login
password: anoncvs
cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic checkout jakarta-commons/logging
cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic checkout jakarta-commons/httpclient

  編譯:
cd jakarta-commons/logging
ant dist
cp dis/*.jar ../httpclient/lib/
cd ../httpclient
ant dist

4、使用HttpClient編程的基本步聚


  1. 創建 HttpClient 的一個實例.
  2. 創建某個方法(DeleteMethod,EntityEnclosingMethod,ExpectContinueMethod,GetMethod,HeadMethod,MultipartPostMethod,OptionsMethod,PostMethod,PutMethod,TraceMethod)的一個實例,一般可用要目標URL為參數。
  3. 讓 HttpClient 執行這個方法.
  4. 讀取應答信息.
  5. 釋放連接.
  6. 處理應答.

  在執行方法的過程中,有兩種異常,一種是HttpRecoverableException,表示偶然性錯誤發生,一般再試可能成功,另一種是IOException,嚴重錯誤。
  這兒有這個教程中的一個例程,可以java">下載。

5、認證


  HttpClient三種不同的認證方案: Basic, Digest and NTLM. 這些方案可用于服務器或代理對客戶端的認證,簡稱服務器認證或代理認證
1)服務器認證(Server Authentication)
  HttpClient處理服務器認證幾乎是透明的,僅需要開發人員提供登錄信息(login credentials)。登錄信息保存在HttpState類的實例中,可以通過 setCredentials(String realm, Credentials cred)和getCredentials(String realm)來獲取或設置。注意,設定對非特定站點訪問所需要的登錄信息,將realm參數置為null. HttpClient內建的自動認證,可以通過HttpMethod類的setDoAuthentication(boolean doAuthentication)方法關閉,而且這次關閉只影響HttpMethod當前的實例。
  搶先認證(Preemptive Authentication)可以通過下述方法打開.
client.getState().setAuthenticationPreemptive(true);
  在這種模式時,HttpClient會主動將basic認證應答信息傳給服務器,即使在某種情況下服務器可能返回認證失敗的應答,這樣做主要是為了減少連接的建立。為使每個新建的 HttpState實例都實行搶先認證,可以如下設置系統屬性。
setSystemProperty(Authenticator.PREEMPTIVE_PROPERTY, "true");

Httpclient實現的搶先認證遵循rfc2617.
2)代理認證(proxy authentication)
  除了登錄信息需單獨存放以外,代理認證服務器認證幾乎一致。用 setProxyCredentials(String realm, Credentials cred)和 getProxyCredentials(String realm)設、取登錄信息。
3)認證方案(authentication schemes)
  Basic
  是HTTP中規定最早的也是最兼容(?)的方案,遺憾的是也是最不安全的一個方案,因為它以明碼傳送用戶名和密碼。它要求一個UsernamePasswordCredentials實例,可以指定服務器端的訪問空間或采用默認的登錄信息。
  Digest
  是在HTTP1.1中增加的一個方案,雖然不如Basic得到的軟件支持多,但還是有廣泛的使用。Digest方案比Basic方案安全得多,因它根本就不通過網絡傳送實際的密碼,傳送的是利用這個密碼對從服務器傳來的一個隨機數(nonce)的加密串。它要求一個UsernamePasswordCredentials實例,可以指定服務器端的訪問空間或采用默認的登錄信息。
  NTLM
  這是HttpClient支持的最復雜的認證協議。它M$設計的一個私有協議,沒有公開的規范說明。一開始由于設計的缺陷,NTLM的安全性比Digest差,后來經過一個ServicePack補丁后,安全性則比較Digest高。NTLM需要一個NTCredentials實例. 注意,由于NTLM不使用訪問空間(realms)的概念,HttpClient利用服務器的域名作訪問空間的名字。還需要注意,提供給NTCredentials的用戶名,不要用域名的前綴 - 如: "adrian" 是正確的,而 "DOMAIN\adrian" 則是錯的.
  NTLM認證的工作機制與basic和digest有很大的差別。這些差別一般由HttpClient處理,但理解這些差別有助避免在使用NTLM認證時出現錯誤。
  1. 從HttpClientAPI的角度來看,NTLM與其它認證方式一樣的工作,差別是需要提供'NTCredentials'實例而不是'UsernamePasswordCredentials'(其實,前者只是擴展了后者)
  2. 對NTLM認證,訪問空間是連接到的機器的域名,這對多域名主機會有一些麻煩.只有HttpClient連接中指定的域名才是認證用的域名。建議將realm設為null以使用默認的設置。
  3. NTLM只是認證了一個連接而不是一請求,所以每當一個新的連接建立就要進行一次認證,且在認證的過程中保持連接是非常重要的。 因此,NTLM不能同時用于代理認證服務器認證,也不能用于http1.0連接或服務器不支持持久連接的情況。

6、重定向


  由于技術限制,以及為保證2.0發布版API的穩定,HttpClient還不能自動處重定向,但對重定向到同一主機、同一端口且采用同一協議的情況HttpClient可以支持。不能自動的處理的情況,包括需要人工交互的情況,或超出httpclient的能力。
  當服務器重定向指令指到不同的主機時,HttpClient只是簡單地將重定向狀態碼作為應答狀態。所有的300到399(包含兩端)的返回碼,都表示是重定向應答。常見的有:
  1. 301 永久移動. HttpStatus.SC_MOVED_PERMANENTLY
  2. 302 臨時移動. HttpStatus.SC_MOVED_TEMPORARILY
  3. 303 See Other. HttpStatus.SC_SEE_OTHER
  4. 307 臨時重定向. HttpStatus.SC_TEMPORARY_REDIRECT

  當收到簡單的重定向時,程序應從HttpMethod對象中抽取新的URL并將其下載。另外,限制一下重定向次數是個好的主意,這可以避免遞歸循環。新的URL可以從頭字段Location中抽取,如下:
String redirectLocation;
Header locationHeader = method.getResponseHeader("location");
if (locationHeader != null) {
redirectLocation = locationHeader.getValue();
} else {
// The response is invalid and did not provide the new location for
// the resource. Report an error or possibly handle the response
// like a 404 Not Found error.
}

特殊重定向:
  1. 300 多重選擇. HttpStatus.SC_MULTIPLE_CHOICES
  2. 304 沒有改動. HttpStatus.SC_NO T_MODIFIED
  3. 305 使用代理. HttpStatus.SC_USE_PROXY
  

7、字符編碼(character encoding)


  一個HTTP協議的請求或應答的頭部(在http協議中,數據包分為兩部分,一部分是頭部,由一些名值對構成,一部分是主體(body),是真正傳辦理的數據(如HTML頁面等)),必須以US-ASCII編碼,這是因為頭部不傳數據而只描述被要傳輸的數據的一些信息,一個例外是cookie,它是數據但是通過頭部進行傳輸的,所以它也要用US-ASCII編碼。
  HTTP數據包的主體部分,可以用任何一種方式進行編碼,默認是ISO-8859-1,具體可以用頭部字段Content-Type指定。可以利用 addRequestHeader方法,設定編碼方式;用 getResponseCharSet取得編碼方式。對HTML或XML等類型的文檔,它們的本身的Content-Type也可以指定編碼方式,主要區分兩者的作用范圍以得到正確實的解碼。
  URL的編碼標準,由RFC1738指定為,只能是由可打印8位/字節的us-ascii字符組成,80-ff不是us-ascii字符,而00-1F是控制字符,這兩個區域中用的字符都須加以編碼(encoded)。
  

8、Cookies


   HttpClient能自動管理cookie,包括允許服務器設置cookie并在需要的時候自動將cookie返回服務器,它也支持手工設置cookie后發送到服務器端。不幸的是,對如何處理cookie,有幾個規范互相沖突:Netscape Cookie 草案, RFC2109, RFC2965,而且還有很大數量的軟件商的cookie實現不遵循任何規范. 為了處理這種狀況,HttpClient提供了策略驅動的cookie管理方式。HttpClient支持的cookie規范有:
  1. Netscape cookie草案,是最早的cookie規范,基于rfc2109。盡管這個規范與rc2109有較大的差別,這樣做可以與一些服務器兼容。
  2. rfc2109,是w3c發布的第一個官方cookie規范。理論上講,所有的服務器在處理cookie(版本1)時,都要遵循此規范,正因如此,HttpClient將其設為默認的規范。遺憾的是,這個規范太嚴格了,以致很多服務器不正確的實施了該規范或仍在作用Netscape規范。在這種情況下,應使用兼容規范。
  3. 兼容性規范,設計用來兼容盡可能多的服務器,即使它們并沒有遵循標準規范。當解析cookie出現問題時,應考慮采用兼容性規范。

   RFC2965規范暫時沒有被HttpClient支持(在以后的版本為會加上),它定義了cookie版本2,并說明了版本1cookie的不足,RFC2965有意有久取代rfc2109.
  在HttpClient中,有兩種方法來指定cookie規范的使用,
  1. HttpClient client = new HttpClient();
    client.getState().setCookiePolicy(CookiePolicy.COMPATIBILITY);
    這種方法設置的規范只對當前的HttpState有效,參數可取值CookiePolicy.COMPATIBILITY,CookiePolicy.NETSCAPE_DRAFT或CookiePolicy.RFC2109。
  2. System.setProperty("apache.commons.httpclient.cookiespec", "COMPATIBILITY");
    此法指的規范,對以后每個新建立的HttpState對象都有效,參數可取值"COMPATIBILITY","NETSCAPE_DRAFT"或"RFC2109"。
      常有不能解析cookie的問題,但更換到兼容規范大都能解決。
  

9、使用HttpClient遇到問題怎么辦?


  1. 用一個瀏覽器訪問服務器,以確認服務器應答正常
  2. 如果在使代理,關掉代理試試
  3. 另找一個服務器來試試(如果運行著不同的服務器軟件更好)
  4. 檢查代碼是否按教程中講的思路編寫
  5. 設置log級別為debug,找出問題出現的原因
  6. 打開wiretrace,來追蹤客戶端與服務器的通信,以確實問題出現在什么地方
  7. 用telnet或netcat手工將信息發送到服務器,適合于猜測已經找到了原因而進行試驗時
  8. 將netcat以監聽方式運行,用作服務器以檢查httpclient如何處理應答的。
  9. 利用最新的httpclient試試,bug可能在最新的版本中修復了
  10. 向郵件列表求幫助
  11. 向bugzilla報告bug.
  

10、SSL


  借助Java Secure Socket Extension (JSSE),HttpClient全面支持Secure Sockets Layer (SSL)或IETF Transport Layer Security (TLS)協議上的HTTP。JSSE已經jre1.4及以后的版本中,以前的版本則需要手工安裝設置,具體過程參見java.sun.com/products/jsse/doc/guide/API_users_guide.html#Installation">Sun網站或本學習筆記。
  HttpClient中使用SSL非常簡單,參考下面兩個例子:
HttpClient httpclient = new HttpClient();
GetMethod httpget = new GetMethod("https://www.verisign.com/");
httpclient.executeMethod(httpget);
System.out.println(httpget.getStatusLine().toString());
,如果通過需要授權的代理,則如下:
HttpClient httpclient = new HttpClient();
httpclient.getHostConfiguration().setProxy("myproxyhost", 8080);
httpclient.getState().setProxyCredentials("my-proxy-realm", " myproxyhost",
new UsernamePasswordCredentials("my-proxy-username", "my-proxy-password"));
GetMethod httpget = new GetMethod("https://www.verisign.com/");
httpclient.executeMethod(httpget);
System.out.println(httpget.getStatusLine().toString());

  在HttpClient中定制SSL的步驟如下:
  1. 提供了一個實現了org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory接口的socket factory。這個 socket factory負責打一個到服務器的端口,使用標準的或第三方的SSL函數庫,并進行象連接握手等初始化操作。通常情況下,這個初始化操作在端口被創建時自動進行的。
  2. 實例化一個org.apache.commons.httpclient.protocol.Protocol對象。創建這個實例時,需要一個合法的協議類型(如https),一個定制的socket factory,和一個默認的端中號(如https的443端口).
    Protocol myhttps = new Protocol("https", new MySSLSocketFactory(), 443);
    然后,這個實例可被設置為協議的處理器。
    HttpClient httpclient = new HttpClient();
    httpclient.getHostConfiguration().setHost("www.whatever.com", 443, myhttps);
    GetMethod httpget = new GetMethod("/");
    httpclient.executeMethod(httpget);

  3. 通過調用Protocol.registerProtocol方法,將此定制的實例,注冊為某一特定協議的默認的處理器。由此,可以很方便地定制自己的協議類型(如myhttps)。
    Protocol.registerProtocol("myhttps",
    new Protocol("https", new MySSLSocketFactory(), 9443));
    ...
    HttpClient httpclient = new HttpClient();
    GetMethod httpget = new GetMethod("myhttps://www.whatever.com/");
    httpclient.executeMethod(httpget);
    如果想用自己定制的處理器取代https默認的處理器,只需要將其注冊為"https"即可。
    Protocol.registerProtocol("https",
    new Protocol("https", new MySSLSocketFactory(), 443));
    HttpClient httpclient = new HttpClient();
    GetMethod httpget = new GetMethod("https://www.whatever.com/");
    httpclient.executeMethod(httpget);

  已知的限制和問題
  1. 持續的SSL連接在Sun的低于1.4JVM上不能工作,這是由于JVM的bug造成。
  2. 通過代理訪問服務器時,非搶先認證( Non-preemptive authentication)會失敗,這是由于HttpClient的設計缺陷造成的,以后的版本中會修改。

  遇到問題的處理
  很多問題,特別是在jvm低于1.4時,是由jsse的安裝造成的。
  下面的代碼,可作為最終的檢測手段。
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.Socket; import javax.net.ssl.SSLSocketFactory; public class Test {

public static final String TARGET_HTTPS_SERVER = "www.verisign.com";
public static final int TARGET_HTTPS_PORT = 443;

public static void main(String[] args) throws Exception {

Socket socket = SSLSocketFactory.getDefault().
createSocket(TARGET_HTTPS_SERVER, TARGET_HTTPS_PORT);
try {
Writer out = new OutputStreamWriter(
socket.getOutputStream(), "ISO-8859-1");
out.write("GET / HTTP/1.1\r\n");
out.write("Host: " + TARGET_HTTPS_SERVER + ":" +
TARGET_HTTPS_PORT + "\r\n");
out.write("Agent: SSL-TEST\r\n");
out.write("\r\n");
out.flush();
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream(), "ISO-8859-1"));
String line = null;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
} finally {
socket.close();
}
}
}

  

11、httpclient的多線程處理


  使用多線程的主要目的,是為了實現并行的下載。在httpclient運行的過程中,每個http協議的方法,使用一個HttpConnection實例。由于連接是一種有限的資源,每個連接在某一時刻只能供一個線程和方法使用,所以需要確保在需要時正確地分配連接。HttpClient采用了一種類似jdbc連接池的方法來管理連接,這個管理工作由 MultiThreadedHttpConnectionManager完成。
MultiThreadedHttpConnectionManager connectionManager =
new MultiThreadedHttpConnectionManager();
HttpClient client = new HttpClient(connectionManager);
此是,client可以在多個線程中被用來執行多個方法。每次調用HttpClient.executeMethod() 方法,都會去鏈接管理器申請一個連接實例,申請成功這個鏈接實例被簽出(checkout),隨之在鏈接使用完后必須歸還管理器。管理器支持兩個設置:
maxConnectionsPerHost 每個主機的最大并行鏈接數,默認為2
maxTotalConnections 客戶端總并行鏈接最大數,默認為20

  管理器重新利用鏈接時,采取早歸還者先重用的方式(least recently used approach)。
  由于是使用HttpClient的程序而不是HttpClient本身來讀取應答包的主體,所以HttpClient無法決定什么時間連接不再使用了,這也就要求在讀完應答包的主體后必須手工顯式地調用releaseConnection()來釋放申請的鏈接。
MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
HttpClient client = new HttpClient(connectionManager);
...
// 在某個線程中。
GetMethod get = new GetMethod("http://jakarta.apache.org/");
try {
client.executeMethod(get);
// print response to stdout
System.out.println(get.getResponseBodyAsStream());
} finally {
// be sure the connection is released back to the connection
// manager
get.releaseConnection();
}
對每一個HttpClient.executeMethod須有一個method.releaseConnection()與之匹配.

12、HTTP方法


  HttpClient支持的HTTP方法有8種,下面分述之。

  1、Options

  HTTP方法Options用來向服務器發送請求,希望獲得針對由請求URL(request url)標志的資源在請求/應答的通信過程可以使用的功能選項。通過這個方法,客戶端可以在采取具體行動之前,就可對某一資源決定采取什么動作和/或以及一些必要條件,或者了解服務器提供的功能。這個方法最典型的應用,就是用來獲取服務器支持哪些HTTP方法。
  HttpClient中有一個類叫OptionsMethod,來支持這個HTTP方法,利用這個類的getAllowedMethods方法,就可以很簡單地實現上述的典型應用。
OptionsMethod options = new OptionsMethod("http://jakarta.apache.org");
// 執行方法并做相應的異常處理
...
Enumeration allowedMethods = options.getAllowedMethods();
options.releaseConnection();

  2、Get

   HTTP方法GET用來取回請求URI(request-URI)標志的任何信息(以實體(entity)的形式),"get"這個單詞本意就是”獲取“的意思。如果請求URI指向的一個數據處理過程,那這個過程生成的數據,在應答中以實體的形式被返回,而不是將這個過程的代碼的返回。
  如果HTTP包中含有If-ModifiedSince, If-Unmodified-Since, If-Match, If-None-Match, 或 If-Range等頭字段,則GET也就變成了”條件GET“,即只有滿足上述字段描述的條件的實體才被取回,這樣可以減少一些非必需的網絡傳輸,或者減少為獲取某一資源的多次請求(如第一次檢查,第二次下載)。(一般的瀏覽器,都有一個臨時目錄,用來緩存一些網頁信息,當再次瀏覽某個頁面的時候,只下載那些修改過的內容,以加快瀏覽速度,就是這個道理。至于檢查,則常用比GET更好的方法HEAD來實現。)如果HTTP包中含有Range頭字段,那么請求URI指定的實體中,只有決定范圍條件的那部分才被取回來。(用過多線程下載工具的朋友,可能比較容易理解這一點)
  這個方法的典型應用,用來從web服務器下載文檔。HttpClient定義了一個類叫GetMethod來支持這個方法,用GetMethod類中getResponseBody, getResponseBodyAsStream 或 getResponseBodyAsString函數就可以取到應答包包體中的文檔(如HTML頁面)信息。這這三個函數中,getResponseBodyAsStream通常是最好的方法,主要是因為它可以避免在處理下載的文檔之前緩存所有的下載的數據。
GetMethod get = new GetMethod("http://jakarta.apache.org");
// 執行方法,并處理失敗的請求.
...
InputStream in = get.getResponseBodyAsStream();
// 利用輸入流來處理信息。
get.releaseConnection();

  對GetMethod的最常見的不正確的使用,是沒有將全部的應答主體的數據讀出來。還有,必須注意要手工明確地將鏈接釋放。

  3、Head

  HTTP的Head方法,與Get方法完全一致,唯一的差別是服務器不能在應答包中包含主體(message-body),而且一定不能包含主體。使用這個方法,可以使得客戶無需將資源下載回就可就以得到一些關于它的基本信息。這個方法常用來檢查超鏈的可訪問性以及資源最近有沒有被修改。
  HTTP的head方法最典型的應用,是獲取資源的基本信息。HttpClient定義了HeadMethod類支持這個方法,HeadMethod類與其它*Method類一樣,用 getResponseHeaders()取回頭部信息,而沒有自己的特殊方法。
HeadMethod head = new HeadMethod("http://jakarta.apache.org");
// 執行方法,并處理失敗的請求.
...
// 取回應答包的頭字段信息.
Header[] headers = head.getResponseHeaders(); // 只取回最后修改日期字段的信息.
String lastModified = head.getResponseHeader("last-modified").getValue();


  4、Post

  Post在英文有“派駐”的意思,HTTP方法POST就是要求服務器接受請求包中的實體,并將其作為請求URI的下屬資源。從本質上說,這意味著服務器要保存這個實體信息,而且通常由服務器端的程序進行處理。Post方法的設計意圖,是要以一種統一的方式實現下列功能:
  1. 對已有的資源做評注
  2. 將信息發布到BBS、新聞組、郵件列表,或類似的文章組中
  3. 將一塊數據,提交給數據處理進程
  4. 通過追加操作,來擴展一個數據庫
  這些都操作期待著在服務器端產生一定的“副作用”,如修改了數據庫等。
  HttpClient定義PostMethod類以支持該HTTP方法,在httpclient中,使用post方法有兩個基本的步驟:為請求包準備數據,然后讀取服務器來的應答包的信息。通過調用 setRequestBody()函數,來為請求包提供數據,它可以接收三類參數:輸入流、名值對數組或字符串。至于讀取應答包需要調用 getResponseBody* 那一系列的方法,與GET方法處理應答包的方法相同。
  常見問題是,沒有將全部應答讀取(無論它對程序是否有用),或沒有釋放鏈接資源。

劉錚 2008-02-18 17:49 發表評論
]]>
用command-chain完成的For循環http://www.tkk7.com/liuzheng/articles/176927.html劉錚 劉錚 Tue, 22 Jan 2008 02:24:00 GMThttp://www.tkk7.com/liuzheng/articles/176927.htmlhttp://www.tkk7.com/liuzheng/comments/176927.htmlhttp://www.tkk7.com/liuzheng/articles/176927.html#Feedback0http://www.tkk7.com/liuzheng/comments/commentRss/176927.htmlhttp://www.tkk7.com/liuzheng/services/trackbacks/176927.htmlpackage nl.enovation.commons.command;

import java.util.Collection;

import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;

public class ForeachCommand implements Command {
    private String collectionProperty;
    private String instanceProperty;
    private Command command;
   
    @SuppressWarnings("unchecked")
    public boolean execute(Context context) throws Exception {
        Collection<? extends Object> collection = (Collection<? extends Object>) context.get(collectionProperty);
        if (collection != null) {
            for(Object object : collection) {
                context.put(instanceProperty, object);
                if (PROCESSING_COMPLETE == command.execute(context)) {
                    return PROCESSING_COMPLETE;
                } // else continue
            }
        }
        return CONTINUE_PROCESSING;
    }

    public String getCollectionProperty() {
        return collectionProperty;
    }

    public void setCollectionProperty(String collectionProperty) {
        this.collectionProperty = collectionProperty;
    }

    public String getInstanceProperty() {
        return instanceProperty;
    }

    public void setInstanceProperty(String instanceProperty) {
        this.instanceProperty = instanceProperty;
    }

    public Command getCommand() {
        return command;
    }

    public void setCommand(Command command) {
        this.command = command;
    }

}



劉錚 2008-01-22 10:24 發表評論
]]>
文件處理http://www.tkk7.com/liuzheng/articles/176807.html劉錚 劉錚 Mon, 21 Jan 2008 09:44:00 GMThttp://www.tkk7.com/liuzheng/articles/176807.htmlhttp://www.tkk7.com/liuzheng/comments/176807.htmlhttp://www.tkk7.com/liuzheng/articles/176807.html#Feedback0http://www.tkk7.com/liuzheng/comments/commentRss/176807.htmlhttp://www.tkk7.com/liuzheng/services/trackbacks/176807.htmlpackage nl.enovation.ems.transferlink.client.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

public class FileUtil {


 public static void moveFile(File source, String path) throws IOException {
  if(!source.renameTo(new File(path, source.getName()))){
   throw new IOException(
     "Can't move file "
     + source.getName()
     + " to "
     + path);
  }
 }
 
 public static File moveFile(File source, File path) throws IOException {
  File newFile = new File(path, source.getName());
  if(!source.renameTo(newFile)){
   throw new IOException(
     "Can't move file "
     + source.getName()
     + " to "
     + path);
  }
  return newFile;
 }
 
 public static void moveFile(File source, File path, boolean uniqueFileName) throws IOException {
  String fileName = source.getName();
  File file = new File(path, fileName);
  int count = 1;
  while(true) {
   if(file.exists()) {
    String newFileName = null;
    int idx = fileName.lastIndexOf(".");
    if(idx > 0) {
     newFileName = fileName.substring(0, idx) + "." + count + fileName.substring(idx); 
    } else {
     newFileName = fileName + "." + count;
    }
    file = new File(path, newFileName);
    count++;
   } else {
    break;
   }
  }
  if(!source.renameTo(file)){
   //try {
   // copyFile(source, file);
   // deleteFile(source);
   //}
   //catch (IOException e)
   //{
    throw new IOException(
      "Can't move file "
      + source.getName() //+ " (" + source.getAbsolutePath() + ")"
      + " to "
      + path);
     //+"("+e.getMessage()+")");
     // + " (" + file.getAbsolutePath() + ")");
   //}
  }
 }
 
 public static void moveFiles(File sourceDir, File destDir) throws IOException {
  java.io.File[] list=sourceDir.listFiles();
  for (int i = 0; i < list.length; i++) {
   if (!list[i].isDirectory()) {
    if(!list[i].renameTo(new File(destDir, list[i].getName()))) {
     throw new IOException(
       "Can't move file "
       + list[i].getName()
       + " to "
       + destDir.getAbsolutePath());
    }
   }
  }
 }
 
 /**
  * Copy a File (source) to a File (destination)
  * @param source
  * @param dest
  * @throws IOException
  */
 public static void copyFile(File source, File dest) throws IOException {
      FileChannel in = null, out = null;
      try {         
           in = new FileInputStream(source).getChannel();
           out = new FileOutputStream(dest).getChannel();

           long size = in.size();
           MappedByteBuffer buf = in.map(FileChannel.MapMode.READ_ONLY, 0, size);
           out.write(buf);

      } finally {
           if (in != null)     
            in.close();
           if (out != null)    
            out.close();
      }
 }
 
 /**
  * Write an input stream to a file.
  * @param source
  * @param dest
  * @param size
  * @throws IOException
  */
 public static int writeFile(InputStream source, File dest, long buffer) throws IOException {
  int bytesWrote = 0;
  FileChannel out = null;
  ReadableByteChannel in = null;
  ByteBuffer data = ByteBuffer.allocate((int) buffer);
  try {
   in = Channels.newChannel(source);
   out = new FileOutputStream(dest).getChannel();
   
   int read = in.read(data);
   while(read > 0) {
    bytesWrote += read;
    data.flip();
    out.write(data);
    data.clear();
    read = in.read(data);
   }

  } finally {
   if(out != null)
    out.close();
   if(in != null)
    in.close();
  }
  return bytesWrote;
 }
 
 public static void delete(java.io.File path) throws IOException {
  if (path.isDirectory()) {
   deleteDirectory(path);
  } else {
   deleteFile(path);
  }
 }
 
 protected static void deleteDirectory(java.io.File path) throws IOException {
  java.io.File[] list = path.listFiles();
  for (int i = 0; i < list.length; i++) {
   if (list[i].isDirectory()) {
    deleteDirectory(list[i]);
    if (!list[i].delete()) {
     throw new IOException("Can't delete directory '"+list[i]+"'.");
    }
   } else {
    deleteFile(list[i]);
   }
  }
 }
 
 protected static void deleteFile(java.io.File path) throws IOException {
  if (!path.delete()) {
   throw new IOException("Can't delete file '"+path+"'.");
  }
 }
 
 // TODO optimize ... read and write with buffers?!
 //@SuppressWarnings("unchecked")
 public static Collection splitFile(File file, int splitlen, File tmpDir) throws IOException
 {
  long leng = 0;
  int count = 1, read = 0;
  byte [] buffer = new byte[8196];
  
  String fileName  = file.getName();
  Collection files = new ArrayList();  
  RandomAccessFile infile = new RandomAccessFile(file, "r");
  read = infile.read(buffer);
  while(read != -1)
  {
   file = new File(tmpDir, fileName + ".lms.sp" + count);
   RandomAccessFile outfile = new RandomAccessFile(file, "rw");
   while( read != -1 && leng < splitlen)
   {
    outfile.write(buffer, 0, read);
    leng += read;
    read = infile.read(buffer);
   }
   leng = 0;
   outfile.close();
   file.deleteOnExit();
   files.add(file);
   count++;
  }
  return files;
 }
 
 public static File joinFile(String fileName, File tmpDir) throws IOException
 {
  int count = 1, read = 0;
  byte [] buffer = new byte[8196];
  
  File destFile = new File(tmpDir, fileName);
  RandomAccessFile outfile = new RandomAccessFile(destFile, "rw");
  while(true)
  {
   File readFile = new File(tmpDir, fileName + ".lms.sp" + count);
   if (readFile.exists())
   {
    RandomAccessFile infile = new RandomAccessFile(readFile, "r");
    read = infile.read(buffer);
    while(read != -1)
    {
     outfile.write(buffer, 0, read);
     read = infile.read(buffer);
    }
    infile.close();
    readFile.delete();
    count++;
   }
   else
   {
    break;
   }
  }
  outfile.close();
  return destFile;
 }
 
 public static boolean isComplete(String fileName, File dir, int parts) {
  int counter = 1;
  int matched = 0;
  File[] list = dir.listFiles();
  for (int i = 0; i < list.length; i++) {
   if (!list[i].isDirectory()) {
    String f = list[i].getName();
    String f2 = fileName + ".lms.sp";
    if(f.startsWith(f2)) {
     matched++;
    }
    counter++;
   }
  }
  if(matched == parts) {
   return true;
  } else {
   return false;
  }
  
 }
 
 public static File zipFile(File file, File dir) throws IOException {
  WritableByteChannel out = null;
  ReadableByteChannel in = null;
  FileInputStream source = new FileInputStream(file);
  File dest = new File(dir, file.getName() + ".zip");
  ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(dest));
  dest.deleteOnExit();
  int buffer = 4096;
  ByteBuffer data = ByteBuffer.allocate(buffer);
  try {
   zos.putNextEntry(new ZipEntry(file.getName()));

   in = Channels.newChannel(source);
   out = Channels.newChannel(zos);
   
   int read = in.read(data);
   while(read > 0) {
    data.flip();
    out.write(data);
    data.clear();
    read = in.read(data);
   }
   
   zos.closeEntry();
  } finally {
   if(zos != null) {
    zos.finish();
   }
   if(out != null)
    out.close();
   if(in != null)
    in.close();
  }
  return dest;
 }
 
 public static File unzipFile(File file, File dir) throws IOException {
  WritableByteChannel out = null;
  ReadableByteChannel in = null;
  ZipEntry entry = null;
  ZipFile zipFile = null;
  int buffer = 4096;
  ByteBuffer data = ByteBuffer.allocate(buffer);
  File unzippedFile = null;
  try {
   zipFile = new ZipFile(file, ZipFile.OPEN_READ);
   Enumeration zipFileEntries = zipFile.entries();
      
   // Process each entry
   if (zipFileEntries.hasMoreElements()) {
    // grab a zip file entry
    entry = (ZipEntry) zipFileEntries.nextElement();
    String currentEntry = entry.getName();
    InputStream input = zipFile.getInputStream(entry);
    unzippedFile = new File(dir, currentEntry);
    FileOutputStream output = new FileOutputStream(unzippedFile);
    
    in = Channels.newChannel(input);
    out = Channels.newChannel(output);

    int read = in.read(data);
    while(read > 0) {
     data.flip();
     out.write(data);
     data.clear();
     read = in.read(data);
    }
   }
  } finally {
   if(file != null)
    file.delete();
   if(zipFile != null)
    zipFile.close();
   if(out != null)
    out.close();
   if(in != null)
    in.close();
  }
  return unzippedFile;
 }
}


不過最好使用Apache Common 的IO組件。
里面有個FileUtils,提供了很多很好的方法。
可以參考:
 

http://commons.apache.org/io/apidocs/index.html



劉錚 2008-01-21 17:44 發表評論
]]>
有關JAXB對xsd中的時間數據類型的處理http://www.tkk7.com/liuzheng/articles/173389.html劉錚 劉錚 Mon, 07 Jan 2008 09:09:00 GMThttp://www.tkk7.com/liuzheng/articles/173389.htmlhttp://www.tkk7.com/liuzheng/comments/173389.htmlhttp://www.tkk7.com/liuzheng/articles/173389.html#Feedback0http://www.tkk7.com/liuzheng/comments/commentRss/173389.htmlhttp://www.tkk7.com/liuzheng/services/trackbacks/173389.html使用xjc 將xsd中的時間數據:time datetime 等等轉化為XMLGregorianCalendar的類
處理XMLGregorianCalendar可以使用DatatypeFactory的工廠類進行創建,其中有個newXMLGregorianCalendar(GregorianCalendar cal)
          根據 GregorianCalendar 創建 XMLGregorianCalendar
newXMLGregorianCalendar(int year, int month, int day, int hour, int minute, int second, int millisecond, int timezone)
          java.util.GregorianCalendar 實例需要轉換為 XMLGregorianCalendar 實例的值空間的構造方法。



劉錚 2008-01-07 17:09 發表評論
]]>
標準的讀取文件的方法,讀取整個文件http://www.tkk7.com/liuzheng/articles/172271.html劉錚 劉錚 Wed, 02 Jan 2008 10:02:00 GMThttp://www.tkk7.com/liuzheng/articles/172271.htmlhttp://www.tkk7.com/liuzheng/comments/172271.htmlhttp://www.tkk7.com/liuzheng/articles/172271.html#Feedback0http://www.tkk7.com/liuzheng/comments/commentRss/172271.htmlhttp://www.tkk7.com/liuzheng/services/trackbacks/172271.html         File f=new File("d:"+File.separator+"1.txt");
        BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream(f)));
        StringBuffer whole=new StringBuffer();
        String line=new String();
        for (line=br.readLine(); line!=null; line=br.readLine()) {
            whole.append(line);
           
        }
        System.out.println(whole.toString());
    }
使用readLine()對整個文件或者Read流進行字符掃描


public void test1() throws IOException{
        File f=new File("d:"+File.separator+"1.txt");

        BufferedInputStream br=new BufferedInputStream(new FileInputStream(f));
        StringBuffer whole=new StringBuffer();
        int line;
        for (line=br.read(); line!=-1; line=br.read()) {
            whole.append(line);
           
        }
        System.out.println(whole.toString());
    }

使用read()對整個文件或者Read流進行字節的掃描



劉錚 2008-01-02 18:02 發表評論
]]>
java多線程中sleep()和join()的區別 http://www.tkk7.com/liuzheng/articles/167243.html劉錚 劉錚 Wed, 12 Dec 2007 07:21:00 GMThttp://www.tkk7.com/liuzheng/articles/167243.htmlhttp://www.tkk7.com/liuzheng/comments/167243.htmlhttp://www.tkk7.com/liuzheng/articles/167243.html#Feedback0http://www.tkk7.com/liuzheng/comments/commentRss/167243.htmlhttp://www.tkk7.com/liuzheng/services/trackbacks/167243.htmlIf the Sleep method is called with System.Threading.Timeout.Infinite passed in as the parameter, the thread will remain in the WaitSleepJoin state until a different thread wakes it by using the Interrupt method. Thread.Sleep(System.Threading.Timeout.Infinite)

One reason you might want to do this is if a thread determines that it is in a state where the best thing it can do is nothing. This may be an alternative to ending the thread by using the Abort method, or simply exiting the thread’s method. Once a thread ends, there is no way to restart it. However, if a thread calls the Sleep method and passes in Infinite for the timeout value, it is possible to exit that state at a later time. This concept is similar to calling Join.

When Join is called and no parameter is passed in, the current thread will wait indefinitely for the thread to end. When Join is called with a timeout value, the Join method will block for at most that period of time and then return a value indicating if the thread of interest ended. A key difference is that Join is called on a different thread while Sleep is called on the current thread. Join also causes the current thread to pause for a period of time, but with the idea that it is waiting for some other thread to terminate. At the point the thread being joined terminates, the Join method returns.



劉錚 2007-12-12 15:21 發表評論
]]>
java多線程的中止http://www.tkk7.com/liuzheng/articles/166419.html劉錚 劉錚 Sun, 09 Dec 2007 03:35:00 GMThttp://www.tkk7.com/liuzheng/articles/166419.htmlhttp://www.tkk7.com/liuzheng/comments/166419.htmlhttp://www.tkk7.com/liuzheng/articles/166419.html#Feedback0http://www.tkk7.com/liuzheng/comments/commentRss/166419.htmlhttp://www.tkk7.com/liuzheng/services/trackbacks/166419.html
在run方法中加入
public void run(){
    while(flag){
    ......
    }
}
再加入
public void shutDown(){
    flag=false;
}

以后使用shutDown進行關閉線程,而不用廢棄了的stop方法,因為stop方法太粗暴了,立馬停止線程的話,對已經打開的資源不能進行處理。

劉錚 2007-12-09 11:35 發表評論
]]>
java 5 中的enum的深入學習心得http://www.tkk7.com/liuzheng/articles/165741.html劉錚 劉錚 Thu, 06 Dec 2007 02:55:00 GMThttp://www.tkk7.com/liuzheng/articles/165741.htmlhttp://www.tkk7.com/liuzheng/comments/165741.htmlhttp://www.tkk7.com/liuzheng/articles/165741.html#Feedback0http://www.tkk7.com/liuzheng/comments/commentRss/165741.htmlhttp://www.tkk7.com/liuzheng/services/trackbacks/165741.htmlThe full enum syntax actually provides quite a bit more power and flexibility:
  • You can define your own fields, methods, and constructors for the enumerated type.

  • If you define one or more constructors, you can invoke a constructor for each enumerated value by following the value name with constructor arguments in parentheses.

  • Although an enum may not extend anything, it may implement one or more interfaces.

  • Most esoterically, individual enumerated values can have their own class bodies that override methods defined by the type.

Rather than formally specifying the syntax for each of these advanced enum declarations, we'll demonstrate the syntax in the examples that follow.

 
for example:
 
public enum Prefix {
// These are the values of this enumerated type.
// Each one is followed by constructor arguments in parentheses.
// The values are separated from each other by commas, and the
// list of values is terminated with a semicolon to separate it from
// the class body that follows.
MILLI("m",    .001),
CENTI("c",    .01),
DECI("d",     .1),
DECA("D",   10.0),
HECTA("h", 100.0),
KILO("k", 1000.0);  // Note semicolon
// This is the constructor invoked for each value above.
Prefix(String abbrev, double multiplier) {
this.abbrev = abbrev;
this.multiplier = multiplier;
}
// These are the private fields set by the constructor
private String abbrev;
private double multiplier;
// These are accessor methods for the fields.  They are instance methods
// of each value of the enumerated type.
public String abbrev() { return abbrev; }
public double multiplier() { return multiplier; }
}


劉錚 2007-12-06 10:55 發表評論
]]>
java 5中枚舉類型學習http://www.tkk7.com/liuzheng/articles/165597.html劉錚 劉錚 Wed, 05 Dec 2007 09:17:00 GMThttp://www.tkk7.com/liuzheng/articles/165597.htmlhttp://www.tkk7.com/liuzheng/comments/165597.htmlhttp://www.tkk7.com/liuzheng/articles/165597.html#Feedback0http://www.tkk7.com/liuzheng/comments/commentRss/165597.htmlhttp://www.tkk7.com/liuzheng/services/trackbacks/165597.html Tiger 中的一個重要新特性是枚舉構造,它是一種新的類型,允許用常量來表示特定的數據 片斷,而且全部都以類型安全的形式來表示。Tiger 專家、developerWorks 的多產作者 Brett McLaughlin將解釋枚舉的定義,介紹如何在應用程序中運用枚舉,以及它為什么能夠讓您拋棄所有舊的
public static final 代碼。

  您已經知道,Java 代碼的兩個基本的構造塊是類 和接口。現在 Tiger 又引入了枚舉,一般簡稱它為 enum。這個新類型允許您表示特定的數據點,這些數據點只接受分配時預先定 義的值集合。
 當然,熟練的程序員可以用靜態常量實現這項功能,如清單 1 所示:



清單 1. public static final 的常量

public class OldGrade {

 public static final int A = 1;
 public static final int B = 2;
 public static final int C = 3;
 public static final int D = 4;
 public static final int F = 5;
 public static final int INCOMPLETE = 6;
}



說明:我要感謝 O"Reilly 媒體公司,該公司允許在本文中使用我撰寫的 Java 1.5 Tiger:
A Developer"s Notebook 一書中“枚舉”這一章中的代碼示例(請參閱參考資料)。

然后您就可以讓類接受像 OldGrade.B 這樣的常量,但是在這樣做的時候,請記住這類常量
是 Java 中 int 類型的常量,這意味著該方法可以接受任何 int 類型的值,即使它和
OldGrade 中定的所有級別都不對應。因此,您需要檢測上界和下界,在出現無效值的時候,可能還
要包含一個 IllegalArgumentException。而且,如果后來又添加另外一個級別(例如
OldGrade.WITHDREW_PASSING),那么必須改變所有代碼中的上界,才能接受這個新值。

換句話說,在使用這類帶有整型常量的類時,該解決方案也許可行,但并不是非常有效。幸
運的是,枚舉提供了更好的方法。

定義枚舉
清單 2 使用了一個可以提供與清單 1 相似的功能的枚舉:

清單 2. 簡單的枚舉類型

package com.oreilly.tiger.ch03;

public enum Grade {
 A, B, C, D, F, INCOMPLETE
};



在這里,我使用了新的關鍵字 enum,為 enum 提供了一個名稱,并指定了允許的值。然后
,Grade 就變成了一個枚舉類型,您可以按清單 3 所示的方法使用它:

清單 3. 使用枚舉類型

package com.oreilly.tiger.ch03;

public class Student {

 private String firstName;
 private String lastName;
 private Grade grade;

 public Student(String firstName, String lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
 }

 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }

 public String getFirstName() {
  return firstName;
 }

 public void setLastName(String lastName) {
  this.lastName = lastName;
 }

 public String getLastName() {
  return lastName;
 }

 public String getFullName() {
  return new StringBuffer(firstName)
      .append(" ")
      .append(lastName)
      .toString();
 }

 public void assignGrade(Grade grade) {
  this.grade = grade;
 }

 public Grade getGrade() {
  return grade;
 }
}



用以前定義過的類型建立一個新的枚舉(grade)之后,您就可以像使用其他成員變量一樣
使用它了。當然,枚舉只能分配枚舉值中的一個(例如,A、C 或 INCOMPLETE)。而且,在
assignGrade() 中是沒有進行錯誤檢測的代碼,也沒有考慮邊界情況,請注意這是如何做到


使用枚舉值
迄今為止,您所看到的示例都相當簡單,但是枚舉類型提供的東西遠不止這些。您可以逐個
遍歷枚舉值,也可以在 switch 語句中使用枚舉值,枚舉是非常有價值的。

遍歷枚舉值
下面我們用一個示例顯示如何遍歷枚舉類型的值。清單 4 所示的這項技術,適用于調試、
快速打印任務以及把枚舉加載到集合(我很快將談到)中的工具:

清單 4. 遍歷枚舉值

public void listGradeValues(PrintStream out) throws IOException {
 for (Grade g : Grade.values()) {
  out.println("Allowed value: "" + g + """);
 }
}



運行這段代碼,將得到清單 5 所示的輸出:

清單 5. 迭代操作的輸出

Allowed Value: "A"
Allowed Value: "B"
Allowed Value: "C"
Allowed Value: "D"
Allowed Value: "F"
Allowed Value: "INCOMPLETE"



這里有許多東西。首先,我使用了 Tiger 的新的 for/in 循環(也叫作 foreach 或 增強
的 for)。另外,您可以看到 values() 方法返回了一個由獨立的 Grade 實例構成的數組
,每個數組都有一個枚舉類型的值。換句話說,values() 的返回值是 Grade[]。

在枚舉間切換
能夠在枚舉的值之間移動很好,但是更重要的是根據枚舉的值進行決策。您當然可以寫一堆
if (grade.equals(Grade.A)) 類型的語句,但那是在浪費時間。Tiger 能夠很方便地把枚
舉支持添加到過去的好東西 switch 語句上,所以它很容易使用,而且適合您已知的內容。
清單 6
向將展示如何解決這個難題:

清單 6. 在枚舉之間切換

public void testSwitchStatement(PrintStream out) throws IOException {
 StringBuffer outputText = new StringBuffer(student1.getFullName());

 switch (student1.getGrade()) {
  case A:
   outputText.append(" excelled with a grade of A");
   break;
  case B: // fall through to C
  case C:
   outputText.append(" passed with a grade of ")
        .append(student1.getGrade().toString());
   break;
  case D: // fall through to F
  case F:
   outputText.append(" failed with a grade of ")
        .append(student1.getGrade().toString());
   break;
  case INCOMPLETE:
   outputText.append(" did not complete the class.");
   break;
 }

 out.println(outputText.toString());
}



在這里,枚舉值被傳遞到 switch 語句中(請記住,getGrade() 是作為 Grade 的實例返回
的),而每個 case 子句將處理一個特定的值。該值在提供時沒有枚舉前綴,這意味著不用
將代碼寫成 case Grade.A,只需將其寫成 case A 即可。如果您不這么做,編譯器不會接
受有前綴的值。

現在,您應該已經了解使用 switch 語句時的基本語法,但是還有一些事情您需要知道。
在使用 switch 之前進行計劃
正如您所期待的,在使用枚舉和 switch 時,您可以使用 default 語句。清單 7 顯示了這
個用法:

清單 7. 添加一個 default 塊

public void testSwitchStatement(PrintStream out) throws IOException {
 StringBuffer outputText = new StringBuffer(student1.getFullName());

 switch (student1.getGrade()) {
  case A:
   outputText.append(" excelled with a grade of A");
   break;
  case B: // fall through to C
  case C:
   outputText.append(" passed with a grade of ")
        .append(student1.getGrade().toString());
   break;
  case D: // fall through to F
  case F:
   outputText.append(" failed with a grade of ")
        .append(student1.getGrade().toString());
   break;
  case INCOMPLETE:
   outputText.append(" did not complete the class.");
   break;
  default:
   outputText.append(" has a grade of ")
        .append(student1.getGrade().toString());
   break;
 }

 out.println(outputText.toString());
}



研究以上代碼可以看出,任何沒有被 case 語句處理的枚舉值都會被 default 語句處理。
這項技術您應當堅持采用。原因是:假設 Grade 枚舉被您的小組中其他程序員修改(而且
他忘記告訴您這件事)成清單 8 所示的版本:

清單 8. 給 Grade 枚舉添加一個值

package com.oreilly.tiger.ch03;

public enum Grade {
 A, B, C, D, F, INCOMPLETE,
 WITHDREW_PASSING, WITHDREW_FAILING
};



現在,如果使用清單 6 的代碼所示的新版 Grade,那么這兩個新值會被忽略。更糟的是,
您甚至看不到錯誤!在這種情況下,存在某種能夠通用的 default 語句是非常重要的。清
單 7 無法很好地處理這些值,但是它會提示您還有其他值,您需要處理這些值。一旦完成處理,
您就會有一個繼續運行的應用程序,而且它不會忽略這些值,甚至還會指導您下一步的動作
。所以這是一個良好的編碼習慣。

枚舉和集合
您所熟悉的使用 public static final 方法進行編碼的那些東西,可能已經轉而采用枚舉
的值作為映射的鍵。如果您不知道其中的含義,請參見清單 9,它是一個公共錯誤信息的示
例,在使用 Ant 的 build 文件時,可能會彈出這樣的消息,如下所示:

清單 9. Ant 狀態碼

package com.oreilly.tiger.ch03;

public enum AntStatus {
 INITIALIZING,
 COMPILING,
 COPYING,
 JARRING,
 ZIPPING,
 DONE,
 ERROR
}



為每個狀態碼分配一些人們能讀懂的錯誤信息,從而允許人們在 Ant 提供某個代碼時查找
合適的錯誤信息,將這些信息顯示在控制臺上。這是映射(Map)的一個絕好用例,在這里
,每個映射(Map)的鍵都是一個枚舉值,而每個值都是鍵的錯誤信息。清單 10 演示了該
映射的工作方式:

清單 10. 枚舉的映射(Map)

public void testEnumMap(PrintStream out) throws IOException {
 // Create a map with the key and a String message
 EnumMap<AntStatus, String> antMessages =
  new EnumMap<AntStatus, String>(AntStatus.class);

 // Initialize the map
 antMessages.put(AntStatus.INITIALIZING, "Initializing Ant...");
 antMessages.put(AntStatus.COMPILING,  "Compiling Java classes...");
 antMessages.put(AntStatus.COPYING,   "Copying files...");
 antMessages.put(AntStatus.JARRING,   "JARring up files...");
 antMessages.put(AntStatus.ZIPPING,   "ZIPping up files...");
 antMessages.put(AntStatus.DONE,     "Build complete.");
 antMessages.put(AntStatus.ERROR,    "Error occurred.");

 // Iterate and print messages
 for (AntStatus status : AntStatus.values() ) {
  out.println("For status " + status + ", message is: " +
        antMessages.get(status));
 }
}



該代碼使用了泛型(generics)(請參閱參考資料)和新的 EnumMap 構造來建立新映射。
而且,枚舉值是通過其 Class 對象提供的,同時提供的還有映射值的類型(在該例中,它
只是一個簡單的字符串)。該方法的輸出如清單 11 所示:

枚舉的 Class 對象
您可能已經注意到,清單 10 中的示例代碼實際上表明 Tiger 把枚舉當作類,這可以從
AntStatus 的 Class 對象那里得到證明,該對象不僅可用,而且正被實際使用。這是真的
。歸根到底, Tiger 還是把枚舉看成是特殊的類類型。有關枚舉的具體實現細節,請參閱
Java 5.0 Tiger: A Developer"s Notebook 的第三章(請參閱參考資料)。


清單 11. 清單 10 的輸出

[echo] Running AntStatusTester...
[java] For status INITIALIZING, message is: Initializing Ant...
[java] For status COMPILING, message is: Compiling Java classes...
[java] For status COPYING, message is: Copying files...
[java] For status JARRING, message is: JARring up files...
[java] For status ZIPPING, message is: ZIPping up files...
[java] For status DONE, message is: Build complete.
[java] For status ERROR, message is: Error occurred.



更進一步枚舉也可以與集合結合使用,而且非常像新的 EnumMap 構造,Tiger 提供了一套新的
EnumSet實現,允許您使用位操作符。另外,可以為枚舉添加方法,用它們實現接口,定義叫作特定
值的類的實體,在該實體中,特定的代碼被附加到枚舉的具體值上。這些特性超出了本文的
范圍,但是在其他地方,有詳細介紹它們的文檔(請參閱參考資料)。

使用枚舉,但是不要濫用
學習任何新版語言的一個危險就是瘋狂使用新的語法結構。如果這樣做,那么您的代碼就會
突然之間有 80% 是泛型、標注和枚舉。所以,應當只在適合使用枚舉的地方才使用它。那
么,枚舉在什么地方適用呢?一條普遍規則是,任何使用常量的地方,例如目前用 switch
代碼切換常量的地方。如果只有單獨一個值(例如,鞋的最大尺寸,或者籠子中能裝猴子的
最大數目),則還是把這個任務留給常量吧。但是,如果定義了一組值,而這些值中的任何
一個都可以用于特定的數據類型,那么將枚舉用在這個地方最適合不過。


劉錚 2007-12-05 17:17 發表評論
]]>
利用jakarta Commons組件收發郵件http://www.tkk7.com/liuzheng/articles/162544.html劉錚 劉錚 Fri, 23 Nov 2007 02:13:00 GMThttp://www.tkk7.com/liuzheng/articles/162544.htmlhttp://www.tkk7.com/liuzheng/comments/162544.htmlhttp://www.tkk7.com/liuzheng/articles/162544.html#Feedback0http://www.tkk7.com/liuzheng/comments/commentRss/162544.htmlhttp://www.tkk7.com/liuzheng/services/trackbacks/162544.html

Telnet to port 25 of a SMTP server, and you will see the server respond with a numeric code (220). SMTPReply.isPositiveCompletion( ) returns true if the response code of the previously executed command is between 200 and 299; the value of the initial response code, 220, is equal to the public static variable SMTPReply.SERVICE_READY. The following example uses getReplyCode( ) and SMTPReply.isPositiveCompletion() to test the connection to the SMTP server:

import org.apache.commons.net.smtp.SMTP;
import org.apache.commons.net.smtp.SMTPClient;
import org.apache.commons.net.smtp.SMTPReply;
SMTPClient client = new SMTPClient( );
client.connect("www.discursive.com");
int response = client.getReplyCode( );
if( SMTPReply.isPositiveCompletion( response ) ) {
// Set the sender and the recipients
client.setSender( "tobrien@discursive.com" );
client.addRecipient( "president@whitehouse.gov" );
client.addRecipient( "vicepresident@whitehouse.gov" );
// Supply the message via a Writer
Writer message = client.sendMessageData( );
message.write( "Spend more money on energy research.  Thanks." );
message.close( );
// Send the message and print a confirmation
boolean success = client.completePendingCommand( );
if( success ) {
System.out.println( "Message sent" );
}
} else {
System.out.println( "Error communicating with SMTP server" );
}
client.disconnect( );

Instead of sendSimpleMessage( ), the previous example sets a sender address and two recipient addresses using setSender( ) and addRecipient(). The message body is then written to a Writer returned by sendMessageData(). When the Writer is closed, the message is sent by calling completePendingCommand(). completePendingCommand( ) returns true if the message has been queued for delivery.

收郵件:

Use Commons Net POP3Client to check a POP3 mailbox for incoming mail. The following example connects to the POP3 server www.discursive.com, logs in as tobrien@discursive.com, and prints each message in the mailbox:

import org.apache.commons.io.CopyUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.net.pop3.POP3Client;
import org.apache.commons.net.pop3.POP3MessageInfo;
POP3Client client = new POP3Client( );
client.connect("www.discursive.com");
client.login("tobrien@discursive.com", "secretpassword");
POP3MessageInfo[] messages = client.listMessages( );
for (int i = 0; i < messages.length; i++) {
int messageNum = messages[i].number;
System.out.println( "************* Message number: " + messageNum );
Reader reader = client.retrieveMessage( messageNum );
System.out.println( "Message:\n" + IOUtils.toString( reader ) );
IOUtils.closeQuietly( reader );
}
client.logout( );
client.disconnect( );


劉錚 2007-11-23 10:13 發表評論
]]>
使用J2SE API讀取Properties文件的六種方法http://www.tkk7.com/liuzheng/articles/157562.html劉錚 劉錚 Thu, 01 Nov 2007 09:54:00 GMThttp://www.tkk7.com/liuzheng/articles/157562.htmlhttp://www.tkk7.com/liuzheng/comments/157562.htmlhttp://www.tkk7.com/liuzheng/articles/157562.html#Feedback0http://www.tkk7.com/liuzheng/comments/commentRss/157562.htmlhttp://www.tkk7.com/liuzheng/services/trackbacks/157562.html   
  1.使用java.util.Properties類的load()方法
  
  示例: InputStream in = lnew BufferedInputStream(new FileInputStream(name));
  Properties p = new Properties();
  p.load(in);
  
  2.使用java.util.ResourceBundle類的getBundle()方法
  
  示例: ResourceBundle rb = ResourceBundle.getBundle(name, Locale.getDefault());
  
  3.使用java.util.PropertyResourceBundle類的構造函數
  
  示例: InputStream in = new BufferedInputStream(new FileInputStream(name));
  ResourceBundle rb = new PropertyResourceBundle(in);
  
  4.使用class變量的getResourceAsStream()方法
  
  示例: InputStream in = JProperties.class.getResourceAsStream(name);
  Properties p = new Properties();
  p.load(in);
  
  5.使用class.getClassLoader()所得到的java.lang.ClassLoader的getResourceAsStream()方法
  
  示例: InputStream in = JProperties.class.getClassLoader().getResourceAsStream(name); bitsCN.nET中國網管博客
  Properties p = new Properties();
  p.load(in);
  
  6.使用java.lang.ClassLoader類的getSystemResourceAsStream()靜態方法
  
  示例: InputStream in = ClassLoader.getSystemResourceAsStream(name);
  Properties p = new Properties();
  p.load(in);
  
  補充
  
  Servlet中可以使用javax.servlet.ServletContext的getResourceAsStream()方法
  
  示例:InputStream in = context.getResourceAsStream(path);
  Properties p = new Properties();
  p.load(in);
  
  完整的示例,可以參考附件文件
  
  如何上傳文件,誰知道請告訴以下。 只好把source都貼上來了
  
  JProperties.java文件
  
  /**
  ** This program is free software.
  **
  ** You may redistribute it and/or modify it under the terms of the GNU
  ** General Public License as published by the Free Software Foundation.
  ** Version 2 of the license should be included with this distribution in
  ** the file LICENSE, as well as License.html. If the license is not 09hr.com網管求職
  ** included with this distribution, you may find a copy at the FSF web
  ** site at 'www.gnu.org' or 'www.fsf.org', or you may write to the
  ** Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139 USA.
  **
  ** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
  ** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
  ** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
  ** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
  ** REDISTRIBUTION OF THIS SOFTWARE.
  **/
  
  package com.kindani;
  
  //import javax.servlet.ServletContext;
  import java.util.*;
  import java.io.InputStream;
  import java.io.IOException;
  import java.io.BufferedInputStream;
  import java.io.FileInputStream;
  
  /**
  * 使用J2SE API読取Properties文件的六種方法
  * User: SYNFORM
  * Date: 2005/07/12
  * Time: 18:40:55
  * To change this template use File | Settings | File Templates.

bitsCN_com


  */
  public class JProperties {
  
  public final static int BY_PROPERTIES = 1;
  public final static int BY_RESOURCEBUNDLE = 2;
  public final static int BY_PROPERTYRESOURCEBUNDLE = 3;
  public final static int BY_CLASS = 4;
  public final static int BY_CLASSLOADER = 5;
  public final static int BY_SYSTEM_CLASSLOADER = 6;
  
  public final static Properties loadProperties(final String name, final int type) throws IOException {
  Properties p = new Properties();
  InputStream in = null;
  if (type == BY_PROPERTIES) {
  in = new BufferedInputStream(new FileInputStream(name));
  assert (in != null);
  p.load(in);
  } else if (type == BY_RESOURCEBUNDLE) {
  ResourceBundle rb = ResourceBundle.getBundle(name, Locale.getDefault());
  assert (rb != null);
  p = new ResourceBundleAdapter(rb);
  } else if (type == BY_PROPERTYRESOURCEBUNDLE) {
  in = new BufferedInputStream(new FileInputStream(name));
  assert (in != null);

DL.bitsCN.com網管軟件下載


  ResourceBundle rb = new PropertyResourceBundle(in);
  p = new ResourceBundleAdapter(rb);
  } else if (type == BY_CLASS) {
  assert (JProperties.class.equals(new JProperties().getClass()));
  in = JProperties.class.getResourceAsStream(name);
  assert (in != null);
  p.load(in);
  //    return new JProperties().getClass().getResourceAsStream(name);
  } else if (type == BY_CLASSLOADER) {
  assert (JProperties.class.getClassLoader().equals(new JProperties().getClass().getClassLoader()));
  in = JProperties.class.getClassLoader().getResourceAsStream(name);
  assert (in != null);
  p.load(in);
  //     return new JProperties().getClass().getClassLoader().getResourceAsStream(name);
  } else if (type == BY_SYSTEM_CLASSLOADER) {
  in = ClassLoader.getSystemResourceAsStream(name);
  assert (in != null);
  p.load(in);
  }
  
  if (in != null) {
  in.close();
  }
  return p;
  
  }
   09hr.com網管求職
  // ---------------------------------------------- servlet used
  /*
  public static Properties loadProperties(ServletContext context, String path) throws IOException {
  assert (context != null);
  InputStream in = context.getResourceAsStream(path);
  assert (in != null);
  Properties p = new Properties();
  p.load(in);
  in.close();
  return p;
  }
  */
  
  // ---------------------------------------------- support class
  
  /**
  * ResourceBundle Adapter class.
  */
  public static class ResourceBundleAdapter extends Properties {
  public ResourceBundleAdapter(ResourceBundle rb) {
  assert (rb instanceof java.util.PropertyResourceBundle);
  this.rb = rb;
  java.util.Enumeration e = rb.getKeys();
  while (e.hasMoreElements()) {
  Object o = e.nextElement();
  this.put(o, rb.getObject((String) o));
  }
  }
  
  private ResourceBundle rb = null;
  
  public ResourceBundle getBundle(String baseName) {

BBS.bitsCN.com國內最早的網管論壇


  return ResourceBundle.getBundle(baseName);
  }
  
  public ResourceBundle getBundle(String baseName, Locale locale) {
  return ResourceBundle.getBundle(baseName, locale);
  }
  
  public ResourceBundle getBundle(String baseName, Locale locale, ClassLoader loader) {
  return ResourceBundle.getBundle(baseName, locale, loader);
  }
  
  public Enumeration<String> getKeys() {
  return rb.getKeys();
  }
  
  public Locale getLocale() {
  return rb.getLocale();
  }
  
  public Object getObject(String key) {
  return rb.getObject(key);
  }
  
  public String getString(String key) {
  return rb.getString(key);
  }
  
  public String[] getStringArray(String key) {
  return rb.getStringArray(key);
  }
  
  protected Object handleGetObject(String key) {
  return ((PropertyResourceBundle) rb).handleGetObject(key);
  }
  
  }
  
  }
Play.bitsCN.com小游戲

  
  
  JPropertiesTest.java文件
  
  /**
  ** This program is free software.
  **
  ** You may redistribute it and/or modify it under the terms of the GNU
  ** General Public License as published by the Free Software Foundation.
  ** Version 2 of the license should be included with this distribution in
  ** the file LICENSE, as well as License.html. If the license is not
  ** included with this distribution, you may find a copy at the FSF web
  ** site at 'www.gnu.org' or 'www.fsf.org', or you may write to the
  ** Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139 USA.
  **
  ** THIS

劉錚 2007-11-01 17:54 發表評論
]]>
Java 的讀寫問題http://www.tkk7.com/liuzheng/articles/157546.html劉錚 劉錚 Thu, 01 Nov 2007 09:28:00 GMThttp://www.tkk7.com/liuzheng/articles/157546.htmlhttp://www.tkk7.com/liuzheng/comments/157546.htmlhttp://www.tkk7.com/liuzheng/articles/157546.html#Feedback0http://www.tkk7.com/liuzheng/comments/commentRss/157546.htmlhttp://www.tkk7.com/liuzheng/services/trackbacks/157546.html
File aFile = new File("input.data");
InputStream is = new FileInputStream(aFile);
byte[] data = new byte[aFile.length()];
is.read(data);

The results can be puzzling. If the input file is an image it may be mangled or not display at all. Binary or text data may be incomplete. This is because Read Doesn't Do What You Think It Does. A quick glance at the Java API for java.io.InputStream gives us the following information:


public int read(byte[] b) throws IOException

Reads some number of bytes from the input stream and stores them
into the buffer array b. The number of bytes actually read is returned as an integer. . .

The documentation states that read reads "some number" of bytes. It is not guaranteed to fill the byte array. The number of bytes read is returned by the read method. One should use this return value to make sure that he is receiving the data which he expects. A correct usage of read() would be to read chunks of a file and collect them in a buffer like so:


ByteArrayOutputStream buffer = new ByteArrayOutputStream();
File aFile = new File("input.data");
InputStream is = new FileInputStream(aFile);
byte[] temp = new byte[1024];
int read;

while((read = is.read(temp)) > 0){
   buffer.write(temp, 0, read);
}
           
byte[] data = buffer.toByteArray();
// process the data array . . .



劉錚 2007-11-01 17:28 發表評論
]]>
內部類的作用http://www.tkk7.com/liuzheng/articles/155015.html劉錚 劉錚 Mon, 22 Oct 2007 07:59:00 GMThttp://www.tkk7.com/liuzheng/articles/155015.htmlhttp://www.tkk7.com/liuzheng/comments/155015.htmlhttp://www.tkk7.com/liuzheng/articles/155015.html#Feedback0http://www.tkk7.com/liuzheng/comments/commentRss/155015.htmlhttp://www.tkk7.com/liuzheng/services/trackbacks/155015.html
Java內部類是Java言語的一個很重要的概念,《Java編程思想》花了很大的篇幅來講述這個概念。但是我們在實踐中很少用到它,雖然我們在很多時候會被動的使用到它,但它仍然像一個幕后英雄一樣,不為我們所知,不為我們所用。
本文不試圖來講述Java內部類的今生前世、來龍去脈,這些在網絡上都已經汗牛充棟。如果讀者想了解這些,可以在網絡上搜索來學習。Java內部類總是躲在它的外部類里,像一個幕后英雄一樣。但是幕后英雄也有用武之地,在很多時候,恰當的使用Java內部類能起到讓人拍案叫絕的作用。本文試圖談一談讓這個幕后英雄也有用武之地的四個場景,希望引起大家對使用Java內部類的興趣。
以下的文字,要求大家熟悉Java內部類的概念后來閱讀。
 
 
場景一:當某個類除了它的外部類,不再被其他的類使用時
我們說這個內部類依附于它的外部類而存在,可能的原因有:1、不可能為其他的類使用;2、出于某種原因,不能被其他類引用,可能會引起錯誤。等等。這個場景是我們使用內部類比較多的一個場景。下面我們以一個大家熟悉的例子來說明。
在我們的企業級Java項目開發過程中,數據庫連接池是一個我們經常要用到的概念。雖然在很多時候,我們都是用的第三方的數據庫連接池,不需要我們親自來做這個數據庫連接池。但是,作為我們Java內部類使用的第一個場景,這個數據庫連接池是一個很好的例子。為了簡單起見,以下我們就來簡單的模擬一下數據庫連接池,在我們的例子中,我們只實現數據庫連接池的一些簡單的功能。如果想完全實現它,大家不妨自己試一試。
首先,我們定義一個接口,將數據庫連接池的功能先定義出來,如下:
public interface Pool extends TimerListener
{
        //初始化連接池
        public boolean init();
        //銷毀連接池
        public void destory();
        //取得一個連接
        public Connection getConn();
        //還有一些其他的功能,這里不再列出
        ……
}
有了這個功能接口,我們就可以在它的基礎上實現數據庫連接池的部分功能了。我們首先想到這個數據庫連接池類的操作對象應該是由Connection對象組成的一個數組,既然是數組,我們的池在取得Connection的時候,就要對數組元素進行遍歷,看看Connection對象是否已經被使用,所以數組里每一個Connection對象都要有一個使用標志。我們再對連接池的功能進行分析,會發現每一個Connection對象還要一個上次訪問時間和使用次數。
通過上面的分析,我們可以得出,連接池里的數組的元素應該是由對象組成,該對象的類可能如下:
public class PoolConn
{
        private Connection conn;
        private boolean isUse;
        private long lastAccess;
        private int useCount;
        ……
}
下面的省略號省掉的是關于四個屬性的一些get和set方法。我們可以看到這個類的核心就是Connection,其他的一些屬性都是Connection的一些標志。可以說這個類只有在連接池這個類里有用,其他地方用不到。這時候,我們就該考慮是不是可以把這個類作為一個內部類呢?而且我們把它作為一個內部類以后,可以把它定義成一個私有類,然后將它的屬性公開,這樣省掉了那些無謂的get和set方法。下面我們就試試看:
public class ConnectPool implements Pool
{
        //存在Connection的數組
        private PoolConn[] poolConns;
        //連接池的最小連接數
        private int min;
        //連接池的最大連接數
        private int max;
        //一個連接的最大使用次數
        private int maxUseCount;
        //一個連接的最大空閑時間
        private long maxTimeout;
        //同一時間的Connection最大使用個數
        private int maxConns;
        //定時器
        private Timer timer;
        public boolean init()
        {
               try
               {
                      ……
                      this.poolConns = new PoolConn[this.min];
                      for(int i=0;i<this.min;i++)
                      {
                             PoolConn poolConn = new PoolConn();
                             poolConn.conn = ConnectionManager.getConnection();
                             poolConn.isUse = false;
                             poolConn.lastAccess = new Date().getTime();
                             poolConn.useCount = 0;
                             this.poolConns[i] = poolConn;
}
……
return true;
               }
               catch(Exception e)
               {
                      return false;
}
}
……
private class PoolConn
{
       public Connection conn;
       public boolean isUse;
public long lastAccess;
       public int useCount;
}
}
因為本文不是專題來講述數據庫連接池的,所以在上面的代碼中絕大部分的內容被省略掉了。PoolConn類不大可能被除了ConnectionPool類的其他類使用到,把它作為ConnectionPool的私有內部類不會影響到其他類。同時,我們可以看到,使用了內部類,使得我們可以將該內部類的數據公開,ConnectionPool類可以直接操作PoolConn類的數據成員,避免了因set和get方法帶來的麻煩。
上面的一個例子,是使用內部類使得你的代碼得到簡化和方便。還有些情況下,你可能要避免你的類被除了它的外部類以外的類使用到,這時候你卻不得不使用內部類來解決問題。
 
場景二:解決一些非面向對象的語句塊
這些語句塊包括if…else if…else語句,case語句,等等。這些語句都不是面向對象的,給我們造成了系統的擴展上的麻煩。我們可以看看,在模式中,有多少模式是用來解決由if語句帶來的擴展性的問題。
Java編程中還有一個困擾我們的問題,那就是try…catch…問題,特別是在JDBC編程過程中。請看下面的代碼:
……
try
         {
                String[] divisionData = null;
                conn = manager.getInstance().getConnection();
                stmt = (OracleCallableStatement)conn.prepareCall("{ Call PM_GET_PRODUCT.HEADER_DIVISION(?, ?) }");
                stmt.setLong(1 ,productId.longValue() );
                stmt.registerOutParameter(2, oracle.jdbc.OracleTypes.CURSOR); ;
                stmt.execute();
                ResultSet rs = stmt.getCursor(2);
                int i = 0 ;
                String strDivision = "";
                while( rs.next() )
                {
                             strDivision += rs.getString("DIVISION_ID") + "," ;
                  }
                  int length = strDivision.length() ;
                  if(length != 0 )
                  {
                         strDivision = strDivision.substring(0,length - 1);
                  }
                  divisionData = StringUtil.split(strDivision, ",") ;
                  map.put("Division", strDivision ) ;
                  LoggerAgent.debug("GetHeaderProcess","getDivisionData","getValue + " + strDivision +" " + productId) ;
       }catch(Exception e)
        {
                       LoggerAgent.error("GetHeaderData", "getDivisionData",
                                                     "SQLException: " + e);
                       e.printStackTrace() ;
 
       }finally
        {
                       manager.close(stmt);
                       manager.releaseConnection(conn);
        }
這是我們最最常用的一個JDBC編程的代碼示例。一個系統有很多這樣的查詢方法,這段代碼一般分作三段:try關鍵字括起來的那段是用來做查詢操作的,catch關鍵字括起來的那段需要做兩件事,記錄出錯的原因和事務回滾(如果需要的話),finally關鍵字括起來的那段用來釋放數據庫連接。
我們的煩惱是:try關鍵字括起來的那段是變化的,每個方法的一般都不一樣。而catch和finally關鍵字括起來的那兩段卻一般都是不變的,每個方法的那兩段都是一樣的。既然后面那兩段是一樣的,我們就非常希望將它們提取出來,做一個單獨的方法,然后讓每一個使用到它們的方法調用。但是,try…catch…finally…是一個完整的語句段,不能把它們分開。這樣的結果,使得我們不得不在每一個數據層方法里重復的寫相同的catch…finally…這兩段語句。
既然不能將那些討厭的try…catch…finally…作為一個公用方法提出去,那么我們還是需要想其他的辦法來解決這個問題。不然我們老是寫那么重復代碼,真是既繁瑣,又不容易維護。
我們容易想到,既然catch…finally…這兩段代碼不能提出來,那么我們能不能將try…里面的代碼提出去呢?唉喲,try…里面的代碼是可變的呢。怎么辦?
既然try…里面的代碼是可變的,這意味著這些代碼是可擴展的,是應該由用戶來實現的,對于這樣的可擴展內容,我們很容易想到用接口來定義它們,然后由用戶去實現。這樣以來我們首先定義一個接口:
public interface DataManager
{
        public void manageData();
}
我們需要用戶在manageData()方法中實現他們對數據層訪問的代碼,也就是try…里面的代碼。
然后我們使用一個模板類來實現所有的try…catch…finally…語句的功能,如下:
public class DataTemplate
{
        public void execute(DataManager dm)
        {
               try
               {
                      dm.manageData();
}
catch(Exception e)
{
       LoggerAgent.error("GetHeaderData", "getDivisionData",
                        "SQLException: " + e);
       e.printStackTrace() ;
 
}finally
{
       manager.close(stmt);
       manager.releaseConnection(conn);
}
}
}
這樣,一個模板類就完成了。我們也通過這個模板類將catch…finally…兩段代碼提出來了。我們來看看使用了這個模板類的數據層方法是怎么實現的:
new DataTemplate().execute(new DataManager()
{
        public void manageData()
        {
                String[] divisionData = null;
                conn = manager.getInstance().getConnection();
                stmt = (OracleCallableStatement)conn.prepareCall("{ Call PM_GET_PRODUCT.HEADER_DIVISION(?, ?) }");
                stmt.setLong(1 ,productId.longValue() );
                stmt.registerOutParameter(2, oracle.jdbc.OracleTypes.CURSOR); ;
                stmt.execute();
                ResultSet rs = stmt.getCursor(2);
                int i = 0 ;
                String strDivision = "";
                while( rs.next() )
                {
                             strDivision += rs.getString("DIVISION_ID") + "," ;
}
                  int length = strDivision.length() ;
                  if(length != 0 )
                  {
                         strDivision = strDivision.substring(0,length - 1);
                  }
                  divisionData = StringUtil.split(strDivision, ",") ;
                  map.put("Division", strDivision ) ;
                  LoggerAgent.debug("GetHeaderProcess","getDivisionData","getValue + " + strDivision +" " + productId) ;
}
});
注意:本段代碼僅供思路上的參考,沒有經過上機測試。
我們可以看到,正是這個實現了DataManager接口得匿名內部類的使用,才使得我們解決了對try…catch…finally…語句的改造。這樣,第一為我們解決了令人痛苦的重復代碼;第二也讓我們在數據層方法的編碼中,直接關注對數據的操作,不用關心那些必需的但是與數據操作無關的東西。
我們現在來回想一下Spring框架的數據層,是不是正是使用了這種方法呢?
 
 
場景之三:一些多算法場合
假如我們有這樣一個需求:我們的一個方法用來對數組排序并且依次打印各元素,對數組排序方法有很多種,用哪種方法排序交給用戶自己確定。
對于這樣一個需求,我們很容易解決。我們決定給哪些排序算法定義一個接口,具體的算法實現由用戶自己完成,只要求他實現我們的接口就行。
public interface SortAlgor
{
        public void sort(int[] is);
}
這樣,我們再在方法里實現先排序后打印,代碼如下:
public void printSortedArray(int[] is,SortAlgor sa)
{
        ……
       sa.sort(is);
        for(int i=0;i<is.length;i++)
        {
               System.out.print(is[i]+” “);
}
System.out.println();
}
客戶端對上面方法的使用如下:
int[] is = new int[]{3,1,4,9,2};
printSortedArray(is,new SortAlgor()
{
        public void sort(is)
        {
               int k = 0;
               for(int i=0;i<is.length;i++)
               {
                     for(int j=i+1;j<is.length;j++)
                      {
                             if(is[i]>is[j])
                             {
                                    k = is[i];
                                    is[i] = is[j];
                                    is[j] = k;
                             }
                      }
               }
}
});
這樣的用法很多,我們都或多或少的被動的使用過。如在Swing編程中,我們經常需要對組件增加監聽器對象,如下所示:
spinner2.addChangeListener(new ChangeListener()
{
public void stateChanged(ChangeEvent e)
{
System.out.println("Source: " + e.getSource());
}
}
);
在Arrays包里,對元素為對象的數組的排序:
Arrays.sort(emps,new Comparator(){
        Public int compare(Object o1,Object o2)
        {
               return ((Employee)o1).getServedYears()-((Employee)o2).getServedYears();
}
});
這樣的例子還有很多,JDK教會了我們很多使用內部類的方法。隨時我們都可以看一看API,看看還會在什么地方使用到內部類呢?
 
 
 
場景之四:適當使用內部類,使得代碼更加靈活和富有擴展性
適當的使用內部類,可以使得你的代碼更加靈活和富有擴展性。當然,在這里頭起作用的還是一些模式的運行,但如果不配以內部類的使用,這些方法的使用效果就差遠了。不信?請看下面的例子:
我們記得簡單工廠模式的作用就是將客戶對各個對象的依賴轉移到了工廠類里。很顯然,簡單工廠模式并沒有消除那些依賴,只是簡單的將它們轉移到了工廠類里。如果有新的對象增加進來,則我們需要修改工廠類。所以我們需要對工廠類做進一步的改造,進一步消除它對具體類的依賴。以前我們提供過一個使用反射來消除依賴的方法;這里,我們將提供另外一種方法。
這種方法是將工廠進一步抽象,而將具體的工廠類交由具體類的創建者來實現,這樣,工廠類和具體類的依賴的問題就得到了解決。而工廠的使用者則調用抽象的工廠來獲得具體類的對象。如下。
我們以一個生產形體的工廠為例,下面是這些形體的接口:
package polyFactory;
 
public interface Shape {
public void draw();
public void erase();
 
}
 
通過上面的描述,大家都可能已經猜到,這個抽象的工廠肯定使用的是模板方法模式。如下:
package polyFactory;
 
import java.util.HashMap;
import java.util.Map;
 
 
public abstract class ShapeFactory {
protected abstract Shape create();
private static Map factories = new HashMap();
public static void addFactory(String id,ShapeFactory f)
{
       factories.put(id,f);
}
public static final Shape createShape(String id)
{
       if(!factories.containsKey(id))
        {
               try
               {
                      Class.forName("polyFactory."+id);
               }
               catch(ClassNotFoundException e)
               {
                      throw new RuntimeException("Bad shape creation : "+id);
               }
        }
        return ((ShapeFactory)factories.get(id)).create();
}
}
不錯,正是模板方法模式的運用。這個類蠻簡單的:首先是一個create()方法,用來產生具體類的對象,留交各具體工廠實現去實現。然后是一個Map類型的靜態變量,用來存放具體工廠的實現以及他們的ID號。接著的一個方法使用來增加一個具體工廠的實現。最后一個靜態方法是用來獲取具體對象,里面的那個Class.forName……的作用是調用以ID號為類名的類的一些靜態的東西。
下面,我們來看具體的類的實現:
package polyFactory;
 
public class Circle implements Shape {
 
 
public void draw() {
        // TODO Auto-generated method stub
       System.out.println("the circle is drawing...");
}
 
 
public void erase() {
        // TODO Auto-generated method stub
       System.out.println("the circle is erasing...");
}
private static class Factory extends ShapeFactory
{
       protected Shape create()
        {
               return new Circle();
        }
}
static {ShapeFactory.addFactory("Circle",new Factory());}
 
}
這個類的其他的地方也平常得很。但就是后面的那個內部類Factory用得好。第一呢,這個類只做一件事,就是產生一個Circle對象,與其他類無關,就這一個條也就滿足了使用內部類的條件。第二呢,這個Factory類需要是靜態的,這也得要求它被使用內部類,不然,下面的ShapeFacotry.addFactory就沒辦法add了。而最后的那個靜態的語句塊是用來將具體的工廠類添加到抽象的工廠里面去。在抽象工廠里調用Class.forName就會執行這個靜態的語句塊了。
下面仍然是一個具體類:
package polyFactory;
 
 
public class Square implements Shape {
 
public void draw() {
        // TODO Auto-generated method stub
       System.out.println("the square is drawing...");
}
 
public void erase() {
        // TODO Auto-generated method stub
       System.out.println("the square is erasing...");
}
private static class Factory extends ShapeFactory
{
       protected Shape create()
        {
               return new Square();
        }
}
static {ShapeFactory.addFactory("Square",new Factory());}
 
}
最后,我們來測試一下:
String[] ids = new String[]{"Circle","Square","Square","Circle"};
        for(int i=0;i<ids.length;i++)
        {
               Shape shape = ShapeFactory.createShape(ids[i]);
               shape.draw();
               shape.erase();
        }
測試結果為:
the circle is drawing...
the circle is erasing...
the square is drawing...
the square is erasing...
the square is drawing...
the square is erasing...
the circle is drawing...
the circle is erasing...
       這個方法是巧妙地使用了內部類,將具體類的實現和它的具體工廠類綁定起來,由具體類的實現者在這個內部類的具體工廠里去產生一個具體類的對象,這當然容易得多。雖然需要每一個具體類都創建一個具體工廠類,但由于具體工廠類是一個內部類,這樣也不會隨著具體類的增加而不斷增加新的工廠類,使得代碼看起來很臃腫,這也是本方法不得不使用內部類的一個原因吧。


劉錚 2007-10-22 15:59 發表評論
]]>
有關類中的重構問題http://www.tkk7.com/liuzheng/articles/153827.html劉錚 劉錚 Thu, 18 Oct 2007 04:07:00 GMThttp://www.tkk7.com/liuzheng/articles/153827.htmlhttp://www.tkk7.com/liuzheng/comments/153827.htmlhttp://www.tkk7.com/liuzheng/articles/153827.html#Feedback0http://www.tkk7.com/liuzheng/comments/commentRss/153827.htmlhttp://www.tkk7.com/liuzheng/services/trackbacks/153827.html
例如:
public class Bookshelf {
private int capacity = 20;
private Collection content;
public void add(Book book) {
if(content.size() + 1 <= capacity)
{
content.add(book);
} else {
throw new
IllegalOperationException(
“The bookshelf has reached
its limit.”);
}
}
}
We can refactor the code, extracting the constraint in a separate
method.
public class Bookshelf {
private int capacity = 20;
private Collection content;
public void add(Book book) {
if(isSpaceAvailable()) {
content.add(book);
} else {
throw new
IllegalOperationException(
“The bookshelf has reached
its limit.”);
}
}
private boolean isSpaceAvailable() {
return content.size() < capacity;
}
}

劉錚 2007-10-18 12:07 發表評論
]]>
在java中使用DOM對XML的解析http://www.tkk7.com/liuzheng/articles/153013.html劉錚 劉錚 Mon, 15 Oct 2007 08:22:00 GMThttp://www.tkk7.com/liuzheng/articles/153013.htmlhttp://www.tkk7.com/liuzheng/comments/153013.htmlhttp://www.tkk7.com/liuzheng/articles/153013.html#Feedback0http://www.tkk7.com/liuzheng/comments/commentRss/153013.htmlhttp://www.tkk7.com/liuzheng/services/trackbacks/153013.html
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();

You can now read a document from a file:

File f = . . .
Document doc = builder.parse(f);

Alternatively, you can use a URL:

URL u = . . .
Document doc = builder.parse(u);

You can even specify an arbitrary input stream:

InputStream in = . . .
Document doc = builder.parse(in);

Element root = doc.getDocumentElement();

注意如果使用這種解析方法的話:
會有空格產生
NodeList children = root.getChildNodes();
for (int i = 0; i < children.getLength(); i++)
{
Node child = children.item(i);
. . .
}

如果避免空格產生


If you expect only subelements, then you can ignore the whitespace:

for (int i = 0; i < children.getLength(); i++)
{
Node child = children.item(i);
if (child instanceof Element)
{
Element childElement = (Element) child;
. . .
}
}

      Text textNode = (Text) childElement.getFirstChild();
      String text = textNode.getData().trim();
getData()方法可以得到textNode中的值



劉錚 2007-10-15 16:22 發表評論
]]>
分頁通用技術http://www.tkk7.com/liuzheng/articles/145904.html劉錚 劉錚 Mon, 17 Sep 2007 08:41:00 GMThttp://www.tkk7.com/liuzheng/articles/145904.htmlhttp://www.tkk7.com/liuzheng/comments/145904.htmlhttp://www.tkk7.com/liuzheng/articles/145904.html#Feedback0http://www.tkk7.com/liuzheng/comments/commentRss/145904.htmlhttp://www.tkk7.com/liuzheng/services/trackbacks/145904.html首先聲明以下是轉載的,感謝原作者

通用分頁實現及其OO設計探討

本來是打算拿到it168投稿的!!那邊的速度太慢了!先貼出來先!!

分頁是一種常用的頁面數據顯示技術,分頁能夠通過減少頁面數據處理量從而提高了系統的性能。分頁應該是做WEB開發必須掌握的一個小技術。而分頁卻是復雜的,倒不是它的技術有多復雜;而是有太多的重復代碼,這些代碼都難以重用。能不能實現一個通用的分頁框架?每次只需要去覆寫一兩個方法,通過少量的代碼就能實現分頁的功能?

一般分頁應該要具有的功能有
1. 靈活的設置分頁大小。可以動態的設置分頁大小,而不是寫死到代碼中。
2. 自動計算總頁數。根據分頁大小和總記錄數自動計算總頁數。
3. 獲得當前頁的頁號。
4. 獲得當前頁的總記錄數。一般是最后一頁的時候可能會小于分頁大小。
5. 判斷當前頁是否為第一頁。
6. 判斷當前頁是否為最后一頁。
7. 判斷當前頁是否有上一頁。
8. 判斷當前頁是否有下一頁。
9. 獲得當前頁的數據列表。
10. 獲得當前頁的第一條記錄的索引號
11. 獲得當前頁的最后一條記錄的索引號。
二、常用的分頁技術
目前常用的分頁技術有兩種:
1. 第一次訪問是讀取所有記錄,放入session中,然后每次從session對象中讀取當前頁的數據
2. 每次都訪問數據庫,從數據庫中讀取當前頁的記錄。
這兩種方法都各有優缺點,當數據量比較少時,第一種方法無疑是要快一些,因為減少與數據庫的連接訪問。而當數據量比較大時,比如查詢結果可能會是上萬條,那么內存的開銷是十分大的,放到session中還有一個問題是能不能及時的清除無用的對象。而且這么大數據量在網絡中傳輸也會使系統變得很慢。
第二種方法就是專門解決這個問題的,它每次訪問數據庫,只讀取當前頁所需的記錄,大大的減少網絡傳輸量;它不會把頁數據放到session中,大大提高服務器的性能。
所以第二種方式要優于第一種方法。Session不要亂用,要用也僅僅是存放一些公共變量,相對于占用空間比較少的對象。不適合存放大量的數據,否則在很多個用戶同時訪問時那么系統會很慢,因為服務器內存被銷耗的很厲害

三、通用分頁框架需要解決的問題
作為一個通用分頁框架,
1. 應該不依賴于任何其它框架
2. 應該支持多種數據庫
3. 應該可以應用于任何web框架中,如:struts,spring等。
4. 應該把數據訪問的具體實現留給用戶去實現。
5. 應該實現關鍵的算法和過程,如:計算總頁數,所需的實始化動作。
6. 應該減化Contrller控制器的代碼,以往的分頁技術在Contrller中存在太多的if…else代碼。十分難懂,應該由一個輔助類來實現。
7. 應該減化jsp頁面的代碼,頁面應該沒有任何與分頁相關的計算。應該由分頁對象來實現。
8. 應該支持兩種分頁方式,采用session或不采用session由用戶控制。

四、具體實現
1.通用分頁接口。定義接口可以有更多不同的實現,接口只聲明了分頁應該具有的公共行為。
ViewPage.java
/**
* 分頁接口
* @author ex_yuanguangdong
*
*/
public interface ViewPage {



/**
* 獲取總頁數
* @return 總頁數
*/
public int getPageCount();


/**
* 獲得頁面大小
* @return 頁面大小
*/
public int getPageSize();


/**
* 設置頁面大小
* @param size
*/
public void setPageSize(int size);
/**
* 獲得當前頁數據
* @return 數據列表
*/
public List getPageData();


/**
* 獲得當前頁索引號
* @return 當前頁索引號
*/
public int getPageIndex();

/**
* 獲得當前頁記錄總數
* @return 當前頁記錄總數
*/
public int getPageRows();

/**
* 是否有下一頁
* @return
*/
public boolean getHashNextPage();


/**
* 是否有上一頁
* @return
*/
public boolean getHashPreviousPage();

/**
* 轉到尾頁
*
*/
public void gotoLastPage();
/**
* 轉到首頁
*
*/
public void gotoFirstPage();

/**
* 是否首頁
* @return
*/
public boolean isFirstPage();

/**
* 是否尾頁
* @return
*/
public boolean isLastPage();

/**
* 轉到上一頁
*
*/
public void gotoPreviousPage();

/**
* 轉到下一頁
*
*/
public void gotoNextPage();

/**
* 轉到指定頁面,pageIndex小于1時,轉到第一頁;pageIndex大于總頁數時,轉到最尾頁
* @param pageIndex 指定的頁號
*/
public void gotoPage(int pageIndex);

/**
* 獲取當前頁第一條記錄的記錄號
* @return int 當前頁第一條記錄的記錄號
*/
public int getPageFirstRecord();

/**
* 獲取當前頁最后一條記錄的記錄號
* @return int 當前頁最后一條記錄的記錄號
*/
public int getPageLastRecord();

}

2. 分頁抽像實現類,實現關鍵的算法
AbstractViewPage.java
/**
* 分頁默認抽象實現
* 初始時,分頁類有下列默認值:
* 分頁大小為-1,為不分頁;
* 總頁數為1頁
* 當前頁為第一頁
* 總記錄數為0條
* 當前頁數據列表為沒有任何記錄的列表
* @author ex_yuanguangdong
*
*/
public abstract class AbstractViewPage implements ViewPage {

//-----------------------------------------
//私有靜態常量
//-----------------------------------------

private static final int DEFAULT_PAGE_INDEX = 1;

private static final int DEFALT_PAGE_COUNT = 1;

private static final int DEFAULT_PAGE_SIZE = -1;

private static final int DEFAULT_ROWS = 0;

//-----------------------------------------
//私有成員變量
//-----------------------------------------

/**當前頁索引號**/
private int pageIndex = DEFAULT_PAGE_INDEX;
/**總頁數**/
private int pageCount =DEFALT_PAGE_COUNT;

/**分頁大小**/
private int pageSize = DEFAULT_PAGE_SIZE ;

/**數據總記錄數**/
private int rows = DEFAULT_ROWS;


//------------------------------------------
//本地成員變量getter,setter方法
//------------------------------------------


/**
* 設置新頁號,只有大于等于1而且小于等于總頁數并且不為當前頁時,才允許設置
* @param pageIndex The pageIndex to set.
*/
private void setPageIndex(int newPageIndex) {

if( newPageIndex >= this.DEFAULT_PAGE_INDEX && newPageIndex <= this.getPageCount() && newPageIndex != this.pageIndex) {
this.pageIndex = newPageIndex;
}

}

/**
* @return Returns the rows.
*/
private int getRows() {
return rows;
}

/**
* @param rows The rows to set.
*/
private void setRows(int rows) {
this.rows = rows;
}
/**
* @param pageCount The pageCount to set.
*/
private void setPageCount(int pageCount) {
this.pageCount = pageCount;
}



//--------------------------------------
//實現Page接口方法
//--------------------------------------


/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageData()
*/
public List getPageData() {
List pageList =null;

//獲得當前頁數據
pageList = this.pageList(this.getPageFirstRecord(), this.getPageRows());
//保證不返回null
if(pageList == null) {
pageList =new ArrayList();
}
return pageList;

}

/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageIndex()
*/
public int getPageIndex() {
return this.pageIndex;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#isFirstPage()
*/
public boolean isFirstPage() {

return this.DEFAULT_PAGE_INDEX ==this.pageIndex;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#isLastPage()
*/
public boolean isLastPage() {
//當前頁索引為總頁數時為最后一頁
return this.pageIndex == this.pageCount;
}

/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getHashNextPage()
*/
public boolean getHashNextPage() {
//當前頁索引號小于總頁數
return this.pageIndex < this.pageCount ;
}

/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getHashPreviousPage()
*/
public boolean getHashPreviousPage() {
//當前頁索引號大于默認的初始頁號,這里為1
return this.pageIndex > this.DEFAULT_PAGE_INDEX ;
}



/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageCount()
*/
public int getPageCount() {

return this.pageCount;
}



/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageSize()
*/
public int getPageSize() {

return this.pageSize;
}

/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageRows()
*/
public int getPageRows() {
//當頁面大小為-1 時,返回總記錄數
if(this.DEFAULT_PAGE_SIZE == this.pageSize ) {
return this.rows;
}

//不為最后一頁時,返回pageSize
if(!this.isLastPage()) {
return this.pageSize;
}
//最后一頁時
return this.rows - (this.pageSize * (this.pageCount -1));
}



/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageFirstRecord()
*/
public int getPageFirstRecord() {

//頁大小為-1 時
if(this.DEFAULT_PAGE_SIZE== this.pageSize ) {
return 0;
}
return (this.pageIndex -1)* this.pageSize;
}

/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageLastRecord()
*/
public int getPageLastRecord() {
//頁大小為-1時,返回總記錄數
if(this.DEFAULT_PAGE_SIZE == this.pageSize) {
return this.rows;
}
return this.getPageFirstRecord() + this.getPageRows() ;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#gotoFirstPage()
*/
public void gotoFirstPage() {
this.gotoPage(this.DEFAULT_PAGE_INDEX);

}

/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#gotoLastPage()
*/
public void gotoLastPage() {
this.gotoPage(this.getPageCount());

}

/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#gotoPreviousPage()
*/
public void gotoPreviousPage() {
this.gotoPage(this.getPageIndex() -1);

}

/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#gotoNextPage()
*/
public void gotoNextPage() {
this.gotoPage(this.getPageIndex() + 1);

}

/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#gotoPage(int)
*/
public void gotoPage(int newPageIndex) {
if( newPageIndex >= this.DEFAULT_PAGE_INDEX && newPageIndex <= this.getPageCount() ) {
this.setPageIndex(newPageIndex);
}

}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#setPageSize(int)
*/
public void setPageSize(int size) {
if(size < 1) {
size = 1;
}

this.pageSize = size;

//進行初始化
this.doInit();
}

//-----------------------------------
//輔助方法
//-----------------------------------


/**
* 分頁初始化方法,為了保證總是能正確的初始化,所以聲明為final ,為了讓子類可以調用聲明為protected
*
*/
protected final void doInit() {
int rows = 0;

//獲得總記錄數
rows= totalRows();

//設置總記錄數
this.setRows(rows);
//設置新的總頁數

//計算并設置總頁數
int pages = calculatePageCount();
this.setPageCount(pages);

//轉到第一頁
this.gotoPage(this.DEFAULT_PAGE_INDEX);

onInit();
}
/**
* 記算總頁數
* @return 總頁數
*/
private int calculatePageCount() {

//總記錄數為0條,則返回的總頁數為1
if(this.getRows() == 0) {
return this.DEFALT_PAGE_COUNT;
}

//如果頁面大小為-1,則返回的總頁數為1
if(this.DEFAULT_PAGE_SIZE == this.getPageSize() ) {
return this.DEFALT_PAGE_COUNT;
}

return this.getRows() / this.getPageSize() + ( this.getRows() % this.getPageSize() ==0 ? 0 :1);
}


/**
* 獲得總記錄數,調用queryTotalRows(),將異常封裝為non-checked 異常
* @return 總記錄數
* @throws ApplicationRuntimeException
*/
private int totalRows() throws ApplicationRuntimeException{
try{
return queryTotalRows();
}
catch(Exception ex){
throw new ApplicationRuntimeException(ex);
}
}
/**
* 獲得當前頁數據,調用queryPageList()方法,將異常封裝為non-checked異常
* @param startRow 開始記錄號
* @param rowCount 記錄總數
* @return 當前頁數據
* @throws ApplicationRuntimeException
*/
private List pageList(int startRow, int rowCount) throws ApplicationRuntimeException{
try{
return queryPageList(startRow, rowCount);
}catch(Exception ex){
throw new ApplicationRuntimeException(ex);
}
}


//-----------------------------------------
//子類實現的方法
//-----------------------------------------

/**
* 初始化附加方法,由子類擴展
*/
protected void onInit() {

}


/**
* 查詢獲得總記錄數,由子類具體實現
* @return 總記錄數
* @throws Exception
*/
protected abstract int queryTotalRows() throws Exception;

/**
* 查詢當前頁數據,從startRow 開始的rowCount條記錄
* @param startRow 開始記錄號
* @param rowCount 記錄總數
* @return 當前頁數據
* @throws Exception
*/
protected abstract List queryPageList(int startRow, int rowCount) throws Exception;

}

3. 分頁輔助類
ViewPageHelper.java/**
* 分頁輔助類,用于減化Controller中的代碼
* @author yuanguangdong
* date: Oct 22, 2006
*/
public class ViewPageHelper {
private static final int FIRST_PAGE_VALUE = 1;

private static final int PREVIOUS_PAGE_VALUE = 2;

private static final int NEXT_PAGE_VALUE = 3;

private static final int LAST_PAGE_VALUE = 4;

private static final int SPECIAL_PAGE_VALUE = 5;

public static final String FIRST_PAGE = "FIRST_PAGE";

public static final String PREVIOUS_PAGE = "PREVIOUS_PAGE";

public static final String NEXT_PAGE = "NEXT_PAGE";

public static final String LAST_PAGE = "LAST_PAGE";

public static final String SPECIAL_PAGE = "SPECIAL_PAGE";

/**分頁動作參數名**/
public static final String PAGE_ACTION = "page_action";

/**分頁對象屬性名**/
public static final String SESSION_PAGE = "session_page";

/**頁號參數名**/
public static final String PAGE_NO = "page_no";

private static Map actionMap = new HashMap();
static {
actionMap.put(FIRST_PAGE, new Integer(FIRST_PAGE_VALUE));
actionMap.put(PREVIOUS_PAGE, new Integer(PREVIOUS_PAGE_VALUE));
actionMap.put(NEXT_PAGE, new Integer(NEXT_PAGE_VALUE));
actionMap.put(LAST_PAGE, new Integer(LAST_PAGE_VALUE));
actionMap.put(SPECIAL_PAGE, new Integer(SPECIAL_PAGE_VALUE));
}
/**
* 執行分頁動作
* @param page 分頁對象
* @param action 分頁動作參數
* @param pageIndex 頁號
*/
public static void doAction(ViewPage page, String action, int pageIndex) {
int actionIndex = 0;
if (page == null) {
throw new NullPointerException("Page對象null");
}
if (action == null || "".equals(action)) {
throw new IllegalArgumentException("無效的分頁動作參數null");
}
action = action.toUpperCase();
if (!actionMap.containsKey(action)) {
throw new UnsupportedOperationException("不支持的分頁動作參數:" + action);
}
Integer index = (Integer) actionMap.get(action);
actionIndex = index.intValue();
switch (actionIndex) {
case FIRST_PAGE_VALUE:
page.gotoFirstPage();
break;
case PREVIOUS_PAGE_VALUE:
page.gotoPreviousPage();
break;
case NEXT_PAGE_VALUE:
page.gotoNextPage();
break;
case LAST_PAGE_VALUE:
page.gotoLastPage();
break;
case SPECIAL_PAGE_VALUE:
page.gotoPage(pageIndex);
}
}

public static void doAction(ViewPage page, String action){
doAction(page, action, 1);
}
}

五、應用通用分頁框架
1.繼承AbstractViewPage類,實現queryPageList(int startRow, int endRow)和
queryTotalRows()方法。

protected int queryTotalRows() throws Exception
獲得查詢條件的總記錄數

protected List queryPageList(int startRow, int rowCount)
用于查詢指定范圍的數據。startRow為開始記錄號, rowCount為查詢的記錄數

queryPageList(0,20)為查詢從第一條開始的20條記錄。

使用Ibatis可以由queryPageList調用queryForList()方法。

/**
* 用戶信息分頁內部類
* @author yuanguangdong
* date: Oct 22, 2006
*/
class UserInfoPage extends AbstractViewPage{

//------------------------------------------------
//實現AbstractViewPage抽象類的抽象方法
//------------------------------------------------

/**
* @see com.prs.application.ehld.web.mvc.AbstractViewPage#getPageDate(int, int)
*/
protected List queryPageList(int startRow, int endRow) throws Exception {
return sampleAction.getUserInfoList(startRow, endRow);
}

/**
* @see com.prs.application.ehld.web.mvc.AbstractViewPage#getRows()
*/
protected int queryTotalRows() throws Exception {
return sampleAction.getUserCount();
}

}



3. 在Contrller中的實現
public ModelAndView listUser(HttpServletRequest request,
HttpServletResponse response) throws Exception {
String pageAction =
RequestUtils.getStringParameter(request,ViewPageHelper.PAGE_ACTION);
Integer pageIndex =
RequestUtils.getIntParameter(request,ViewPageHelper.PAGE_NO);
//聲明分頁對象
ViewPage userPage =
(ViewPage) request.getSession().getAttribute(ViewPageHelper.SESSION_PAGE);
//第一次請求
if(pageAction == null || userPage == null){
//構建一個新的分頁對象
userPage = new UserInfoPage();
//設置分頁大小
userPage.setPageSize(2);
}else{

if(ViewPageHelper.SPECIAL_PAGE.equals(pageAction)){
//如果頁數為空,則默認為1
if (pageIndex == null)
pageIndex = new Integer(1);
ViewPageHelper.doAction(userPage,pageAction,pageIndex.intValue());
}else{
ViewPageHelper.doAction(userPage,pageAction);
}
}

//從分頁對象中獲得當前頁數據
List userInfoList = userPage.getPageData();

ModelAndView mav = new ModelAndView(userInfoListView);
mav.addObject(this.userInfoListKey,userInfoList);
request.getSession().setAttribute(ViewPageHelper.SESSION_PAGE,userPage);
return mav;
}

4. jsp頁面實現

<%@ page contentType="text/html;charset=utf-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt" %>
<%@ taglib prefix="tiles" uri="http://jakarta.apache.org/struts/tags-tiles" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<html>
<head>
<title>顯示所有員工</title>

<SCRIPT language="javaScript">
function pageNoChange(pageNo){
location.href= "ehld.sample.getuserinfolist.do?page_action=SPECIAL_PAGE&page_no="+pageNo.value;

}
</SCRIPT>
</head>

<body>
<table width="80%" border="0">
<tr>
<td bgcolor="#F0FEFF"><div align="left">  用戶列表</div></td>
</tr>
</table>
<br>
<input name="adduser" type="submit" id="adduser" value="新增用戶" onclick="location.href='ehld.sample.edituserinfo.do'">
<table width="80%" border="0">
<tr bgcolor="#58ED64">
<th width="25%">id</th>
<th width="34%">姓名</th>
<th colspan="2">操作</th>
</tr>
<c:forEach items="${userInfoList}" var="userInfoDTO">
<tr bgcolor="#D6EBF8">
<td><c:out value="${userInfoDTO.userID}"/></td>
<td><c:out value="${userInfoDTO.userName}"/></td>
<td width="21%"><a href="ehld.sample.edituserinfo.do?id=<c:out value='${userInfoDTO.userID}'/>">編輯</a></td>
<td width="20%"><a href="#">刪除</a></td>
</tr>
</c:forEach>
</table>
<c:if test="${session_page.firstPage}">
首頁
</c:if>
<c:if test="${! session_page.firstPage}">
<a href="ehld.sample.getuserinfolist.do?page_action=FIRST_PAGE">首頁</a>
</c:if>
<c:if test="${! session_page.hashPreviousPage}">
上一頁
</c:if>
<c:if test="${session_page.hashPreviousPage}">
<a href="ehld.sample.getuserinfolist.do?page_action=PREVIOUS_PAGE">上一頁</a>
</c:if>

<c:if test="${!session_page.hashNextPage}">
下一頁
</c:if>

<c:if test="${session_page.hashNextPage}">
<a href="ehld.sample.getuserinfolist.do?page_action=NEXT_PAGE">下一頁</a>
</c:if>

<c:if test="${session_page.lastPage}">
尾頁
</c:if>

<c:if test="${!session_page.lastPage}">
<a href="ehld.sample.getuserinfolist.do?page_action=LAST_PAGE">尾頁</a>
</c:if>

共有<c:out value="${session_page.pageCount}" />頁,第

<select name = "pageNo" onChange = "java script:pageNoChange(this);">
<c:forEach begin="1" end = "${session_page.pageCount}" var = "pageIndex">
<option value="<c:out value='${pageIndex}'/>" <c:if test = "${pageIndex ==session_page.pageIndex }">selected</c:if>>
<c:out value="${pageIndex}"/>
</option>
</c:forEach>
</select>

</body>
</html>

六、設計探討
1.通過提供queryTotalRows() 和queryPageList(int startRow, int rowCount)方法,交由用戶具體的去實現,所以能夠支持任何數據庫。
對于Ibatis用戶可以使用queryForList()方法,對于用jdbc實現也可以有多種方法來支持各種數據庫。
Ms sql 可以使用top 關鍵字,來獲得指定范圍的數據
ORACEL可以使用rowid 偽列來獲得指定范圍的數據
具體怎么去讀取數據,完全交由用戶控制
2.分頁對象與具體的業務對象分離。分頁對象如果不能與具體的業務對象分離那么就不可能實現分頁對象的重用,不可以實現代碼的最大的重用。這不符合oo的按職責來設計對象的原則。
3. ViewPageHelper幫助類的使用有兩個好處,統一為分頁代碼所需的字符參數進行定義,便于contrller和jsp頁面代碼的維護。第二便于代碼重用,減少在contrller中的if分支句語。如果不使用幫助類,則在每個controller中都會產生大量相同的代碼。
4. final關鍵字的使用,protected final void doInit()用于分頁對象的實始化,它讀取并設置總記錄數,計算總頁數,默認為第一頁等。為什么不在構造函數中來做它呢?如果在構造函數來做它,子類就不可以擴展了。像這樣的初始化方法的位置應該由擴展類來靈活控制。聲明為protected是不讓它由外部對象來進行訪問,但是子類又可以進行調用。聲明為final是為了子類不能重寫它,如果子類重寫不當就會造成分頁對象的執行邏輯錯誤。但是如果子類又想擴展它怎么辦?子類重寫protected void onInit()方法就可以了。這樣就能保證父類的邏輯,又能夠讓子類進行擴展。
5.異常處理的思考,queryTotalRows()和queryPageList方法都是要求由子類實現的抽象類,這兩個類的特點都是可能會調用業務對象去實現相應的功能,業務對象可能會訪問業務數據庫等,可能會拋出任何Exception,但是分頁對象類去調用queryTotalRows()和queryPageList的方法是不應該對這些Exception進行任何處理的,如果進行try…catch那么就會隱藏了異常的細節,這是十分可怕的。如果這些方法拋出異常,分頁對象應該是不能處理的,不能處理的異常應該封裝為運行時異常,所以就有了下面的實現
private List pageList(int startRow, int rowCount) throws ApplicationRuntimeException{
try{
return queryPageList(startRow, rowCount);
}catch(Exception ex){
throw new ApplicationRuntimeException(ex);
}
}

private int totalRows() throws ApplicationRuntimeException{
try{
return queryTotalRows();
}
catch(Exception ex){
throw new ApplicationRuntimeException(ex);
}
}

分頁對象內部調用pageList和totalRows方法,這樣就很好的解決了異常的問題,把異常交由外部調用者去決定是否處理,而不是強制調用者去處理。

5. 模板方法模式的使用,這是一個典型的模板方法模式的運用。在父類實現關鍵的算法代碼,實現分頁對象的處理邏輯,而把某些會發生改變的方法交由子類去實現,使得子類完全不用去關心父類的實現細節,子類只需要重寫兩個簡單的方法就可以實現父類的功能。這就是模板方法帶來的最大好處。模板方法模式在各種開源框架中有著廣泛的運用,看看spring的源碼就知道。子類只需要去實現自己最關心的細節,而父類實現那些不變的邏輯或算法。
6. 針對接口編程,而不是針對類編程。接口可以實現多重繼承,而類卻不能。接口有比類獲得更多的好處,更利于擴展。比如說分頁接口,它可以讓用戶有更多不同的實現,完全不依賴于任何類。只需要為它定制了共同的行為就可以了。在使用委托的時候接口比抽像類更好用。比如在裝飾模式的使用中,可能需要實現一個接口,而其中還要有一個本接口的引用。 如果是抽象類,則不可以實現。
7. 通用框架應該具有靈活性,不應該依懶于任何具體的框架。如果通用框架依懶于某一技術細節,某一框架,那么它就有一定的局限性。所以通用分頁不應該依懶于ibatis或hibernate 或spring的某一特點。更不應該依懶于sql或oralce某種數據庫。



劉錚 2007-09-17 16:41 發表評論
]]>
java中inputstream的有關類的設計模式-the decorator patternhttp://www.tkk7.com/liuzheng/articles/144443.html劉錚 劉錚 Wed, 12 Sep 2007 04:02:00 GMThttp://www.tkk7.com/liuzheng/articles/144443.htmlhttp://www.tkk7.com/liuzheng/comments/144443.htmlhttp://www.tkk7.com/liuzheng/articles/144443.html#Feedback0http://www.tkk7.com/liuzheng/comments/commentRss/144443.htmlhttp://www.tkk7.com/liuzheng/services/trackbacks/144443.htmljava中inputstream的有關類的設計模式-the decorator pattern

inputstream中比較重要的decorator----BufferedInputStream
它的構造函數為BufferedInputStream(InputStream in)
BufferedInputStream(InputStream in, int size)


可以裝飾繼承了inputsream類的類

自己完成的新的inputstream
public class LowerCaseInputStream extends FilterInputStream {
public LowerCaseInputStream(InputStream in) {
super(in);
}
public int read() throws IOException {
int c = super.read();
return (c == -1 ? c : Character.toLowerCase((char)c));
}
public int read(byte[] b, int offset, int len) throws IOException {
int result = super.read(b, offset, len);
for (int i = offset; i < offset+result; i++) {
b[i] = (byte)Character.toLowerCase((char)b[i]);
}
return result;
}
}


FiterInputStream 便是一種特殊的類,他滿足裝飾器(decorator)的條件,
1。必須繼承需要decorator的類:InputStream
2。在此類中必須包含需要decorator的類的實例,這樣的話此類就擁有decorator的類的功能還能擴展其他功能。
FiterInputStream 的原代碼

/*
 * @(#)FilterInputStream.java 1.28 03/12/19
 *
 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package java.io;

/**
 * A <code>FilterInputStream</code> contains
 * some other input stream, which it uses as
 * its  basic source of data, possibly transforming
 * the data along the way or providing  additional
 * functionality. The class <code>FilterInputStream</code>
 * itself simply overrides all  methods of
 * <code>InputStream</code> with versions that
 * pass all requests to the contained  input
 * stream. Subclasses of <code>FilterInputStream</code>
 * may further override some of  these methods
 * and may also provide additional methods
 * and fields.
 *
 * @author  Jonathan Payne
 * @version 1.28, 12/19/03
 * @since   JDK1.0
 */
public
class FilterInputStream extends InputStream {
    /**
     * The input stream to be filtered.
     */
    protected volatile InputStream in;   
/**
     * Creates a <code>FilterInputStream</code>
     * by assigning the  argument <code>in</code>
     * to the field <code>this.in</code> so as
     * to remember it for later use.
     *
     * @param   in   the underlying input stream, or <code>null</code> if
     *          this instance is to be created without an underlying stream.
     */
    protected FilterInputStream(InputStream in) {
 this.in = in;
    }

    /**
     * Reads the next byte of data from this input stream. The value
     * byte is returned as an <code>int</code> in the range
     * <code>0</code> to <code>255</code>. If no byte is available
     * because the end of the stream has been reached, the value
     * <code>-1</code> is returned. This method blocks until input data
     * is available, the end of the stream is detected, or an exception
     * is thrown.
     * <p>
     * This method
     * simply performs <code>in.read()</code> and returns the result.
     *
     * @return     the next byte of data, or <code>-1</code> if the end of the
     *             stream is reached.
     * @exception  IOException  if an I/O error occurs.
     * @see        java.io.FilterInputStream#in
     */
    public int read() throws IOException {
 return in.read();
    }

    /**
     * Reads up to <code>byte.length</code> bytes of data from this
     * input stream into an array of bytes. This method blocks until some
     * input is available.
     * <p>
     * This method simply performs the call
     * <code>read(b, 0, b.length)</code> and returns
     * the  result. It is important that it does
     * <i>not</i> do <code>in.read(b)</code> instead;
     * certain subclasses of  <code>FilterInputStream</code>
     * depend on the implementation strategy actually
     * used.
     *
     * @param      b   the buffer into which the data is read.
     * @return     the total number of bytes read into the buffer, or
     *             <code>-1</code> if there is no more data because the end of
     *             the stream has been reached.
     * @exception  IOException  if an I/O error occurs.
     * @see        java.io.FilterInputStream#read(byte[], int, int)
     */
    public int read(byte b[]) throws IOException {
 return read(b, 0, b.length);
    }

    /**
     * Reads up to <code>len</code> bytes of data from this input stream
     * into an array of bytes. This method blocks until some input is
     * available.
     * <p>
     * This method simply performs <code>in.read(b, off, len)</code>
     * and returns the result.
     *
     * @param      b     the buffer into which the data is read.
     * @param      off   the start offset of the data.
     * @param      len   the maximum number of bytes read.
     * @return     the total number of bytes read into the buffer, or
     *             <code>-1</code> if there is no more data because the end of
     *             the stream has been reached.
     * @exception  IOException  if an I/O error occurs.
     * @see        java.io.FilterInputStream#in
     */
    public int read(byte b[], int off, int len) throws IOException {
 return in.read(b, off, len);
    }

    /**
     * Skips over and discards <code>n</code> bytes of data from the
     * input stream. The <code>skip</code> method may, for a variety of
     * reasons, end up skipping over some smaller number of bytes,
     * possibly <code>0</code>. The actual number of bytes skipped is
     * returned.
     * <p>
     * This method
     * simply performs <code>in.skip(n)</code>.
     *
     * @param      n   the number of bytes to be skipped.
     * @return     the actual number of bytes skipped.
     * @exception  IOException  if an I/O error occurs.
     */
    public long skip(long n) throws IOException {
 return in.skip(n);
    }

    /**
     * Returns the number of bytes that can be read from this input
     * stream without blocking.
     * <p>
     * This method
     * simply performs <code>in.available()</code> and
     * returns the result.
     *
     * @return     the number of bytes that can be read from the input stream
     *             without blocking.
     * @exception  IOException  if an I/O error occurs.
     * @see        java.io.FilterInputStream#in
     */
    public int available() throws IOException {
 return in.available();
    }

    /**
     * Closes this input stream and releases any system resources
     * associated with the stream.
     * This
     * method simply performs <code>in.close()</code>.
     *
     * @exception  IOException  if an I/O error occurs.
     * @see        java.io.FilterInputStream#in
     */
    public void close() throws IOException {
 in.close();
    }

    /**
     * Marks the current position in this input stream. A subsequent
     * call to the <code>reset</code> method repositions this stream at
     * the last marked position so that subsequent reads re-read the same bytes.
     * <p>
     * The <code>readlimit</code> argument tells this input stream to
     * allow that many bytes to be read before the mark position gets
     * invalidated.
     * <p>
     * This method simply performs <code>in.mark(readlimit)</code>.
     *
     * @param   readlimit   the maximum limit of bytes that can be read before
     *                      the mark position becomes invalid.
     * @see     java.io.FilterInputStream#in
     * @see     java.io.FilterInputStream#reset()
     */
    public synchronized void mark(int readlimit) {
 in.mark(readlimit);
    }

    /**
     * Repositions this stream to the position at the time the
     * <code>mark</code> method was last called on this input stream.
     * <p>
     * This method
     * simply performs <code>in.reset()</code>.
     * <p>
     * Stream marks are intended to be used in
     * situations where you need to read ahead a little to see what's in
     * the stream. Often this is most easily done by invoking some
     * general parser. If the stream is of the type handled by the
     * parse, it just chugs along happily. If the stream is not of
     * that type, the parser should toss an exception when it fails.
     * If this happens within readlimit bytes, it allows the outer
     * code to reset the stream and try another parser.
     *
     * @exception  IOException  if the stream has not been marked or if the
     *               mark has been invalidated.
     * @see        java.io.FilterInputStream#in
     * @see        java.io.FilterInputStream#mark(int)
     */
    public synchronized void reset() throws IOException {
 in.reset();
    }

    /**
     * Tests if this input stream supports the <code>mark</code>
     * and <code>reset</code> methods.
     * This method
     * simply performs <code>in.markSupported()</code>.
     *
     * @return  <code>true</code> if this stream type supports the
     *          <code>mark</code> and <code>reset</code> method;
     *          <code>false</code> otherwise.
     * @see     java.io.FilterInputStream#in
     * @see     java.io.InputStream#mark(int)
     * @see     java.io.InputStream#reset()
     */
    public boolean markSupported() {
 return in.markSupported();
    }
}



劉錚 2007-09-12 12:02 發表評論
]]>
JNDI的簡單概念&簡單示例http://www.tkk7.com/liuzheng/articles/143902.html劉錚 劉錚 Mon, 10 Sep 2007 02:43:00 GMThttp://www.tkk7.com/liuzheng/articles/143902.htmlhttp://www.tkk7.com/liuzheng/comments/143902.htmlhttp://www.tkk7.com/liuzheng/articles/143902.html#Feedback0http://www.tkk7.com/liuzheng/comments/commentRss/143902.htmlhttp://www.tkk7.com/liuzheng/services/trackbacks/143902.html介紹JNDI的簡單概念&簡單示例。

JNDI: The Java Naming and Directory Interface


 什么是JNDI

The Java Naming and Directory Interface是訪問不同名字和目錄服務的統一API接口。

不同的服務使用不同的名字格式。

Java程序需要以相同的格式訪問數據庫,文件,目錄,對象和網絡。

JNID有兩部分接口:應用程序接口和提供服務的接口。在應用程序中使用API來訪問名字或目錄服務,在一個新的服務中使用SPI來提供服務。

JNDI結構

名字服務(Naming Services)

名字服務提供一種方法,映射標識符到實體或對象。

你需要知道的幾條基本條款:

綁定:綁定是將一個不可分割的名字("原子"名字)與一個對象聯系起來。像DNS,我們用名字www.yahoo.com與IP地址216.32.74.53聯系起來,一個文件對象用文件名afile.txt聯系起來。

名字空間;名字空間包含一組名字,但名字空間內每個名字是唯一的。一個文件目錄就是一個簡單的名字空間,如目錄C:\temp,在這個目錄下,不能有兩個相同名字的文件,但是,不同目錄下的兩個文件可能有相同的名字。

復合名字:復合名字是用名字空間構成的唯一名字,有一個或多個"原子"名字構成,取決于所在的名字空間。文件路徑就是一個復合名字,比如我們用C:\temp\myfile.txt,我們可以看到,這個名字由根目錄名(C:\),臨時目錄名(temp)和一個文件名(myfile.txt)構成,這3個名字復合起來表示一個唯一的名字。

組合名字:組合名字能跨越多個名字空間。一個URL就是一個組合名字,如果你看見http://www.npu.edu/index.htm,你使用http服務連接到服務器,然后使用另一個名字空間/index.htm來訪問一個文件。

目錄服務

目錄服務提供一組分成等級的目錄對象,具有可搜索的能力。

在目錄服務中存儲的對象可以是任何能用一組屬性描述的對象,每個對象都可通過一組屬性來描述該對象的能力。例如,一個Person對象可能有height,hair color,age,sex等屬性。目錄服務還可提供根據要求來搜索的能力,如我們可以使用Person的age屬性,搜索20-25歲間的Person對象,目錄服務將返回符合條件的Persion對象。這通常被稱作基于內容的搜索。


在客戶端使用JNDI:

u       創建一個java.util.Hashtable或者java.util.Properties的實例。

u       添加變量到Hashtable或Properties對象:

由naming server提供的JNDI class類名。

包含aming server位置的URL。

安全信任書。

u       通過Hashtable或Properites或jndi屬性文件創建一個InitialContext對象。

 

示例:
import java.util.*;
import javax.naming.*;
...
env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
env.put(Context.PROVIDER_URL,"t3://localhost:7001");
InitialContext ctx = new InitialContext(env);

環境變量

相應的常量

說明

java.naming.factory.initial

Context.INITIAL_CONTEXT_FACTORY

Context Factory

類名,由服務提供商給出。

java.naming.provider.url

Context.PROVIDE_URL

初始化地址。

java.naming.security.

principal

Context.SECURITY_PRINCIPAL

服務使用者信息。

java.naming.security.

credentials

Context.SECURITY_CREDENTIAL

口令。

更多的配置示例:
Hashtable env = new Hashtable();
env.put (Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
env.put(Context.PROVIDER_URL, "t3://localhost:7001");
env.put(Context.SECURITY_PRINCIPAL, "system");
env.put(Context.SECURITY_CREDENTIALS, "password here");
Properties env = new Properties();
env.setProperties ("java.naming.factory.initial",
"weblogic.jndi.WLInitialContextFactory");
env.setProperties("java.naming.provider.url" , "t3://localhost:7001");
env.setProperties("java.naming.security.principal" , "tommy");
env.setProperties("java.naming.security.credentials" ,"password here");
創建InitialContext
Class Name: javax.naming.InitialContext
Interfaces that it implements: javax.naming.Context
Constructors:
    public InitialContext();
    public InitialContext(Hashtable configuration);
    public InitialContext(Properties configuration);

以上所有方法都可能拋出NamingException。

一個Binding示例:
public static InitialContext getInitialContext() throws NamingException {
    Hashtable env = new Hashtable();
    env.put(Context.INITIAL_CONTEXT_FACTORY,
         "weblogic.jndi.WLInitialContextFactory");
    env.put(Context.PROVIDER_URL,"t3://localhost:7001");
    InitialContext context = new InitialContext(env);
    return context;
}
//Obtain the initial context
InitialContext initialContext = getInitialContext();
//Create a Bank object.
Bank myBank = new Bank();
//Bind the object into the JNDI tree.
initialContext.rebind("theBank",myBank);
一個Lookup示例:
public static InitialContext getInitialContext() throws NamingException {
    Hashtable env = new Hashtable();
    env.put(Context.INITIAL_CONTEXT_FACTORY,
         "weblogic.jndi.WLInitialContextFactory");
    env.put(Context.PROVIDER_URL,"t3://localhost:7001");
    InitialContext context = new InitialContext(env);
    return context;
}
//Obtain the initial context
InitialContext initialContext = getInitialContext();
//Lookup an existing Bank object.
Bank myBank = (Bank) initialContext.lookup("theBank");
可能發生的NamingException
AuthenticationException
CommunicationException
InvalidNameException
NameNotFoundException
NoInitialContextException
枚舉所有名字對象:
NamingEnumeration Declaration:
public interface NamingEnumeration extends Enumeration {
    public boolean hashMore() throws NamingException;
    public Object next() throws NamingException;
    public void close() throws NamingException; //jndi 1.2
}
try {
    ...
    NamingEnumeration enum = ctx.list("");
    while (enum.hasMore()) {
        NameClassPair ncp = (NameClassPair) enum.next();
        System.out.println("JNDI name is:" + ncp.getName());
    }
}
catch (NamingException e) {...}
最后的示例:
import java.util.*;
import javax.naming.*;
import javax.naming.directory.*;
import java.io.*;
public class ListAll {
    public static void main(java.lang.String[] args) {
        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY,
            "weblogic.jndi.WLInitialContextFactory");
        env.put(Context.PROVIDER_URL, "t3://localhost:7001");
        try {
            InitialContext ctx = new InitialContext(env);
            NamingEnumeration enum = ctx.listBindings("");
            while(enum.hasMore()) {
                Binding binding = (Binding) enum.next();
                Object obj = (Object) binding.getObject();
                System.out.println(obj);
            }
        } catch (NamingException e) {
        System.out.println(e);
        }
    } // end main

}



劉錚 2007-09-10 10:43 發表評論
]]>
JAVA動態代理學習心得http://www.tkk7.com/liuzheng/articles/143197.html劉錚 劉錚 Thu, 06 Sep 2007 08:18:00 GMThttp://www.tkk7.com/liuzheng/articles/143197.htmlhttp://www.tkk7.com/liuzheng/comments/143197.htmlhttp://www.tkk7.com/liuzheng/articles/143197.html#Feedback0http://www.tkk7.com/liuzheng/comments/commentRss/143197.htmlhttp://www.tkk7.com/liuzheng/services/trackbacks/143197.htmlJAVA動態代理學習心得

1.       所有的額外新添加的方法要放到InvocationHandler的實現類中

2.       Proxy類都與InvocationHandler相聯系的,也就是說Proxy類的中的方法調用都會被重新分配到實例的InvoctionHandler中的invoke方法中,傳遞了reflect中的method

3.       創建Java動態代理類的步驟:

a)       實現InvocationHandler接口

b)      通過Proxy.newProxyInstance得到一個Proxy類的實例

一般的寫法如下:

public class XXXHandler implements InvocationHandler {

private Object originalObject;

public Object bind(Object obj) {

this.originalObject = obj;

return Proxy.newProxyInstance(

obj.getClass().getClassLoader(),

obj.getClass().getInterfaces(),

this)

}

public Object invoke(Object proxy, Method method, Object[] args)

throws Throwable {

method.invoke(originalObject, args);

}

4.       這樣調用XXXHandler:代理目標的接口類= XXXHandler的實例.bind(代理目標的實現類)



劉錚 2007-09-06 16:18 發表評論
]]>
JAVA 學習目標和資料http://www.tkk7.com/liuzheng/articles/139100.html劉錚 劉錚 Fri, 24 Aug 2007 06:48:00 GMThttp://www.tkk7.com/liuzheng/articles/139100.htmlhttp://www.tkk7.com/liuzheng/comments/139100.htmlhttp://www.tkk7.com/liuzheng/articles/139100.html#Feedback0http://www.tkk7.com/liuzheng/comments/commentRss/139100.htmlhttp://www.tkk7.com/liuzheng/services/trackbacks/139100.html本文將告訴你學習Java需要達到的30個目標,希望能夠對你的學習有所幫助。對比一下自己,你已經掌握了這30條中的多少條了呢?

  1.你需要精通面向對象分析與設計(OOA/OOD)、涉及模式(GOF,J2EEDP)以及綜合模式。你應該十分了解UML,尤其是class,object,interaction以及statediagrams。

  2.你需要學習JAVA語言的基礎知識以及它的核心類庫(collections,serialization,streams,networking, multithreading,reflection,event,handling,NIO,localization,以及其他)。

  3.你應該了解JVM,classloaders,classreflect,以及垃圾回收的基本工作機制等。你應該有能力反編譯一個類文件并且明白一些基本的匯編指令。

  4.如果你將要寫客戶端程序,你需要學習WEB的小應用程序(applet),必需掌握GUI設計的思想和方法,以及桌面程序的SWING,AWT, SWT。你還應該對UI部件的JAVABEAN組件模式有所了解。JAVABEANS也被應用在JSP中以把業務邏輯從表現層中分離出來。

  5.你需要學習java數據庫技術,如JDBCAPI并且會使用至少一種persistence/ORM構架,例如Hibernate,JDO, CocoBase,TopLink,InsideLiberator(國產JDO紅工廠軟件)或者iBatis。
  6.你還應該了解對象關系的阻抗失配的含義,以及它是如何影響業務對象的與關系型數據庫的交互,和它的運行結果,還需要掌握不同的數據庫產品運用,比如:oracle,mysql,mssqlserver。

  7.你需要學習JAVA的沙盒安全模式(classloaders,bytecodeverification,managers,policyandpermissions,
codesigning, digitalsignatures,cryptography,certification,Kerberos,以及其他)還有不同的安全/認證 API,例如JAAS(JavaAuthenticationandAuthorizationService),JCE (JavaCryptographyExtension),JSSE(JavaSecureSocketExtension),以及JGSS (JavaGeneralSecurityService)。

  8.你需要學習Servlets,JSP,以及JSTL(StandardTagLibraries)和可以選擇的第三方TagLibraries。

  9.你需要熟悉主流的網頁框架,例如JSF,Struts,Tapestry,Cocoon,WebWork,以及他們下面的涉及模式,如MVC/MODEL2。

  10.你需要學習如何使用及管理WEB服務器,例如tomcat,resin,Jrun,并且知道如何在其基礎上擴展和維護WEB程序。

  11.你需要學習分布式對象以及遠程API,例如RMI和RMI/IIOP。

  12.你需要掌握各種流行中間件技術標準和與java結合實現,比如Tuxedo、CROBA,當然也包括javaEE本身。

  13.你需要學習最少一種的XMLAPI,例如JAXP(JavaAPIforXMLProcessing),JDOM(JavaforXMLDocumentObjectModel),DOM4J,或JAXR(JavaAPIforXMLRegistries)。

  14.你應該學習如何利用JAVAAPI和工具來構建WebService。例如JAX-RPC(JavaAPIforXML/RPC),SAAJ (SOAPwithAttachmentsAPIforJava),JAXB(JavaArchitectureforXMLBinding),JAXM(JavaAPIforXMLMessaging), JAXR(JavaAPIforXMLRegistries),或者JWSDP(JavaWebServicesDeveloperPack)。

  15.你需要學習一門輕量級應用程序框架,例如Spring,PicoContainer,Avalon,以及它們的IoC/DI風格(setter,constructor,interfaceinjection)。

  16.你需要熟悉不同的J2EE技術,例如JNDI(JavaNamingandDirectoryInterface),JMS (JavaMessageService),JTA/JTS(JavaTransactionAPI/JavaTransactionService),JMX (JavaManagementeXtensions),以及JavaMail。

  17.你需要學習企業級JavaBeans(EJB)以及它們的不同組件模式:Stateless/StatefulSessionBeans,EntityBeans(包含Bean- ManagedPersistence[BMP]或者Container-ManagedPersistence[CMP]和它的EJB-QL),或者 Message-DrivenBeans(MDB)。

  18.你需要學習如何管理與配置一個J2EE應用程序服務器,如WebLogic,JBoss等,并且利用它的附加服務,例如簇類,連接池以及分布式處理支援。你還需要了解如何在它上面封裝和配置應用程序并且能夠監控、調整它的性能。

  19.你需要熟悉面向方面的程序設計以及面向屬性的程序設計(這兩個都被很容易混淆的縮寫為AOP),以及他們的主流JAVA規格和執行。例如AspectJ和AspectWerkz。

  20.你需要熟悉對不同有用的API和frame work等來為你服務。例如Log4J(logging/tracing),Quartz (scheduling),JGroups(networkgroupcommunication),JCache(distributedcaching), Lucene(full-textsearch),JakartaCommons等等。

  21.如果你將要對接或者正和舊的系統或者本地平臺,你需要學習JNI (JavaNativeInterface) and JCA (JavaConnectorArchitecture)。

  22.你需要熟悉JINI技術以及與它相關的分布式系統,比如掌握CROBA。

  23.你需要JavaCommunityProcess(JCP)以及他的不同JavaSpecificationRequests(JSRs),例如Portlets(168),JOLAP(69),DataMiningAPI(73),等等。

  24.你應該熟練掌握一種JAVAIDE例如sunOne,netBeans,IntelliJIDEA或者Eclipse。(有些人更喜歡VI或EMACS來編寫文件。隨便你用什么了:)

  25.JAVA(精確的說是有些配置)是冗長的,它需要很多的人工代碼(例如EJB),所以你需要熟悉代碼生成工具,例如XDoclet。

  26.你需要熟悉一種單元測試體系(JNunit),并且學習不同的生成、部署工具(Ant,Maven)。

  27.你需要熟悉一些在JAVA開發中經常用到的軟件工程過程。例如RUP(RationalUnifiedProcess)andAgilemethodologies。

  28.你需要能夠深入了解加熟練操作和配置不同的操作系統,比如GNU/linux,sunsolaris,macOS等,做為跨平臺軟件的開發者。

  29.你還需要緊跟java發展的步伐,比如現在可以深入的學習javaME,以及各種java新規范,技術的運用,如新起的web富客戶端技術。

  30.你必需要對opensource有所了解,因為至少java的很多技術直接是靠開源來驅動發展的,如java3D技術。

posted @ 2007-07-07 15:46 topquan 閱讀(80) | 評論 (0)編輯 收藏

My Favorite Java Site

1.TheServerside.com  依然是地位無可動搖的CCTV1。

2.InfoQ.com Floyd Marinescu 在離開 TSS 后另起爐灶,2006年中最重要推薦。視野不再局限于Java 而是包括Java,.Net, Ruby ,SOA, Agile方法等熱門話題。

3.JDJ的電子雜志 在JDJ首頁的最底處訂閱,文章質量不低于5-7的傳統三強。

4.SWik.net  收集了大量OpenSource Project的資源聚合。其中如Spring,Hibernate的更新度非常高,出現什么和Spring有關的blog,article,project都會馬上被聚合。

5.IBM DeveloperWorks 傳統、穩定的Java文章來源地。

6.JavaWorld 傳統、穩定的Java文章來源地。

7.OnJava  傳統、穩定的Java文章來源地。

8.Artima.com 類似于TSS而略遜,其中Spotlight 文章值得關注,而Java News是聚合了所有其他Java站點的大聚合。

9.JavaLobby  站內的Announcements 是大大小小Java  Project的發布聲明區,Trips and Tricks 有很多的Tips。

10. No Fluff Just Stuff 的Blogs 聚合 一直缺一個所有優秀Java Blogger的rss總聚合,NFJS這里勉強算一個。

11. JBOSS 官方網站 JBOSS被RedHat收購后,在各方面都有所加強,出現了高質量的產品



劉錚 2007-08-24 14:48 發表評論
]]>
主站蜘蛛池模板: 亚洲色偷偷综合亚洲AV伊人蜜桃| 99ri精品国产亚洲| 国产精品亚洲а∨天堂2021| 在线观看人成视频免费| 亚洲综合无码无在线观看| 男人的好看免费观看在线视频| 亚洲天堂一区在线| 久久99九九国产免费看小说| 亚洲色偷偷偷综合网| 亚洲A∨精品一区二区三区| 一级毛片高清免费播放| 亚洲香蕉成人AV网站在线观看 | 日本亚洲欧美色视频在线播放| 精品国产免费观看一区| 四虎国产精品永免费| 亚洲综合亚洲综合网成人| 国产一级片免费看| 亚洲免费闲人蜜桃| 国产免费久久精品| a级黄色毛片免费播放视频| 亚洲av无码av制服另类专区| 69天堂人成无码麻豆免费视频| 亚洲国产AV一区二区三区四区| 亚洲高清视频一视频二视频三| 在线看片免费人成视频久网下载| 久久精品a亚洲国产v高清不卡| 两个人的视频高清在线观看免费| 国产亚洲福利精品一区二区| 亚洲人成人一区二区三区| 色片在线免费观看| 猫咪免费观看人成网站在线| 久久水蜜桃亚洲av无码精品麻豆| 午夜色a大片在线观看免费| 免费无码又爽又刺激高潮软件| 久久精品国产亚洲AV久| 国产精品亚洲美女久久久| 国产三级在线免费| 美女黄频免费网站| 亚洲图片激情小说| 国产亚洲老熟女视频| 手机在线看永久av片免费|