Java Tools

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            83 隨筆 :: 0 文章 :: 16 評論 :: 0 Trackbacks

          #

          這個插件不錯,可以監視JVM的內存使用情況,并且可以強制GC工作。


          Current Version 1.0.0
          . Released Feb 1st 2004

          Description

          Cloudgarden's MemoryManager is a small plugin for IBM's Eclipse Java IDE, which displays the current memory usage of Eclipse (letting you know when Eclipse is close to using up all it's memory allowance), and automatically invokes garbage collection when deemed necessary by a simple but effective algorithm (see below), thus preventing or reducing times of forced inactivity while the Eclipse JVM cleans up it's virtual memory space.

          The plugin takes up little screen real estate, and provides a visual and numerical display of the free, total and maximum memory allocations, as well as indicating when it forced a garbage collection (it also has a button to manually force garbage collection). In the screen shot, the green region represents the free memory, the red region the used memory (which is equal to the total memory minus the free memory) and the black region represents space for expansion. The blue lines indicate when a garbage-collection happened. Scrolling of the display can be paused and re-started, and past values can be stepped through as a simple tool for analysing memory usage by applications in the workbench.

          The plugin is Open Source, (the source is here) and should work on most platforms (it has been tested on Windows, Linux and Mac).


          Download

          The plugin is contained in this file. Simply extract and install in your eclipse folder, then start eclipse.


          Usage

          Show the plugin by choosing "Windows->Views->Other->MemoryManager->Memory" in the eclipse main menu. The plugin will immediately start displaying memory usage and collecting garbage when necessary. Note: If total usage is less than half of the maximum allowed space, the display will be scaled vertically by a factor of two (ie, the height of the display represents only half of the maximum memory), but once the total memory excedes half of the maximum, the height represents the maximum allowed memory usage.


          Requirements

          Eclipse version 2.1.2 or 3M6. If run under a 1.3 JVM, the maximum memory cannot be calculated (since there is no such method in the 1.3 API), and the display will have no black area.


          Garbage collection algorithm


          1) At startup, or immediately after garbage-collection, find the free memory.
          2) Keep checking free memory every second or two.
          3) When the free memory drops below 75% of the free memory after the last garbage collection (or at startup), do another garbage-collection.

          That's it - simple, but apparently effective.
          posted @ 2007-07-05 16:18 和田雨 閱讀(525) | 評論 (0)編輯 收藏

          作者: CNET科技資訊網
          CNETNews.com.cn
          2007-07-04 11:36:28
          關鍵詞: 智能手機 手機 蘋果 iPhone

          CNET科技資訊網7月4日國際報道 蘋果iPhone已經發布有幾天了,iSuppli市場研究公司為了探究其內部硬件零部件的成本,將iPhone進行了拆解。

          1.屏幕

          圖解:蘋果iPhone真機拆解 零部件成本大揭底

          iPhone的屏幕為3.5英寸觸摸屏,480*320像素。除了內存以及觸摸屏元件外,屏幕是iPhone最昂貴的部件。iPhone屏幕的成本達到了24.5美元,按8GB型號的價格計算,這一成本占其零售價格的9.8%。iPhone的觸摸屏由愛普生,夏普以及東芝松下顯示器技術公司生產。

          2.觸摸屏元件

          圖解:蘋果iPhone真機拆解 零部件成本大揭底

          iSuppli測算,iPhone硬件的總體成本分別是,4GB型號為225.85美元,8GB版本為249.85美元。分析師Jagdish Rebello說,;兩種型號的iPhone唯一的區別是閃存的不同。iPhone的觸摸屏元件產自Balda以及TPK Solutions,其成本占8GB型號價格的11%。

          3.電話卡插槽及閃存位置

          圖解:蘋果iPhone真機拆解 零部件成本大揭底

          底部主板。圖示1的位置為電話卡插槽位置,僅能支持AT&T SIM的電話卡,而且目前iPhone僅支持AT&T服務。我們曾經嘗試插入非AT&T SIM電話卡,iPhone顯示不支持這些卡。圖示2處,下面就是一塊8MB多功能閃存。

          4.電路板三明治

          圖解:蘋果iPhone真機拆解 零部件成本大揭底

          iPhone非常薄,經測量僅12毫米厚。iPhone內部的電路板設計體現了蘋果的風格,電路板象被聰明的疊加在了一起,象三明治一樣。為了探究各個部件,你只能分別拆開各層電路板。

          5.鳥瞰圖

          圖解:蘋果iPhone真機拆解 零部件成本大揭底

          圖示1處為程序電路板,圖示3處為無線接口電路板,圖示2處為立體聲耳機電路。雖然iPhone的耳機插孔是標準插孔,但當電路板被組合起來以后,普通的耳機很難插入插孔當中,有些緊。如果你想在iPhone上使用你現在的耳機,你要購買一個10美元的適配器。

          6.無線接口部件

          圖解:蘋果iPhone真機拆解 零部件成本大揭底

          這塊電路板上都是無線通訊部件:(A)處為四頻GSM(GSM850,GSM900,GSM1800以及GSM1900-MHz)/EDGE收發器;(B)處為功率放大器;(C)處為藍牙2.0芯片組;(D)處為無線802.11 a/b/g芯片組;(E)處為基帶芯片組;(F)處為電源管理芯片組。它的成本分別是2.23美元,2.55美元,1.9美元,6美元,11.5美元以及1.4美元。

          7.固定電池

          圖解:蘋果iPhone真機拆解 零部件成本大揭底

          用戶無法拆下iPhone的鋰離子電池。這種電池內置于iPhone當中,與無線接口電路板焊接在一起。蘋果表示,這種電池可以被充電300到400次。如果要想更換電池,你需要將手機送到蘋果,為此,你需要掏79美元外加6.95美元的運輸費。

          蘋果承諾,一旦電池的電量低于原容量的50%,蘋果將更換電池,但iPhone的電量顯示為圖示的,不是數字式的。iPhone電池的成本為5.2美元。

          8.iPhone大腦第一部分

          圖解:蘋果iPhone真機拆解 零部件成本大揭底

          iPhone核心電路。閃存成本在iPhone當中所占的比例很大,4GB的三星NAND閃存成本24美元,8GB的成本為48美元。三星的NAND閃存使用了MLC(多層式儲存單元)技術,雖然這種技術可以比SLC(單層式儲存單元)存儲更多的信息,但耗電量也比后者大。

          9.iPhone大腦第二部分

          圖解:蘋果iPhone真機拆解 零部件成本大揭底

          iPhone核心電路板上還包括運動感應/加速計(圖示B,成本為1.5美元),它可以讓iPhone自動判斷屏幕的方向;C處為24位RGB顯示接口,由National制造,成本為1.3美元;D處為Wolfson Microelectronics生產的音頻解碼器,成本為28.25美元;左上角是iPhone照相機。

          iPhone的核心處理器是620-MHz ARM1176JZF處理器,它有1GBDDR SDRAM內存,三星生產。(還有人說是Marvell生產)

          10.iPhone的相機

          圖解:蘋果iPhone真機拆解 零部件成本大揭底

          iPhone的相機為200萬像素,固定鏡頭模塊,帶CMOS感應器,成本估計為9.5美元。

          posted @ 2007-07-04 12:12 和田雨 閱讀(349) | 評論 (0)編輯 收藏

          對一個簡單的 JDBC 包裝器的擴展及應用

          developerWorks
          文檔選項
          將此頁作為電子郵件發送

          將此頁作為電子郵件發送

          未顯示需要 JavaScript 的文檔選項



          級別: 初級

          宗鋒 (zong_feng@263.net)西北大學計算機系碩士

          2001 年 12 月 16 日

          本文將對 《一個簡單的 JDBC 包裝器》中的JDBC包裝器進行一些擴展,然后介紹一下其在jsp+javabean開發模式中的應用。

          最近看了 《一個簡單的 JDBC 包裝器》,覺得這篇文章很有應用價值,我便在自己的開發中使用了它,不過這個包裝器也存在一些不足,于是我對它進行了一些擴展。首先原文中的Table類缺少刪除功能,我便增加了刪除功能。代碼如下:
          public void delRow(Row row) throws SQLException {
                                  String ss="";
                                  ss = "delete from "+name+" where ";
                                  for (int i=0; i<row.length(); ++i) {
                                  String k = row.getKey( i );
                                  String v = row.get( i );
                                  ss += k+"='"+v+"'";
                                  if (i != row.length()-1)
                                  ss += " and ";
                                  }
                                  Connection con = database.getConnection();
                                  Statement st = con.createStatement();
                                  st.executeUpdate( ss );
                                  }
                                  public void delRow(String conditions)throws SQLException {
                                  String ss="";
                                  ss = "delete from "+name+" where ";
                                  ss +=conditions;
                                  Connection con = database.getConnection();
                                  Statement st = con.createStatement();
                                  st.executeUpdate( ss );
                                  }

          這兩個函數分別用于刪除一個Row和滿足一定條件的記錄。對于具有主關鍵字的表,我們可以用下面代碼中的方法二來進行刪除,如果沒有主關鍵字,我們可以用方法一刪除。

          示例如下:
          //方法一
                                  Row e = table.getRow( "id=2001" );
                                  table.delRow(e);
                                  //方法二
                                  table.delRow("id=2001");
                                  

          另外這個包裝器沒有對查詢結果為NULL的情況作處理,我通過修改Table類的execute函數和RowSet類的get函數對這種情況作了處理。具體代碼見附件。

          下面談談利用這個JDBC包裝器實現對數據庫的封裝,假定我們有一個表:student,創建表的Sql語句如下:
          create table student(
                                  id varchar(10) not null primary key,
                                  name varchar(16) not null,
                                  sex char(2) not null,
                                  password varchar(16) not null,
                                  department varchar(32) not null
                                  )

          我們對這個表進行封裝,下面是Student類的主要代碼:
          public class Student{
                                  private Row r;
                                  public Student() {
                                  r=new Row();
                                  }
                                  public Student(Row row) {
                                  this.r=row;
                                  }
                                  private Table getTable() {
                                  Database db =
                                  new Database( "jdbc:mysql://localhost:3306/manger",
                                  "zf", "zf" );
                                  return db.getTable("student");
                                  }
                                  public void setName(String name){
                                  r.put("name",name);
                                  }
                                  public void setPassword(String pass){
                                  r.put("password",pass);
                                  }
                                  public void setId(String number){
                                  r.put("id",number);
                                  }
                                  public void setDepart(String depart){
                                  r.put("department",depart);
                                  }
                                  public void setSex(String sex){
                                  r.put("sex",sex);
                                  }
                                  public String getName(){
                                  return r.get("name");
                                  }
                                  public String getPassword(){
                                  return r.get("password");
                                  }
                                  public String getId(){
                                  return r.get("id");
                                  }
                                  public String getDepart(){
                                  return r.get("department");
                                  }
                                  public String getSex(){
                                  return r.get("sex");
                                  }
                                  /**
                                  *condition表示限制條件,如果為空,則插入新記錄,否則更新記錄
                                  */
                                  public void save(String conditions) throws SQLException{
                                  if(conditions==null)
                                  {getTable().putRow(r);}
                                  else
                                  getTable().putRow(r,conditions);
                                  }
                                  /**
                                  *由于id作為主關鍵字,所以我們使用字符串為參數的delRow()函數
                                  */
                                  public void delete()throws SQLException{
                                  //getTable().delRow(this.r);
                                  String conditions="";
                                  conditions = "id=" + "'"+getId()+"'";
                                  getTable().delRow(conditions);
                                  }
                                  }

          下面這個類是相應的一個查詢類的主要代碼:
          public class StudentFactory{
                                  public static Student findStudentById(String id)
                                  throws SQLException{
                                  Row r=getTable().getRow("id="+id);
                                  if(r==null)
                                  return null;
                                  else
                                  return new Student(r);
                                  }
                                  public static Student[] findAllStudents()
                                  throws SQLException{
                                  RowSet rs=getTable().getRows("1>0");
                                  if (rs==null)
                                  return null;
                                  else
                                  Student[] stu=null;
                                  stu=new Student[rs.length()];
                                  for(int i=0;i<rs.length(); i++){
                                  stu[i]=new Student(rs.get(i));
                                  }
                                  return stu;
                                  }
                                  }

          我使用javabean來實現很多功能,這樣可以減少在jsp中的java代碼量,方便我們對界面的改進。我們要實現的功能為對學生的編輯,添加,刪除和列表。這些功能定義在兩個javabean中:下面是兩個jsp文件的主要代碼:
          <%-- student.jsp --%>
                                  <%@ page contentType="text/html;charset=GB2312" %>
                                  <%@ page import="org.gjt.mm.mysql.*,manger.bean.*,manger.tools.*,manger.business.*" %>
                                  <html>
                                  <head>
                                  <SCRIPT TYPE="text/javascript" LANGUAGE="JavaScript" >
                                  <!--
                                  function doDelete()
                                  {
                                  if(confirm('你確定刪除嗎?')) {
                                  document.EditForm.event.value='delete';
                                  document.EditForm.submit();
                                  }
                                  }
                                  function doEdit()
                                  {
                                  if(confirm('你確定編輯嗎?')) {
                                  document.EditForm.event.value='showEdit';
                                  document.EditForm.submit();
                                  }
                                  }
                                  function showAddPage()
                                  {
                                  document.location='editstudent.jsp?event=showAdd';
                                  }
                                  </SCRIPT>
                                  </head>
                                  <body bgcolor="#FFFFFF" text="#000000">
                                  <%
                                  try {
                                  Class.forName("org.gjt.mm.mysql.Driver").newInstance();
                                  }
                                  catch (Exception E) {
                                  out.println("Unable to load driver.");
                                  } %>
                                  <jsp:useBean id="table" scope="page" class="manger.bean.ListStudentBean" />
                                  <%
                                  Student[] student=table.getStudent(pageContext);
                                  int total=0;
                                  int currPage=table.getCurPage();
                                  int pageCount=table.getPageCount();
                                  if(student!=null)
                                  {total=student.length;}%>
                                  <FORM NAME="EditForm" ACTION="editstudent.jsp">
                                  <INPUT TYPE="HIDDEN" NAME="event" VALUE="">
                                  <table width="75%" border="1">
                                  <tr>
                                  <td colspan="5">學生列表</td>
                                  </tr>
                                  <tr>
                                  <td>學號</td>
                                  <td>姓名</td>
                                  <td>班級</td>
                                  <td>備注一</td>
                                  <td>選擇</td>
                                  </tr>
                                  <%for (int i=0;i<total;i++){
                                  Student current=student[i];%>
                                  <tr>
                                  <td><%=current.getId()%></td>
                                  <td><%=current.getName()%></td>
                                  <td><%=current.getDepart()%></td>
                                  <td><%=current.getSex() %></td>
                                  <td>
                                  <input type="checkbox" name="id" value=<%=current.getId()%>>
                                  </td>
                                  <% } %>
                                  </tr><tr>
                                  <td colspan="5">
                                  <INPUT TYPE="BUTTON" onclick="doEdit();" VALUE="編輯">
                                  <INPUT TYPE="BUTTON" onclick="showAddPage()" VALUE="增加">
                                  <INPUT TYPE="BUTTON" onclick="doDelete();" VALUE="刪除">
                                  </td>
                                  </tr>
                                  </table>
                                  </form>
                                  </html>

          <%-- studentedit.jsp --%>
                                  <jsp:useBean id="table" scope="page" class="manger.bean.EditStudentBean" />
                                  <%table.processRequest(pageContext);%>
                                  <p>_lt;/p>
                                  <form name="EditForm" action="editstudent.jsp">
                                  <INPUT TYPE="hidden" NAME="event" VALUE="<%=table.getEvent()%>" >
                                  <table width="75%" border="1">
                                  <tr>
                                  <td colspan="2">
                                  <div align="center"><b>編輯學生信息</b></div>
                                  </td>
                                  </tr>
                                  <tr>
                                  <td width="40%">學號:</td>
                                  <td width="60%">
                                  <input type="text" name="id" value="<%=table.getStudent().getId()%>">
                                  </td>
                                  </tr>
                                  <%--下面的一些學生信息我們省略了--%>
                                  <tr>
                                  <td colspan="2">
                                  <input type="submit" name="Submit" value="確定">
                                  </td>
                                  </tr>
                                  </table>
                                  </form>
                                  

          我的想法是在student.jsp中顯示學生列表,從這個頁面可以轉到增加和編輯頁面,也可以在這個頁面中刪除學生,這三個功能都是由editstudent.jsp完成的。在editstudent.jsp中非常關鍵的一行代碼就是<%table.processRequest(pageContext);%>,這會調用EditStudentBean類的processRequest方法:下面是EditStudentBean類processRequest方法和addStudent方法的代碼:
          public void processRequest (PageContext pageContext)
                                  throws SQLException,ServletException,IOException{
                                  this.student.setId("");
                                  this.student.setName("");
                                  this.student.setPassword("");
                                  this.student.setDepart("");
                                  this.student.setSex("");
                                  HttpServletRequest request=(HttpServletRequest)pageContext.getRequest();
                                  String event=request.getParameter("event");
                                  //this.event=event;
                                  if(event==null || event.equals("")||event.equals("showAdd"))
                                  {this.event="add";
                                  }
                                  else if(event.equals("showEdit"))
                                  {this.event="edit";
                                  String id=request.getParameter("id");
                                  Student stu=StudentFactory.findStudentById(id);
                                  this.student=stu;
                                  }
                                  else if(event.equals("add"))
                                  {this.addStudent(pageContext);
                                  }
                                  else if(event.equals("delete"))
                                  {this.deleteStudent(pageContext);
                                  }
                                  else if(event.equals("edit"))
                                  {this.editStudent(pageContext);
                                  }
                                  }
                                  public void addStudent(PageContext page)
                                  throws SQLException,ServletException,IOException{
                                  HttpServletRequest request=(HttpServletRequest)page.getRequest();
                                  HttpServletResponse response=(HttpServletResponse)page.getResponse();
                                  JspWriter out=page.getOut();
                                  String id=request.getParameter("id");
                                  String name=request.getParameter("name");
                                  String sex=request.getParameter("sex");
                                  String pass=request.getParameter("password");
                                  String depart=request.getParameter("depart");
                                  if (id!=null&&name!=null&&sex!=null&&pass!=null&&depart!=null
                                  &&!id.equals("")&&!name.equals("")&&!sex.equals("")&&!pass.equals("")&&!depart.equals(""))
                                  {Student s=new Student();
                                  s.setId(id);
                                  s.setName(name);
                                  s.setSex(sex);
                                  s.setPassword(pass);
                                  s.setDepart(depart);
                                  s.save(null);
                                  response.sendRedirect("student.jsp");
                                  }
                                  else
                                  {out.print("請添完所有信息");
                                  }
                                  }

          processRequest方法的功能主要為獲取提交給editstudent.jsp的event的值,根據不同的event調用不同的函數。例如event=add,則調用addStudent函數。

          注意:在設置Student對象的各個屬性值時,一定要按照表(見上面的SQL語句)中順序來設置,例如在表中,id字段在name字段的前面,所以setId方法在setName方法前面,這主要是由于Table類中的putRow函數中執行插入操作的SQL語句有缺陷。在那個SQL語句中,它是按各屬性在Row中的順序來執行插入操作的。如過你不愿意按表中字段的順序來設置Student的屬性,可以對putRow函數更改如下:
          String ss = "";
                                  if (conditions==null) {
                                  ss = "INSERT INTO "+name+'(";
                                  for (int i=0;i<row.length();++i) {
                                  String k = row.getKey( i );
                                  ss += k;
                                  if (i != row.length()-1)
                                  ss += ", ";
                                  }
                                  ss +=") VALUES (";
                                  for (int j=0; j<row.length(); ++j) {
                                  String v = row.get( j );
                                  ss += "'"+v+"'";
                                  if (j != row.length()-1)
                                  ss += ", ";
                                  }
                                  ss += ")";
                                  }

          限于篇幅,我省略了student.jsp文件中的一些內容,對這個jsp文件進行處理的bean為ListStudentBean,這個類主要實現一個函數getStudent,這個函數主要通過StudentFactory查尋到所有的學生,由于我在此函數中進行了分頁處理,因此此函數只返回當前頁需要的Student數組。具體的代碼參看 附件

          總之,我改進之后的這個JDBC封裝器還是比較有效的,它能滿足一般地需要,當然你也可以根據你的情況對其進行擴充,以更好地適應你的開發。

          代碼在Tomcat4.01+mysql3.23.43下測試通過。



          關于作者

           

          宗鋒,男,西北大學計算機系碩士。興趣主要集中在:java,linux,enhydra,barracuda。希望能與有共同愛好的朋友進行交流。E-mail: zong_feng@263.net.

          posted @ 2007-07-03 17:22 和田雨 閱讀(221) | 評論 (0)編輯 收藏

          一個簡單的 JDBC 包裝器

          一種簡單程序的快速數據訪問解決方案

          developerWorks
          文檔選項
          將此頁作為電子郵件發送

          將此頁作為電子郵件發送

          未顯示需要 JavaScript 的文檔選項



          級別: 初級

          Greg Travis (mito@panix.com), 自由程序員

          2001 年 8 月 04 日

          JDBC 提供了一種強大、全面的接口用來從 Java 程序訪問數據庫。對于較小的項目來說,使用 JDBC 似乎是理所當然的,它使一些程序員避免了一起使用數據庫。本文描述了一種簡單的包裝器庫,它讓使用簡單的數據庫易如反掌。您會發現您已經開始想在編寫的每一個程序中都使用 JDBC。

          事情發生得很突然。您正在修改一個程序,您原以為它是個小程序,不料竟發現它已經迅速增長成為一個龐大的東西 ― 一個 項目― 而現在您已到了需要保存一些數據的時候了。

          問題是,您并不是有意讓程序發展到這種程度的。 您只是不由自主地做了一小部分修改。您并沒有真正準備要存儲或裝入。而且,您的程序是個 applet,而 applet 是無法存儲或裝入的。

          可以存儲或裝入它們嗎?

          實際上,JDBC API 允許任何 Java 程序 ― 甚至是 applet ― 連接到關系型數據庫(RDBMS)上。 不幸的是,JDBC 對您的小程序來說可能有一點頭重腳輕了。畢竟,如果您希望做的只是存儲一點點數據的話,RDBMS 是一個強大、復雜的系統。

          在本文中,我們將分析一個簡單的使用 JDBC 的抽象層。對于簡單的應用程序來說,它可以讓您只用幾行代碼就實現存儲和裝入結構化數據。它不會處理復雜的 RDBMS 使用,但那正是我們要避免的,不是嗎?

          JDBC 的復雜性

          JDBC 使用起來可能是一個復雜的 API。它不僅必須支持整個強大的 SQL 標準,還必須很好地隱藏不同數據庫引擎之間的區別。

          JDBC 的復雜還在于關系型數據庫是基于 SQL 構建的,而 SQL 是要給人用的,而不是給程序用的。直接使用 JDBC 有點象同時用兩種語言編程。

          雖然 JDBC 的這些方面并不是什么壞事,但它們確實與我們的目標 ― 快速地存儲少量數據相沖突。





          回頁首


          簡化的抽象

          我們將創建一個簡單的抽象層,讓您不必顧慮所有繁瑣的細節問題來直接使用 JDBC。如果您早已熟悉 JDBC 或關系型數據庫了,那您一眼看到我們的類列表應該是很熟悉的:

          • Database
          • Table
          • RowSet
          • Row

          我們這里不是在作任何實質性的事情;我們的數據模型本質上和關系型模型是一樣的,但去掉了煩人的細節(同時去掉了強大的功能)。每一個類映射到一個基本的 RDBMS 概念上,同時也映射到一個 JDBC 類上。就是這種映射讓我們的 API 可以在保持易用性的同時保留它的相關特性。

          這種 API 的設計是基于對我們的數據存儲需要的設想。如果您發現自己的程序需要一點不同的地方,您可以隨意地改變這種抽象以適應您的情況。 這些類可以被認為是一種簡化您工作的模式,而不是一成不變的規則。

          如果您不熟悉 SQL 或者 RDBMS 技術,不必害怕。 下面的四節中,每一節都會幫助您熟悉我們的一個類,還有這些類映射到的 RDBMS 功能。

          Database 類

          當使用 JDBC 與數據庫建立連接時,您必須告訴 JDBC 在何處可以找到實際的數據。 因為不同的數據庫引擎有不同的訪問方法和描述這些方法的不同語法,所以有不止一種方法來指定數據源。 在 JDBC 中,統一資源標識符(Uniform Resource Identifier,URI)字符串是用來指定數據源的,而這個字符串的結構是依賴于數據庫的。

          Database 類的主要目的就是封裝這個字符串,還有建立連接操作時可能需要的任何用戶名/密碼信息。

          下面是如何創建一個 Database 對象的方法:

            Database db =
                                  new Database( "jdbc:postgresql://localhost/mito",
                                  "mito", "" );
                                  

          構造函數的第一個參數是數據源的 URI。 在這個示例中,我使用了 PostgreSQL數據庫引擎,而且在本機上訪問了一個名為 mito 的數據庫。 另外,我指定我的用戶名 mito 和一個空的密碼分別作為第二個和第三個參數。

          一旦您創建了 Database 對象,您就可以使用它來訪問數據,如我們在下一章可以看到的一樣。

          Table 類

          我們在 API 中對簡化的一個設想就是,當您從表的一行讀取數據時,您會得到整行的數據。換句話說,表的一行是作為讀寫單獨一塊數據的最小單位。這并不十分有效,但效率不是我們方法中所首要考慮的。

          Talbe 類讓您可以讀寫這些行對象。您必須做的第一步是創建一個表對象,它簡單得只要知道它的名稱即可:

            Table table = db.getTable( "employee" );
                                  

          創建 Table 對象的操作實際上并沒有做任何事,只是讓對象記住自己的名稱。要做一些實際的事,我們就需要實際地使用這個 Table 對象了。在這里,我們從表中讀取一行。

            Row row = table.getRow( "id=101");
                                  

          注意,我們已經指定了我們只需要那些‘id’值設定為‘101’的行。通過使用 getRow() 方法,我們假定只有一行符合這個條件。在另外的情況下,我們可能需要多個行,那樣我們就需要這樣使用 getRows() 方法:

            RowSet rows = table.getRows( "id<103" );
                                  

          在這種情況下,返回的值是一個 RowSet ,而不是一個 Row。 RowSet 就是 Row 的一個集合。

          在接下來的兩節里,我們將討論 RowRowSet 類。

          Row 類

          在我們的抽象中, Row 是在 RDBMS 中表示表中一行的名稱/值對的集合。不同于 RDBMS 值可以是不同的類型, Row 僅包含一種類型,即字符串類型。 這還是為了讓事情簡單一點 ― 我們假定您不需要字符串類型提供的任何更強大的功能。

          一旦您有了一個 Row ,就很容易從其中取出一個值,正如我們在清單 1 中看見的一樣。



          L清單 1. 從 Row 中獲取值
            Row e = table.getRow( "id=100" );
                                  String name = e.get( "name" );
                                  System.out.println( "Employee name: "+name );
                                  

          還要注意的是 Row 是排序的,這樣您就可以通過索引來取出名稱/值的對。清單 2 中給出了這樣的一個示例。



          清單 2. 迭代整個 Row
            Row e = table.getRow( "id=100" );
                                  for (int i=0; i<e.length(); ++i) {
                                  String key = e.getKey( i );
                                  String value = e.get( i );
                                  System.out.println( key+" = "+value );
                                  }
                                  

          當然,您還可以改變 Row 中的值。這是在數據庫中更改數據所必需的 — 稍后我們會看到這一點。

          RowSet 類

          記住有一些查詢可以返回多個 Row ,這樣的話您就會得到一個 RowSet 。 RowSet 有一點不同于基于 Vector 的包裝器。你可以輕易地迭代 RowSet 中所有的 Row ,在清單 3 中可以看到這一點。



          清單 3. 迭代整個 RowSet
            RowSet rs = table.get( "id<104" );
                                  for (int i=0; i<rs.length(); ++i) {
                                  Row row = rs.get( i );
                                  // do something with the row....
                                  }
                                  

          一個完整的示例

          現在我們已經看過了所有的類,讓我們來看一個完整的示例吧。在清單 4 中,我們將抽取出符合特定條件的一套記錄,然后打印出它們的值。



          清單 4. 一個讀取數據的完整示例
              // First, get all rows meeting the criterion
                                  RowSet rs = table.getRows( "id<103" );
                                  // Iterate through the set
                                  for (int i=0; i<rs.length(); ++i) {
                                  // Grab each row in turn
                                  Row row = rs.get( i );
                                  // Get and print the value of the "name" field
                                  String name = row.get( "name" );
                                  System.out.println( "Name: "+name );
                                  }
                                  

          如此容易!在下一節中,我們將看看怎樣向數據庫寫入數據。





          回頁首


          修改數據

          正如前面所提到的,使用我們的 API 讀寫數據是以整個 為單位的。為了向數據庫寫入數據,您必須創建(或修改) Row 對象,然后向數據庫寫入那個 Row 對象。

          向數據庫寫入數據是通過使用 Table 中的 putRow 方法。這種方法有兩種變體:

          • public void putRow( Row row )
          • public void putRow( Row row, String conditions )

          這兩種變體分別對應于 SQL 中的 INSERTUPDATE 命令。

          在第一個變體中,寫一行意味著將一個全新的行插入表中。

          在第二個變體中,寫一行意味著修改一個現有的行。 conditions 參數使您能夠指定您想要修改的是哪一行(哪些行)。

          讓我們來看看每種方法的一個示例。

          插入一個新行

          插入一個新行很簡單,因為您不必指定要修改的行(一行或多行)。您只是簡單地把行插入:

            table.putRow( row );
                                  

          您可以重新創建一個 Row ,如清單 5 所示。



          清單 5. 重新創建一個 Row
            // Create an empty row object
                                  Row row = new Row();
                                  // Fill it up with data
                                  row.put( "id", "200" );
                                  row.put( "name", "Joey Capellino" );
                                  

          或者,您可以修改一個以前曾經從數據庫中讀取的一個現有的行,如清單 6 所示。



          清單 6. 修改現有的 Row
            // Grab a row from the database
                                  Row row = table.getRow( someConditions );
                                  // Change some or all of the fields
                                  row.put( "name", "Joey Capellino" );
                                  

          雖然通常是在插入時重新創建 Row ,更新時使用現有的 Row ,實際上您可以用任何方式來進行。

          更新現有的行

          正如前面的部分提到的,對于您如何 創建用來更新的 Row 是沒有限制的。 但是,通常您是使用一個剛從數據庫中讀出的 Row 。

          為了詳細描述這一點,我們將使用一個示例(在該例子中我們讀出一個員工的姓名),改變這個名字,然后將更改后的結果寫回數據庫,如清單 7 所示。



          清單 7. 通過修改 Row 進行更新
              Row row = table.getRow( "id=104" );
                                  row.put( "name", newName );
                                  table.putRow( row, "id=104" );
                                  

          注意我們必須在調用 putRow() 中指定條件。這樣才會使調用成為 更新,而不是 插入

          注意,這個調用將更新 所有符合條件的行,而不是其中的一行。





          回頁首


          結論

          在本文中,我們初步認識了一種通過 JDBC 包提供一種簡化的通往關系型數據庫接口的 API。這種抽象保留了 JDBC 接口的很多基本關系型功能,但對其進行了簡化,從而讓使用非常地方便。這種簡化是以效率為代價的,但當目標是簡單性時,這并不是一個令人驚奇的結果。



          參考資料



          關于作者

           

          Greg Travis 是居住在紐約的一個自由程序員。他對計算機的興趣可能是源于“ Bionic Woman”中的一段情節 - Jamie 四處奔跑,試圖逃離一所燈光和門都被邪惡的人工智能所控制的房子,人工智能還通過揚聲器嘲笑她。他是一個傳統觀念的虔誠信徒 - 如果一個計算機程序能夠工作,那完全是個巧合??梢酝ㄟ^ mito@panix.com與他聯系。

          posted @ 2007-07-03 17:16 和田雨 閱讀(233) | 評論 (0)編輯 收藏

               摘要: 本文通過開發一個JSP 編輯器插件的示例,介紹了 Eclipse 中設置 JSP 斷點的方法,以及如何遠程調試 JSP。作為基礎知識,本文的前兩部分描述了 JAVA Debug 和 JSR-45 的基本原理。
            閱讀全文
          posted @ 2007-07-03 16:40 和田雨 閱讀(413) | 評論 (0)編輯 收藏

          JavaBeans的屬性 

          JavaBeans的屬性與一般Java程序中所指的屬性,或者說與所有面向對象的程序設計語言中對象的屬性是一個概念,在程序中的具體體現就是類中的變量。在JavaBeans設計中,按照屬性的不同作用又細分為四類:Simple, Index, Bound與Constrained屬性。 

          1. Simple屬性 

          一個簡單屬性表示一個伴隨有一對get/set方法(C語言的過程或函數在Java程序中稱為"方法")的變量。屬性名與和該屬性相關的get/set方法名對應。例如:如果有setX和getX方法,則暗指有一個名為"X"的屬性。如果有一個方法名為isX,則通常暗指"X"是一個布爾屬性(即X的值為true或false)。例如在下面這個程序中: 


          public class alden1 extends Canvas { 
          string ourString= "Hello"; //屬性名為ourString,類型為字符串 
          public alden1(){     //alden1()是alden1的構造函數, 
          與C++中構造函數的意義相同 
          setBackground(Color.red); 
          setForeground(Color.blue); 

          /* "set"屬性*/ 
          public void setString(String newString) { 
          ourString=newString; 

          /* "get"屬性 */ 
          public String getString() { 
          return ourString; 






          2. Indexed屬性 

          一個Indexed屬性表示一個數組值。使用與該屬性對應的set/get方法可取得數組中的數值。該屬性也可一次設置或取得整個數組的值。例: 


          public class alden2 extends Canvas { 
          int[] dataSet={1,2,3,4,5,6}; // dataSet是一個indexed屬性 
          public alden2() { 
          setBackground(Color.red); 
          setForeground(Color.blue); 

          /* 設置整個數組 */ 
          public void setDataSet(int[] x){ 
          dataSet=x; 

          /* 設置數組中的單個元素值 */ 
          public void setDataSet(int index, int x){ 
          dataSet[index]=x; 

          /* 取得整個數組值 */ 
          public int[] getDataSet(){ 
          return dataSet; 

          /* 取得數組中的指定元素值 */ 
          public int getDataSet(int x){ 
          return dataSet[x]; 






          3. Bound屬性 

          一個Bound屬性是指當該種屬性的值發生變化時,要通知其它的對象。每次屬性值改變時,這種屬性就點火一個PropertyChange事件(在Java程序中,事件也是一個對象)。事件中封裝了屬性名、屬性的原值、屬性變化后的新值。這種事件是傳遞到其它的Beans,至于接收事件的Beans應做什么動作由其自己定義。當PushButton的background屬性與Dialog的background屬性bind時,若PushButton的background屬性發生變化時,Dialog的background屬性也發生同樣的變化。 例: 


          public class alden3 extends Canvas{ 
          String ourString= "Hello"; 
          //ourString是一個bound屬性 
          private PropertyChangeSupport changes = new PropertyChangeSupport(this); 
          /** 注:Java是純面向對象的語言, 
          如果要使用某種方法則必須指明是要使用哪個對象的方法, 
          在下面的程序中要進行點火事件的操作, 
          這種操作所使用的方法是在PropertyChangeSupport類中的。 
          所以上面聲明并實例化了一個changes對象, 
          在下面將使用changes的firePropertyChange方法來點火ourString的屬性改變事件。*/ 

          public void setString(string newString){ 
          String oldString = ourString; 
          ourString = newString; 
          /* ourString的屬性值已發生變化,于是接著點火屬性改變事件 */ 
          changes.firePropertyChange("ourString",oldString,newString); 

          public String getString(){ 
          return ourString; 

          /** 以下代碼是為開發工具所使用的。 
          我們不能預知alden3將與哪些其它的Beans組合成為一個應用, 
          無法預知若alden3的ourString屬性發生變化時有哪些其它的組件與此變化有關, 
          因而alden3這個Beans要預留出一些接口給開發工具, 
          開發工具使用這些接口, 
          把其它的JavaBeans對象與alden3掛接。*/ 

          public void addPropertyChangeListener(PropertyChangeLisener l){ 
          changes.addPropertyChangeListener(l); 

          public void removePropertyChangeListener(PropertyChangeListener l){ 
          changes.removePropertyChangeListener(l); 





          通過上面的代碼, 

          開發工具調用changes的addPropertyChangeListener方法 

          把其它JavaBeans注冊入ourString屬性的監聽者隊列l中, 

          l是一個Vector數組,可存儲任何Java對象。 

          開發工具也可使用changes的removePropertyChangeListener方法, 

          從l中注銷指定的對象, 

          使alden3的ourString屬性的改變不再與這個對象有關。 

          當然,當程序員手寫代碼編制程序時, 

          也可直接調用這兩個方法, 

          把其它Java對象與alden3掛接。 

          4. Constrained屬性 

          一個JavaBeans的constrained屬性,是指當這個屬性的值要發生變化時,與這個屬性已建立了某種連接的其它Java對象可否決屬性值的改變。constrained屬性的監聽者通過拋出PropertyVetoException來阻止該屬性值的改變。例:下面程序中的constrained屬性是PriceInCents。 


          public class JellyBeans extends Canvas{ 
          private PropertyChangeSupport changes=new PropertyChangeSupport(this); 
          private VetoableChangeSupport Vetos=new VetoableChangeSupport(this); 
          /*與前述changes相同, 
          可使用VetoableChangeSupport對象的實例Vetos中的方法, 
          在特定條件下來阻止PriceInCents值的改變。*/ 


          ...... 
          public void setPriceInCents(int newPriceInCents) throws PropertyVetoException { 
          /*方法名中throws PropertyVetoException的作用是當有 
          其它Java對象否決PriceInCents的改變時, 
          要拋出例外。*/ 
          /* 先保存原來的屬性值*/ 

          int oldPriceInCents=ourPriceInCents; 
          /**點火屬性改變否決事件*/ 
          vetos.fireVetoableChange("priceInCents",new Integer(OldPriceInCents), 
          new Integer(newPriceInCents)); 

          /**若有其它對象否決priceInCents的改變, 
          則程序拋出例外,不再繼續執行下面的兩條語句, 
          方法結束。若無其它對象否決priceInCents的改變, 
          則在下面的代碼中把ourPriceIncents賦予新值, 
          并點火屬性改變事件*/ 

          ourPriceInCents=newPriceInCents; 
          changes.firePropertyChange("priceInCents", 
          new Integer(oldPriceInCents), 
          new Integer(newPriceInCents)); 


          /**與前述changes相同, 
          也要為PriceInCents屬性預留接口, 
          使其它對象可注冊入PriceInCents否決改變監聽者隊列中, 
          或把該對象從中注銷 

          public void addVetoableChangeListener(VetoableChangeListener l) 
          { vetos.addVetoableChangeListener(l); 

          public void removeVetoableChangeListener(VetoableChangeListener l){ 
          vetos.removeVetoableChangeListener(l); 

          ...... 





          從上面的例子中可看到,一個constrained屬性有兩種監聽者:屬性變化監聽者和否決屬性改變的監聽者。否決屬性改變的監聽者在自己的對象代碼中有相應的控制語句,在監聽到有constrained屬性要發生變化時,在控制語句中判斷是否應否決這個屬性值的改變。 

          總之,某個Beans的constrained屬性值可否改變取決于其它的Beans或者是Java對象是否允許這種改變。允許與否的條件由其它的Beans或Java對象在自己的類中進行定義。 

          JavaBeans的事件 

          事件處理是JavaBeans體系結構的核心之一。通過事件處理機制,可讓一些組件作為事件源,發出可被描述環境或其它組件接收的事件。這樣,不同的組件就可在構造工具內組合在一起,組件之間通過事件的傳遞進行通信,構成一個應用。從概念上講,事件是一種在"源對象"和"監聽者對象"之間,某種狀態發生變化的傳遞機制。事件有許多不同的用途,例如在Windows系統中常要處理的鼠標事件、窗口邊界改變事件、鍵盤事件等。在Java和JavaBeans中則是定義了一個一般的、可擴充的事件機制,這種機制能夠: 

          對事件類型和傳遞的模型的定義和擴充提供一個公共框架,并適合于廣泛的應用。 

          與Java語言和環境有較高的集成度。 

          事件能被描述環境捕獲和點火。 

          能使其它構造工具采取某種技術在設計時直接控制事件,以及事件源和事件監聽者之間的聯系。 

          事件機制本身不依賴于復雜的開發工具。特別地,還應當: 

          能夠發現指定的對象類可以生成的事件。 

          能夠發現指定的對象類可以觀察(監聽)到的事件。 

          提供一個常規的注冊機制,允許動態操縱事件源與事件監聽者之間的關系。 

          不需要其它的虛擬機和語言即可實現。 

          事件源與監聽者之間可進行高效的事件傳遞。 

          能完成JavaBeans事件模型與相關的其它組件體系結構事件模型的中立映射。 

          JavaBeans事件模型的主要構成有: 事件從事件源到監聽者的傳遞是通過對目標監聽者對象的Java方法調用進行的。對每個明確的事件的發生,都相應地定義一個明確的Java方法。這些方法都集中定義在事件監聽者(EventListener)接口中,這個接口要繼承java.util.EventListener。實現了事件監聽者接口中一些或全部方法的類就是事件監聽者。 伴隨著事件的發生,相應的狀態通常都封裝在事件狀態對象中,該對象必須繼承自java.util.EventObject。事件狀態對象作為單參傳遞給應響應該事件的監聽者方法中。 發出某種特定事件的事件源的標識是:遵從規定的設計格式為事件監聽者定義注冊方法,并接受對指定事件監聽者接口實例的引用。 有時,事件監聽者不能直接實現事件監聽者接口,或者還有其它的額外動作時,就要在一個源與其它一個或多個監聽者之間插入一個事件適配器類的實例,來建立它們之間的聯系。 

          事件狀態對象(Event State Object) 

          與事件發生有關的狀態信息一般都封裝在一個事件狀態對象中,這種對象是java.util.EventObject的子類。按設計習慣,這種事件狀態對象類的名應以Event結尾。例如: 


          public class MouseMovedExampleEvent extends java.util.EventObject 

          { protected int x, y; 
          /* 創建一個鼠標移動事件MouseMovedExampleEvent */ 
            MouseMovedExampleEvent(java.awt.Component source, Point location) { 
          super(source); 
          x = location.x; 
          y = location.y; 

          /* 獲取鼠標位置*/ 
          public Point getLocation() { 
          return new Point(x, y); 
          }} 




          事件監聽者接口(EventListener Interface)與事件監聽者 

          由于Java事件模型是基于方法調用,因而需要一個定義并組織事件操縱方法的方式。JavaBeans中,事件操縱方法都被定義在繼承了java.util.EventListener類的EventListener接口中,按規定,EventListener接口的命名要以Listener結尾。任何一個類如果想操縱在EventListener接口中定義的方法都必須以實現這個接口方式進行。這個類也就是事件監聽者。例如: 


          /*先定義了一個鼠標移動事件對象*/ 
             public class MouseMovedExampleEvent 
          extends java.util.EventObject { 
          // 在此類中包含了與鼠標移動事件有關的狀態信息 
               ... 
             } 
             /*定義了鼠標移動事件的監聽者接口*/ 
             interface MouseMovedExampleListener 
          extends java.util.EventListener { 
          /*在這個接口中定義了鼠標移動事件監聽者所應支持的方法*/ 
          void mouseMoved(MouseMovedExampleEvent mme); 


          在接口中只定義方法名, 
          方法的參數和返回值類型。 
          如:上面接口中的mouseMoved方法的 
          具體實現是在下面的ArbitraryObject類中定義的。 

          class ArbitraryObject implements MouseMovedExampleListener { 
              public void mouseMoved(MouseMovedExampleEvent mme) 
            { ... } 
          } 
          ArbitraryObject就是MouseMovedExampleEvent事件的監聽者。 




          事件監聽者的注冊與注銷 

          為了各種可能的事件監聽者把自己注冊入合適的事件源中,建立源與事件監聽者間的事件流,事件源必須為事件監聽者提供注冊和注銷的方法。在前面的bound屬性介紹中已看到了這種使用過程,在實際中,事件監聽者的注冊和注銷要使用標準的設計格式: 


          public void add< ListenerType>(< ListenerType> listener); 
          public void remove< ListenerType>(< ListenerType> listener); 




          例如: 

          首先定義了一個事件監聽者接口: 


          public interface 
          ModelChangedListener extends java.util.EventListener { 
          void modelChanged(EventObject e); 





          接著定義事件源類: 


          public abstract class Model { 
          private Vector listeners = new Vector(); // 定義了一個儲存事件監聽者的數組 

          /*上面設計格式中的< ListenerType>在此處即是下面的ModelChangedListener*/ 

          public synchronized void addModelChangedListener(ModelChangedListener mcl) 
             { listeners.addElement(mcl); }//把監聽者注冊入listeners數組中 
          public synchronized void removeModelChangedListener(ModelChangedListener mcl) 
               { listeners.removeElement(mcl); //把監聽者從listeners中注銷 
               } 
             /*以上兩個方法的前面均冠以synchronized, 
          是因為運行在多線程環境時, 
          可能同時有幾個對象同時要進行注冊和注銷操作, 
          使用synchronized來確保它們之間的同步。 
          開發工具或程序員使用這兩個方法建立源與監聽者之間的事件流*/ 

          protected void notifyModelChanged() { 
          /**事件源使用本方法通知監聽者發生了modelChanged事件*/ 
              Vector l; 
               EventObject e = new EventObject(this); 
          /* 首先要把監聽者拷貝到l數組中, 
          凍結EventListeners的狀態以傳遞事件。 
          這樣來確保在事件傳遞到所有監聽者之前, 
          已接收了事件的目標監聽者的對應方法暫不生效。*/ 
               synchronized(this) { 
                 l = (Vector)listeners.clone(); 
               } 
               for (int i = 0; i < l.size(); i++) { 
               /* 依次通知注冊在監聽者隊列中的每個監聽者發生了modelChanged事件, 
               并把事件狀態對象e作為參數傳遞給監聽者隊列中的每個監聽者*/ 
          ((ModelChangedListener)l.elementAt(i)).modelChanged(e); 
               } 
              } 
              } 




          在程序中可見事件源Model類顯式地調用了接口中的modelChanged方法,實際是把事件狀態對象e作為參數,傳遞給了監聽者類中的modelChanged方法。 

          適配類 

          適配類是Java事件模型中極其重要的一部分。在一些應用場合,事件從源到監聽者之間的傳遞要通過適配類來"轉發"。例如:當事件源發出一個事件,而有幾個事件監聽者對象都可接收該事件,但只有指定對象做出反應時,就要在事件源與事件監聽者之間插入一個事件適配器類,由適配器類來指定事件應該是由哪些監聽者來響應。 

          適配類成為了事件監聽者,事件源實際是把適配類作為監聽者注冊入監聽者隊列中,而真正的事件響應者并未在監聽者隊列中,事件響應者應做的動作由適配類決定。目前絕大多數的開發工具在生成代碼時,事件處理都是通過適配類來進行的。 

          JavaBeans用戶化 

          JavaBeans開發者可以給一個Beans添加用戶化器(Customizer)、屬性編輯器(PropertyEditor)和BeansInfo接口來描述一個Beans的內容,Beans的使用者可在構造環境中通過與Beans附帶在一起的這些信息來用戶化Beans的外觀和應做的動作。一個Beans不必都有BeansCustomizer、PrpertyEditor和BeansInfo,根據實際情況,這些是可選的,當有些Beans較復雜時,就要提供這些信息,以Wizard的方式使Beans的使用者能夠用戶化一個Beans。有些簡單的Beans可能這些信息都沒有,則構造工具可使用自帶的透視裝置,透視出Beans的內容,并把信息顯示到標準的屬性表或事件表中供使用者用戶化Beans,前幾節提到的Beans的屬性、方法和事件名要以一定的格式命名,主要的作用就是供開發工具對Beans進行透視。當然也是給程序員在手寫程序中使用Beans提供方便,使他能觀其名、知其意。 

          用戶化器接口(Customizer Interface) 

          當一個Beans有了自己的用戶化器時,在構造工具內就可展現出自己的屬性表。在定義用戶化器時必須要實現java.Beanss.Customizer接口。例如,下面是一個"按鈕"Beans的用戶化一器: 


          public class OurButtonCustomizer 
          extends Panel implements Customizer { 
          ... ... 
          /*當實現象OurButtonCustomizer這樣的常規屬性表時, 
          一定要在其中實現addProperChangeListener 
          和removePropertyChangeListener,這樣, 
          構造工具可用這些功能代碼為屬性事件添加監聽者。*/ 
          ... ... 
          private PropertyChangeSupport changes=new PropertyChangeSupport(this); 
          public void addPropertyChangeListener(PropertyChangeListener l) { 
          changes.addPropertyChangeListener(l); 
          public void removePropertyChangeListener(PropertyChangeListener l) { 
          changes.removePropertyChangeListener(l); 

          ... ... 




          屬性編輯器接口(PropertyEditor Interface) 

          一個JavaBeans可提供PropertyEditor類,為指定的屬性創建一個編輯器。這個類必須繼承自java.Beanss.PropertyEditorSupport類。構造工具與手寫代碼的程序員不直接使用這個類,而是在下一小節的BeansInfo中實例化并調用這個類。例: 


          public class MoleculeNameEditor extends java.Beanss.PropertyEditorSupport { 
          public String[] getTags() { 
          String resule[]={ 
          "HyaluronicAcid","Benzene","buckmisterfullerine", 
          "cyclohexane","ethane","water"}; 
          return resule;} 





          上例中是為Tags屬性創建了屬性編輯器,在構造工具內,可從下拉表格中選擇MoleculeName的屬性應是"HyaluronicAid"或是"water"。 

          BeansInfo接口 

          每個Beans類也可能有與之相關的BeansInfo類,在其中描述了這個Beans在構造工具內出現時的外觀。BeansInfo中可定義屬性、方法、事件,顯示它們的名稱,提供簡單的幫助說明。 例如: 


          public class MoleculeBeansInfo extends SimpleBeansInfo { 
          public PropertyDescriptor[] getPropertyDescriptors() { 
          try { 
          PropertyDescriptor pd=new PropertyDescriptor("moleculeName",Molecule.class); 
          /*通過pd引用了上一節的MoleculeNameEditor類,取得并返回moleculeName屬性*/ 
          pd.setPropertyEditorClass(MoleculeNameEditor.class); 
          PropertyDescriptor result[]={pd}; 
          return result; 
          } catch(Exception ex) { 
          System.err.println("MoleculeBeansInfo: unexpected exeption: "+ex); 
          return null; 







          JavaBeans持久化 

          當一個JavaBeans在構造工具內被用戶化,并與其它Beans建立連接之后,它的所有狀態都應當可被保存,下一次被load進構造工具內或在運行時,就應當是上一次修改完的信息。為了能做到這一點,要把Beans的某些字段的信息保存下來,在定義Beans時要使它實現java.io.Serializable接口。例如: 

          public class Button 
          implements java.io.Serializable { 




          實現了序列化接口的Beans中字段的信息將被自動保存。若不想保存某些字段的信息則可在這些字段前冠以transient或static關鍵字,transient和static變量的信息是不可被保存的。通常,一個Beans所有公開出來的屬性都應當是被保存的,也可有選擇地保存內部狀態。 Beans開發者在修改軟件時,可以添加字段,移走對其它類的引用,改變一個字段的private/protected/public狀態,這些都不影響類的存儲結構關系。然而,當從類中刪除一個字段,改變一個變量在類體系中的位置,把某個字段改成transient/static,或原來是transient/static,現改為別的特性時,都將引起存儲關系的變化。 

          JavaBeans的存儲格式 

          JavaBeans組件被設計出來后,一般是以擴展名為jar的Zip格式文件存儲,在jar中包含與JavaBeans有關的信息,并以MANIFEST文件指定其中的哪些類是JavaBeans。以jar文件存儲的JavaBeans在網絡中傳送時極大地減少了數據的傳輸數量,并把JavaBeans運行時所需要的一些資源捆綁在一起,本章主要論述了JavaBeans的一些內部特性及其常規設計方法,參考的是JavaBeans規范1.0A版本。隨著世界各大ISV對JavaBeans越來越多的支持,規范在一些細節上還在不斷演化,但基本框架不會再有大的變動。
          posted @ 2007-07-03 15:14 和田雨 閱讀(248) | 評論 (0)編輯 收藏

          一個讀取xml文件內容的類 
          package project.util.xml;

          import java.io.*;
          import java.util.*;
          import javax.servlet.http.*;
          import org.apache.log4j.*;
          import org.jdom.*;
          import org.jdom.input.*;

          /**
          * <p>Title: <font color="steelblue" size="10">讀取xml文件信息</font></p>
          * <p>Description: <font color="steelblue">從XML配置文件中獲得配置信息。excerpt form jdom。</font></p>
          * <p>Copyright: <font color="steelblue">Copyright (c) 2004</font></p>
          * <p>Company: <font color="steelblue">Harmonious</font></p>
          * @author <font color="steelblue">TZL</font>
          * @version <font color="steelblue">1.0</font>
          */

          public class XMLReader {
          /*
          #設置根的輸出配置,格式為 "info [2004-05-01 22:35:30] [name]logname(b.c) [line] 86 msg-->log信息"
          log4j.rootLogger=DEBUG, rootAppender
          log4j.appender.rootAppender=org.apache.log4j.RollingFileAppender
          log4j.appender.rootAppender.File=e:/MapXtremeSmpl.log
          log4j.appender.rootAppender.MaxFileSize=1000KB
          log4j.appender.rootAppender.layout=org.apache.log4j.PatternLayout
          log4j.appender.rootAppender.layout.ConversionPattern=%-5p [%d{yyyy-mm-dd HH:mm:ss}] [name] %c{2} [line] %L msg--> %m%n
          */
          static public Logger log = Logger.getLogger(XMLReader.class);
          protected Element m_RootElement = null;
          protected String m_webAppPath = null;

          /**
          * <font color="orange">構造函數。</font>
          * @param xmlFile <font color="steelblue">要讀取的配置文件的絕對路徑。</font>
          */
          public XMLReader(String xmlFile) {
          m_webAppPath = null;
          try {
          PatternLayout layout = new PatternLayout("%-5p %d{yyyy-MM-dd HH:mm:ss} [name] %c{2} [line] %L [msg] %m%n");
          ConsoleAppender appender = new ConsoleAppender(/*new SimpleLayout(),*/layout, "System.err");
          log.addAppender(appender);

          SAXBuilder builder = new SAXBuilder();
          document.nbspdoc = null;
          doc = builder.build(new FileInputStream(xmlFile));
          m_RootElement = doc.getRootElement();
          }
          catch (IOException ex) {
          log.error("XMLReader構造時出現IO錯誤:" + ex.toString());
          }
          catch (JDOMException ex1) {
          log.error("XMLReader構造時分析XML文件出錯:" + ex1.toString());
          }
          catch (Exception ex) {
          log.error("XMLReader 構造出錯:" + ex.toString());
          }
          }

          /**
          * <font color="orange">構造函數。配置文件必須指定為發布的應用的根目錄下的/XmlConfig/Config.xml。</font>
          * @param servletObj <font color="steelblue">隨便一個HttpServlet對象。</font>
          */
          public XMLReader(HttpServlet servletObj) {
          m_webAppPath = servletObj.getServletContext().getRealPath("/");
          String configFileName = m_webAppPath + "XmlConfig/Config.xml";

          try {
          PatternLayout layout = new PatternLayout("%-5p %d{yyyy-MM-dd HH:mm:ss} [name] %c{2} [line] %L [msg] %m%n");
          ConsoleAppender appender = new ConsoleAppender( /*new SimpleLayout(),*/layout, "System.err");
          log.addAppender(appender);

          SAXBuilder builder = new SAXBuilder();
          document.nbspdoc = null;
          doc = builder.build(new FileInputStream(configFileName));
          m_RootElement = doc.getRootElement();
          }
          catch (IOException ex) {
          log.error("XMLReader構造時出現IO錯誤(/XmlConfig/Config.xml):" + ex.toString());
          }
          catch (JDOMException ex1) {
          log.error("XMLReader構造時分析XML文件出錯(/XmlConfig/Config.xml):" + ex1.toString());
          }
          catch (Exception ex) {
          log.error("XMLReader構造出錯(/XmlConfig/Config.xml):" + ex.toString());
          }
          }

          /**
          * <font color="orange">web應用發布在web服務器的絕對路徑根目錄,最后已經有目錄分割符。</font>
          * @return <font color="tomato">返回web應用發布在web服務器的絕對路徑的根目錄。</font>
          */
          public String getWebAppPath() {
          return m_webAppPath;
          }

          /**
          * <font color="orange">從配置文件中獲得配置信息。</font>
          * @param key <font color="steelblue">要獲取的配置名稱。</font>
          * @param curRootName <font color="steelblue">查找的起始節點名稱,如果為null從根開始查找。</font>
          * @return <font color="tomato">配置的字符串。</font>
          */
          public String getElementvalue(String curRootName, String key) {
          String value = null;
          Element curRoot = getElement(null, curRootName);
          if (null == curRoot) {
          curRoot = m_RootElement;
          }
          Element keyNode = getElement(curRoot, key);
          if (null != keyNode) {
          value = keyNode.getTextTrim();

          }
          return value;
          }

          /**
          * <font color="orange">根據名字獲得節點。廣度遍歷,遞歸調用。</font>
          * @param nodeName <font color="steelblue">節點的名字。</font>
          * @param curRoot <font color="steelblue"> 從開始查找的起始節點,如果為null從根開始查找。</font>
          * @return <font color="tomato">返回從指定節點下找到的第一個節點。如果沒有返回null。</font>
          */
          private Element getElement(Element curRoot, String nodeName) {
          Element retElement = null;

          if (null == nodeName)
          return m_RootElement;

          if (null == curRoot) {
          curRoot = m_RootElement;
          }

          if (null != curRoot) {
          retElement = curRoot.getChild(nodeName);
          if (null == retElement) {
          List nestElements = curRoot.getChildren();
          Iterator iterator = nestElements.iterator();
          while (iterator.hasNext() && null == retElement) {
          retElement = getElement( (Element) iterator.next(), nodeName);
          }
          }
          }

          return retElement;
          }

          /**
          * <font color="orange">獲得指定節點的屬性。</font>
          * @param elementName <font color="steelblue">節點的名稱。</font>
          * @param attName <font color="steelblue">要獲得的屬性的名稱。</font>
          * @return <font color="tomato">要查找的屬性的值。</font>
          */
          public String getElementAtrribute(String elementName, String attName)
          {
          Element el = getElement(null, elementName);
          if (null == el)
          return null;

          return el.getAttributevalue(attName);
          }

          }
          posted @ 2007-07-03 14:55 和田雨 閱讀(453) | 評論 (0)編輯 收藏

          引言
            在做無線項目的時候,與通訊公司的數據通訊有一部分是通過XML交互的,所以必須要動態抓取通訊公司提供的固定的Internet上的數據,便研究了一下如何抓取固定url上的數據,現與大家分享一下。

            類名GetPageCode,有一個方法GetSource,通過屬性傳遞參數,入參控制的是要取得URL的地址,代理服務器的設置及輸出方式的控制,這里大家可以再擴展自己的需要,我這里只提供了兩種方式,一種是直接寫到本地的某個文件中,另外一種就是返回字符串的。類里已經作了比較詳細的注釋,我想大家很容易就看明白了,如果實在不明白, 那就msn上問吧,MSN:yubo@x263.net。

            調用方式:
            #region 測試獲取遠程網頁



          GetPageCode gpc = new GetPageCode();
            gpc.Url="http://ppcode.com";
            gpc.ProxyState=1;//使用代理服務器,0為不使用,設置為1后下面的代理設置才起作用
            gpc.ProxyAddress="http://proxyName.com";//代理服務器地址
            gpc.ProxyPort="80";//代理服務器的端口
            gpc.ProxyAccount="proxy";//代理服務器賬號
            gpc.ProxyPassword="password";//代理服務器密碼
            gpc.ProxyDomain="bqc";//代理服務器域
            gpc.OutFilePath=filePath;//設置輸出文件路徑的地方,如果不設置,則返回字符串
            gpc.GetSource();//處理
            string tempErr=gpc.NoteMessage;//如果出錯,這里會提示
            string tempCode=gpc.OutString;//返回的字符串
            #endregion
            類代碼:
            using System;
            using System.Collections;
            using System.ComponentModel;
            using System.Data;
            using System.Drawing;
            using System.IO;
            using System.Net;
            using System.Text;
            using System.Web;
            namespace Test.Com
            {
             /// <summary>
             /// 功能:取得Internet上的URL頁的源碼
             /// 創建:2004-03-22
             /// 作者:Rexsp MSN:yubo@x263.net
            /// </summary>
             public class GetPageCode
             {
             #region 私有變量
            /// <summary>
            /// 網頁URL地址
            /// </summary>
            private string url=null;
            /// <summary>
            /// 是否使用代碼服務器:0 不使用  1 使用代理服務器
            /// </summary>
            private int proxyState=0;
            /// <summary>
            /// 代理服務器地址
            /// </summary>
            private string proxyAddress=null;
            /// <summary>
            /// 代理服務器端口
            /// </summary>
            private string proxyPort=null;
            /// <summary>
            /// 代理服務器用戶名
            /// </summary>
            private string proxyAccount=null;
            /// <summary>
            /// 代理服務器密碼
            /// </summary>
            private string proxyPassword=null;
            /// <summary>
            /// 代理服務器域
            /// </summary>
            private string proxyDomain=null;
           /// <summary>
            /// 輸出文件路徑
            /// </summary>
            private string outFilePath=null;
            /// <summary>
            /// 輸出的字符串
            /// </summary>
            private string outString=null;
            /// <summary>
            /// 提示信息
            /// </summary>
            private string noteMessage;

            #endregion

            #region 公共屬性
            /// <summary>
            /// 欲讀取的URL地址
            /// </summary>
            public string Url
            {
             get{return url;}
             set{url=value;}
            }
            /// <summary>
            /// 是否使用代理服務器標志
            /// </summary>
            public int ProxyState
            {
             get{return proxyState;}
             set{proxyState=value;}
            }
            /// <summary>
            /// 代理服務器地址
            /// </summary>
            public string ProxyAddress
            {
             get{return proxyAddress;}
             set{proxyAddress=value;}
            }
            /// <summary>

            /// 代理服務器端口
            /// </summary>
            public string ProxyPort
            {
             get{return proxyPort;}
             set{proxyPort=value;}
            }
            /// <summary>
            /// 代理服務器賬號
            /// </summary>
            public string ProxyAccount
            {
             get{return proxyAccount;}
             set{proxyAccount=value;}
            }
            /// <summary>
            /// 代理服務器密碼
            /// </summary>
            public string ProxyPassword
            {
             get{return proxyPassword;}
             set{proxyPassword=value;}
            }
            /// <summary>
            /// 代理服務器域
            /// </summary>
            public string ProxyDomain
            {
             get{return proxyDomain;}
             set{proxyDomain=value;}
            }
            /// <summary>
            /// 輸出文件路徑
            /// </summary>
            public string OutFilePath
            {
             get{return outFilePath;}

            set{outFilePath=value;}
            }
            /// <summary>
            /// 返回的字符串
            /// </summary>
            public string OutString
            {
             get{return outString;}
             
            }
            /// <summary>
            /// 返回提示信息
            /// </summary>
            public string NoteMessage
            {
             get{return noteMessage;}
             
            }
            
            #endregion
            
            #region 構造函數
            public GetPageCode()
            {
            }
            #endregion

            #region 公共方法
            /// <summary>
            /// 讀取指定URL地址,存到指定文件中
            /// </summary>
            public void GetSource() 
            { 
             WebRequest request = WebRequest.Create(this.url);
             //使用代理服務器的處理
             if(this.proxyState==1)
             {
              //默認讀取80端口的數據

              if(this.proxyPort==null)
               this.ProxyPort="80";

              WebProxy myProxy=new WebProxy(); 
              myProxy = (WebProxy)request.Proxy; 
              myProxy.Address = new Uri(this.ProxyAddress+":"+this.ProxyPort); 
              myProxy.Credentials = new NetworkCredential(this.proxyAccount, this.proxyPassword, this.ProxyDomain);
              request.Proxy = myProxy; 
             }
             try
             
             {
              //請求服務
              WebResponse response = request.GetResponse();
              //返回信息
              Stream resStream = response.GetResponseStream(); 
              StreamReader sr = new StreamReader(resStream, System.Text.Encoding.Default);
              string tempCode= sr.ReadToEnd();
              resStream.Close(); 
              sr.Close();

              //如果輸出文件路徑為空,便將得到的內容賦給OutString屬性
              if(this.outFilePath==null)
              {
               this.outString=tempCode;
              }
              else
              {

               FileInfo fi = new FileInfo(this.outFilePath);
               //如果存在文件則先干掉
               if(fi.Exists)
                fi.Delete();
               StreamWriter sw = new StreamWriter(this.outFilePath,true,Encoding.Default);
               sw.Write(tempCode);
               sw.Flush();
               sw.Close();
              }
             }
             catch
             {
              this.noteMessage="出錯了,請檢查網絡是否連通;";
               }

                }
             #endregion

             }
            }
          posted @ 2007-07-03 14:52 和田雨 閱讀(259) | 評論 (0)編輯 收藏

          <%@ page contentType="image/jpeg" import="java.awt.*, 
          java.awt.image.*,java.util.*,javax.imageio.*" %> 
          <% 
          // 在內存中創建圖象 
          int width=60, height=20; 
          BufferedImage image = new BufferedImage(width, height, 
          BufferedImage.TYPE_INT_RGB); 

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

          // 設定背景色 
          g.setColor(new Color(0xDCDCDC)); 
          g.fillRect(0, 0, width, height); 

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

          // 取隨機產生的認證碼(4位數字) 
          String rand = request.getParameter("rand"); 
          rand = rand.substring(0,rand.indexOf(".")); 
          switch(rand.length()) 

          case 1: rand = "000"+rand; break; 
          case 2: rand = "00"+rand; break; 
          case 3: rand = "0"+rand; break; 
          default: rand = rand.substring(0,4); break; 


          // 將認證碼存入SESSION 
          session.setAttribute("rand",rand); 

          // 將認證碼顯示到圖象中 
          g.setColor(Color.black); 
          Integer tempNumber = new Integer(rand); 
          String numberStr = tempNumber.toString(); 

          g.setFont(new Font("Atlantic Inline",Font.PLAIN,18)); 
          String Str = numberStr.substring(0,1); 
          g.drawString(Str,8,17); 

          Str = numberStr.substring(1,2); 
          g.drawString(Str,20,15); 
          Str = numberStr.substring(2,3); 
          g.drawString(Str,35,18); 

          Str = numberStr.substring(3,4); 
          g.drawString(Str,45,15); 

          // 隨機產生88個干擾點,使圖象中的認證碼不易被其它程序探測到 
          Random random = new Random(); 
          for (int i=0;i<20;i++) 

          int x = random.nextInt(width); 
          int y = random.nextInt(height); 
          g.drawOval(x,y,0,0); 


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

          // 輸出圖象到頁面 
          ImageIO.write(image, "JPEG", response.getOutputStream()); 
          %>
          posted @ 2007-07-03 14:51 和田雨 閱讀(227) | 評論 (0)編輯 收藏

          JSP/Servlet 中的漢字編碼問題

          developerWorks
          文檔選項
          將此頁作為電子郵件發送

          將此頁作為電子郵件發送

          未顯示需要 JavaScript 的文檔選項



          級別: 初級

          IBM,

          2001 年 3 月 01 日

          網上就 JSP/Servlet 中 DBCS字符編碼問題有許多優秀的文章和討論,本文對它們作一些整理,并結合IBM WebSphere Application Server3.5(WAS)的解決方法作一些說明,希望它不是多余的。

          問題的起源

          每個國家(或區域)都規定了計算機信息交換用的字符編碼集,如美國的擴展 ASCII碼, 中國的 GB2312-80,日本的 JIS 等,作為該國家/區域內信息處理的基礎,有著統一編碼的重要作用。字符編碼集按長度分為 SBCS(單字節字符集),DBCS(雙字節字符集)兩大類。早期的軟件(尤其是操作系統),為了解決本地字符信息的計算機處理,出現了各種本地化版本(L10N),為了區分,引進了 LANG, Codepage 等概念。但是由于各個本地字符集代碼范圍重疊,相互間信息交換困難;軟件各個本地化版本獨立維護成本較高。因此有必要將本地化工作中的共性抽取出來,作一致處理,將特別的本地化處理內容降低到最少。這也就是所謂的國際化(I18N)。各種語言信息被進一步規范為 Locale 信息。處理的底層字符集變成了幾乎包含了所有字形的 Unicode。

          現在大部分具有國際化特征的軟件核心字符處理都是以 Unicode 為基礎的,在軟件運行時根據當時的 Locale/Lang/Codepage 設置確定相應的本地字符編碼設置,并依此處理本地字符。在處理過程中需要實現 Unicode 和本地字符集的相互轉換,甚或以 Unicode 為中間的兩個不同本地字符集的相互轉換。這種方式在網絡環境下被進一步延伸,任何網絡兩端的字符信息也需要根據字符集的設置轉換成可接受的內容。

          Java 語言內部是用 Unicode 表示字符的,遵守 Unicode V2.0。Java 程序無論是從/往文件系統以字符流讀/寫文件,還是往 URL 連接寫 HTML 信息,或從 URL 連接讀取參數值,都會有字符編碼的轉換。這樣做雖然增加了編程的復雜度,容易引起混淆,但卻是符合國際化的思想的。

          從理論上來說,這些根據字符集設置而進行的字符轉換不應該產生太多問題。而事實是由于應用程序的實際運行環境不同,Unicode 和各個本地字符集的補充、完善,以及系統或應用程序實現的不規范,轉碼時出現的問題時時困擾著程序員和用戶。





          回頁首


          GB2312-80,GBK,GB18030-2000 漢字字符集及 Encoding

          其實解決 JAVA 程序中的漢字編碼問題的方法往往很簡單,但理解其背后的原因,定位問題,還需要了解現有的漢字編碼和編碼轉換。

          GB2312-80 是在國內計算機漢字信息技術發展初始階段制定的,其中包含了大部分常用的一、二級漢字,和 9 區的符號。該字符集是幾乎所有的中文系統和國際化的軟件都支持的中文字符集,這也是最基本的中文字符集。其編碼范圍是高位0xa1-0xfe,低位也是 0xa1-0xfe;漢字從 0xb0a1 開始,結束于 0xf7fe;

          GBK 是 GB2312-80 的擴展,是向上兼容的。它包含了 20902 個漢字,其編碼范圍是 0x8140-0xfefe,剔除高位 0x80 的字位。其所有字符都可以一對一映射到 Unicode 2.0,也就是說 JAVA 實際上提供了 GBK 字符集的支持。這是現階段 Windows 和其它一些中文操作系統的缺省字符集,但并不是所有的國際化軟件都支持該字符集,感覺是他們并不完全知道 GBK 是怎么回事。值得注意的是它不是國家標準,而只是規范。隨著 GB18030-2000國標的發布,它將在不久的將來完成它的歷史使命。

          GB18030-2000(GBK2K) 在 GBK 的基礎上進一步擴展了漢字,增加了藏、蒙等少數民族的字形。GBK2K 從根本上解決了字位不夠,字形不足的問題。它有幾個特點,

          • 它并沒有確定所有的字形,只是規定了編碼范圍,留待以后擴充。
          • 編碼是變長的,其二字節部分與 GBK 兼容;四字節部分是擴充的字形、字位,其編碼范圍是首字節 0x81-0xfe、二字節0x30-0x39、三字節 0x81-0xfe、四字節0x30-0x39。
          • 它的推廣是分階段的,首先要求實現的是能夠完全映射到 Unicode 3.0 標準的所有字形。
          • 它是國家標準,是強制性的。

          現在還沒有任何一個操作系統或軟件實現了 GBK2K 的支持,這是現階段和將來漢化的工作內容。

          Unicode 的介紹......就免了吧。

          JAVA 支持的encoding中與中文編程相關的有:(有幾個在JDK文檔中未列出)

          ASCII 7-bit, 同 ascii7
          ISO8859-1 8-bit, 同 8859_1,ISO-8859-1,ISO_8859-1,latin1...
          GB2312-80 同gb2312,gb2312-1980,EUC_CN,euccn,1381,Cp1381, 1383, Cp1383, ISO2022CN,ISO2022CN_GB......
          GBK (注意大小寫),同MS936
          UTF8 UTF-8
          GB18030 (現在只有IBM JDK1.3.?有支持), 同Cp1392,1392

          JAVA 語言采用Unicode處理字符. 但從另一個角度來說,在java程序中也可以采用非Unicode的轉碼,重要的是保證程序入口和出口的漢字信息不失真。如完全采用ISO-8859-1來處理漢字也能達到正確的結果。網絡上流行的許多解決方法,都屬于這種類型。為了不致引起混淆,本文不對這種方法作討論。





          回頁首


          中文轉碼時'?'、亂碼的由來

          兩個方向轉換都有可能得到錯誤的結果:

          • Unicode-->Byte, 如果目標代碼集不存在對應的代碼,則得到的結果是0x3f.

            如:
            "\u00d6\u00ec\u00e9\u0046\u00bb\u00f9".getBytes("GBK") 的結果是 "?ìéF?ù", Hex 值是3fa8aca8a6463fa8b4.

            仔細看一下上面的結果,你會發現\u00ec被轉換為0xa8ac, \u00e9被轉換為\xa8a6... 它的實際有效位變長了!這是因為GB2312符號區中的一些符號被映射到一些公共的符號編碼,由于這些符號出現在ISO-8859-1或其它一些SBCS字符集中,故它們在Unicode中編碼比較靠前,有一些其有效位只有8位,和漢字的編碼重疊(其實這種映射只是編碼的映射,在顯示時仔細不是一樣的。Unicode 中的符號是單字節寬,漢字中的符號是雙字節寬) . 在Unicode\u00a0--\u00ff 之間這樣的符號有20個。了解這個特征非常重要!由此就不難理解為什么JAVA編程中,漢字編碼的錯誤結果中常常會出現一些亂碼(其實是符號字符), 而不全是'?'字符, 就比如上面的例子。

          • Byte-->Unicode, 如果Byte標識的字符在源代碼集不存在,則得到的結果是0xfffd.

            如:
            Byte ba[] = {(byte)0x81,(byte)0x40,(byte)0xb0,(byte)0xa1}; new String(ba,"gb2312");

            結果是"?啊", hex 值是"\ufffd\u554a". 0x8140 是GBK字符,按GB2312轉換表沒有對應的值,取\ufffd. (請注意:在顯示該uniCode時,因為沒有對應的本地字符,所以也適用上一種情況,顯示為一個"?".)

          實際編程中,JSP/Servlet 程序得到錯誤的漢字信息,往往是這兩個過程的疊加,有時甚至是兩個過程疊加后反復作用的結果.





          回頁首


          JSP/Servlet 漢字編碼問題及在 WAS 中的解決辦法

          4.1 常見的 encoding 問題的現象

          網上常出現的 JSP/Servlet encoding 問題一般都表現在 browser 或應用程序端,如:

          • 瀏覽器中看到的 Jsp/Servlet 頁面中的漢字怎么都成了 ’?’ ?
          • 瀏覽器中看到的 Servlet 頁面中的漢字怎么都成了亂碼?
          • JAVA 應用程序界面中的漢字怎么都成了方塊?
          • Jsp/Servlet 頁面無法顯示 GBK 漢字。
          • JSP 頁面中內嵌在<%...%>,<%=...%>等Tag包含的 JAVA code 中的中文成了亂碼,但頁面的其它漢字是對的。
          • Jsp/Servlet 不能接收 form 提交的漢字。
          • JSP/Servlet 數據庫讀寫無法獲得正確的內容。

          隱藏在這些問題后面的是各種錯誤的字符轉換和處理(除第3個外,是因為 Java font 設置錯誤引起的)。解決類似的字符 encoding 問題,需要了解 Jsp/Servlet 的運行過程,檢查可能出現問題的各個點。

          4.2 JSP/Servlet web 編程時的 encoding 問題

          運行于Java 應用服務器的 JSP/Servlet 為 Browser 提供 HTML 內容,其過程如下圖所示:




          其中有字符編碼轉換的地方有:

          • JSP 編譯。Java 應用服務器將根據 JVM 的 file.encoding 值讀取 JSP 源文件,編譯生成 JAVA 源文件,再根據 file.encoding 值寫回文件系統。如果當前系統語言支持 GBK,那么這時候不會出現 encoding 問題。如果是英文的系統,如 LANG 是 en_US 的 Linux, AIX 或 Solaris,則要將 JVM 的 file.encoding 值置成 GBK 。系統語言如果是 GB2312,則根據需要,確定要不要設置 file.encoding,將 file.encoding 設為 GBK 可以解決潛在的 GBK 字符亂碼問題

          • Java 需要被編譯為 .class 才能在 JVM 中執行,這個過程存在與a.同樣的 file.encoding 問題。從這里開始 servlet 和 jsp 的運行就類似了,只不過 Servlet 的編譯不是自動進行的。對于JSP程序, 對產生的JAVA 中間文件的編譯是自動進行的(在程序中直接調用sun.tools.javac.Main類). 因此如果在這一步出現問題的話, 也要檢查encoding和OS的語言環境,或者將內嵌在JSP JAVA Code 中的靜態漢字轉為 Unicode, 要么靜態文本輸出不要放在 JAVA code 中。對于Servlet, javac 編譯時手工指定-encoding 參數就可以了。

          • Servlet 需要將 HTML 頁面內容轉換為 browser 可接受的 encoding 內容發送出去。依賴于各 JAVA App Server 的實現方式,有的將查詢 Browser 的 accept-charset 和 accept-language 參數或以其它猜的方式確定 encoding 值,有的則不管。因此采用固定encoding 也許是最好的解決方法。對于中文網頁,可在 JSP 或 Servlet 中設置 contentType="text/html; charset=GB2312";如果頁面中有GBK字符,則設置為contentType="text/html; charset=GBK",由于IE 和 Netscape對GBK的支持程度不一樣,作這種設置時需要測試一下。因為16位 JAVA char在網絡傳送時高8位會被丟棄,也為了確保Servlet頁面中的漢字(包括內嵌的和servlet運行過程中得到的)是期望的內碼,可以用 PrintWriter out=res.getWriter() 取代 ServletOutputStream out=res.getOutputStream(). PrinterWriter 將根據contentType中指定的charset作轉換 (ContentType需在此之前指定!); 也可以用OutputStreamWriter封裝 ServletOutputStream 類并用write(String)輸出漢字字符串。對于 JSP,JAVA Application Server 應當能夠確保在這個階段將嵌入的漢字正確傳送出去。

          • 這是解釋 URL 字符 encoding 問題。如果通過 get/post 方式從 browser 返回的參數值中包含漢字信息, servlet 將無法得到正確的值。SUN的 J2SDK 中,HttpUtils.parseName 在解析參數時根本沒有考慮 browser 的語言設置,而是將得到的值按 byte 方式解析。這是網上討論得最多的 encoding 問題。因為這是設計缺陷,只能以 bin 方式重新解析得到的字符串;或者以 hack HttpUtils 類的方式解決。參考文章 2 均有介紹,不過最好將其中的中文 encoding GB2312、 CP1381 都改為 GBK,否則遇到 GBK 漢字時,還是會有問題。 Servlet API 2.3 提供一個新的函數 HttpServeletRequest.setCharacterEncoding 用于在調用 request.getParameter(“param_name”) 前指定應用程序希望的 encoding,這將有助于徹底解決這個問題。

          4.3 IBM Websphere Application Server 中的解決方法

          WebSphere Application Server 對標準的 Servlet API 2.x 作了擴展,提供較好的多語言支持。運行在中文的操作系統中,可以不作任何設置就可以很好地處理漢字。下面的說明只是對WAS是運行在英文的系統中,或者需要有GBK支持時有效。

          上述c,d情況,WAS 都要查詢 Browser 的語言設置,在缺省狀況下, zh, zh-cn 等均被映射為 JAVA encoding CP1381(注意: CP1381 只是等同于 GB2312 的一個 codepage,沒有 GBK 支持)。這樣做我想是因為無法確認 Browser 運行的操作系統是支持GB2312, 還是 GBK,所以取其小。但是實際的應用系統還是要求頁面中出現 GBK 漢字,最著名的是朱總理名字中的“?F"(rong2 ,0xe946,\u9555),所以有時還是需要將 Encoding/Charset 指定為 GBK。當然 WAS 中變更缺省的 encoding 沒有上面說的那么麻煩,針對 a,b,參考文章 5,在 Application Server 的命令行參數中指定 -Dfile.encoding=GBK 即可; 針對 d,在 Application Server 的命令行參數中指定-Ddefault.client.encoding=GBK。如果指定了-Ddefault.client.encoding=GBK,那么c情況下可以不再指定charset。

          上面列出的問題中還有一個關于Tag<%...%>,<%=...%>中的 JAVA 代碼里包含的靜態文本未能正確顯示的問題,在WAS中的解決方法是除了設置正確的file.encoding, 還需要以相同方法設置-Duser.language=zh -Duser.region=CN。這與JAVA locale的設置有關。

          4.4 數據庫讀寫時的 encoding 問題

          JSP/Servlet 編程中經常出現 encoding 問題的另一個地方是讀寫數據庫中的數據。

          流行的關系數據庫系統都支持數據庫 encoding,也就是說在創建數據庫時可以指定它自己的字符集設置,數據庫的數據以指定的編碼形式存儲。當應用程序訪問數據時,在入口和出口處都會有 encoding 轉換。對于中文數據,數據庫字符編碼的設置應當保證數據的完整性. GB2312,GBK,UTF-8 等都是可選的數據庫 encoding;也可以選擇 ISO8859-1 (8-bit),那么應用程序在寫數據之前須將 16Bit 的一個漢字或 Unicode 拆分成兩個 8-bit 的字符,讀數據之后則需將兩個字節合并起來,同時還要判別其中的 SBCS 字符。沒有充分利用數據庫 encoding 的作用,反而增加了編程的復雜度,ISO8859-1不是推薦的數據庫 encoding。JSP/Servlet編程時,可以先用數據庫管理系統提供的管理功能檢查其中的中文數據是否正確。

          然后應當注意的是讀出來的數據的 encoding,JAVA 程序中一般得到的是 Unicode。寫數據時則相反。

          4.5 定位問題時常用的技巧

          定位中文encoding問題通常采用最笨的也是最有效的辦法――在你認為有嫌疑的程序處理后打印字符串的內碼。通過打印字符串的內碼,你可以發現什么時候中文字符被轉換成Unicode,什么時候Unicode被轉回中文內碼,什么時候一個中文字成了兩個 Unicode 字符,什么時候中文字符串被轉成了一串問號,什么時候中文字符串的高位被截掉了……

          取用合適的樣本字符串也有助于區分問題的類型。如:”aa啊aa?@aa” 等中英相間、GB、GBK特征字符均有的字符串。一般來說,英文字符無論怎么轉換或處理,都不會失真(如果遇到了,可以嘗試著增加連續的英文字母長度)。





          回頁首


          結束語

          其實 JSP/Servlet 的中文encoding 并沒有想像的那么復雜,雖然定位和解決問題沒有定規,各種運行環境也各不盡然,但后面的原理是一樣的。了解字符集的知識是解決字符問題的基礎。不過,隨著中文字符集的變化,不僅僅是 java 編程,中文信息處理中的問題還是會存在一段時間的。



          參考資料



          關于作者

           

          IBM has authored this article

          posted @ 2007-07-03 14:49 和田雨 閱讀(276) | 評論 (0)編輯 收藏

          僅列出標題
          共9頁: 上一頁 1 2 3 4 5 6 7 8 9 下一頁 
          主站蜘蛛池模板: 襄汾县| 西青区| 房产| 聂拉木县| 衡阳县| 东乡族自治县| 布尔津县| 湘乡市| 霸州市| 浙江省| 垫江县| 勐海县| 墨竹工卡县| 南昌县| 南昌市| 广丰县| 常熟市| 中阳县| 达孜县| 芦山县| 仙游县| 喀什市| 徐州市| 香港| 安多县| 永安市| 天气| 定远县| 鄂尔多斯市| 留坝县| 鄂托克旗| 九寨沟县| 阿鲁科尔沁旗| 定远县| 江津市| 东兴市| 丹寨县| 榆林市| 无极县| 鸡西市| 衢州市|