2008年4月29日 星期二

TOMCAT + JSP 的中文問題

1、JSP頁面顯示的中文問題
Page的第一行改成:<%@ page contentType="text/html; charset=UTF-8" %>
Head里加:<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

2、頁面Form 內容提交的中文問題
1) 只要把%TOMCAT安裝目錄%/ webapps\jsp-examples\WEB-INF\classes\filters\SetCharacterEncodingFilter.class 文件拷到你的webapp目錄下的filters下,如果沒有filters目錄,就創建一個。

2)在你的web.xml的</web-app>之前加入:
<filter>
<filter-name>Set Character Encoding</filter-name>
<filter-class>filters.SetCharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Set Character Encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

或者你不習慣用這個,可以在每個jsp頁面�使用request.setCharacterEncoding("UTF-8");我個人感覺是一樣的

3、get方式的解決辦法

1) 打開tomcat的server.xml文件,找到區塊,加入如下一行:URIEncoding="UTF-8"

完整的應如下:

<Connector port="8090" maxThreads="150" minSpareThreads="25"

maxSpareThreads="75"
enableLookups="false" redirectPort="8443" acceptCount="100"
debug="0" connectionTimeout="20000"
disableUploadTimeout="true"
URIEncoding="UTF-8"/>

2)重啓tomcat,一切OK。
當然還有最土的解決方案,雖然不太會用到,不過還是列出來,以備最無奈的時候使用:
String encodeStr=new String(fieldValue.getBytes("8859_1"), "UTF-8");

執行如下test.jsp頁面,測試是否成功
<%@ page contentType="text/html;charset=UTF-8"%>
<%@ page import="java.util.*"%>
<META http-equiv=Content-Type content="text/html;charset=UTF-8">
<%
String q=request.getParameter("q");
q = q == null?"沒有值":q;
%>
<HTML>
<HEAD>
<TITLE>新聞列表顯示</TITLE>
<body>你提交了:<%=q%><br>
<form action="test.jsp" method="post">
輸入中文:<input type="text" name="q"><input type="submit" value="確定"> <br>
<a href="test.jsp?q=中國">通過get方式提交</a>
</form>
</BODY>
</HTML>

4、mysql的中文問題
首先要修改mysql配置文件的encoding爲UTF-8,然後把jdbc connection改成如下:
jdbc:mysql://localhost:3306/bookuseUnicode=true;characterEncoding=UTF-8


電腦編碼的基本概念(轉貼)

電腦編碼的基本概念(轉貼)

編碼 (Encoding)
(作者: 謝東翰)
--------------------

A. 電腦編碼系統簡介:

電腦系統中資料儲存的最基本單位是「位元 (bit)」,而每個「位元」所
能代表的數值只有 0 與 1 而已。因此當我們要用電腦做資料的儲存與處理時,
我們通常會將連續幾個位元和起來看成一個單位,稱之為「位元組 (byte)」,
其所能代表的數值就不會只限於 0 與 1,而可以達到 2^n-1 (其中 n 為該位
元組中所包含的位元個數),如此我們就可以將我們想記錄在電腦系統的符號
一一編號,以位元組為單位儲存在電腦中,這就是編碼 (Encoding) 的基本觀念。

由於早期的電腦系統是發源於美國,因此最早的編碼系統也是發源於此。由於
他們的資料只需數字、26 個英文字母 (包括大小寫)、標點與其他特殊符號、
外加一些電腦系統的控制碼即可,因此當時一個位元組的大小只需 7 個位元
即可包含所有所需的資訊,總共可容納 128 個符號,這也就是大家所熟知的
ASCII 編碼。

然而對於歐洲語系而言,7 個位元的編碼空間不符合所需,因為除了 26 個英
文字母外,歐洲許多國家還需要拉丁字母、特殊字母的上下標與其他符號等,
才能完整表示他們的語言。因此原來的編碼系統必須再擴充,由 7 個位元變
為 8 個位元,以容納那些多出的符號,這就是 ISO8859 的編碼標準。而依地
區語系的不同, ISO8859 的編碼標準又分成幾個部分,例如 ISO8859-1 所包
含的符號可使用於英語、丹麥語、芬蘭語、德語 .... 等,而 ISO8859-2 則
可用於英語、德語、羅馬尼亞語、波蘭語 .... 等。每一個部分的 0-127 碼
的編碼方式與 ASCII 都是一樣的,只有在 128-255 碼才依地區不同而不同。
如此不同地區的人們可以選用不同部分的 ISO8859 編碼來處理他們的資料。

然而到了亞洲地區,上述的 8 位元編碼又不夠用了。因為亞洲地區不再是拼
音文字,因此多出了成千上萬個符號,我們已無法仿照當初 ISO8859 的做法,
弄出屬於中文的部分,或屬於日文的部分。唯一的作法只有合併幾個位元來
共同表示一個符號,如此才有足夠的空間來容納一個地區所有的文字。例如
我們台灣地區最常見的 BIG5 編碼,就是將兩個位元組合起來看成一個字。
但當一份文件中同時要有中英文時,意味著我們要有一個規則可以區分這個
位元組應該視為 ASCII 碼,或者它與它的下一個位元組應合起來視為一個
BIG5 碼。為了定出這樣的規則,就表示說我們無法全部使用整個雙位元組
的所有空間 (共 65536 個值)。就如我們的 BIG5 碼,其所包含的字數就只
有 13000 多個。

以上就地區語文使用似乎已經足夠了,然而在某些場合下,我們仍需要在同
一份文件中記錄多國文字。以 BIG5 編碼為例,它可以確保中文字與 ASCII
碼可以同時存在,不至於混餚 (事實上對於其他語系而言,他們的編碼方式多
半都是如此: 可以讓該地區的文字與 ASCII 碼同時存在),但如果我們要同時
表示中文、日文、韓文的文字呢?這裡就真正牽涉到多國語文的編碼範籌了。
而編碼系統的擴充就有兩種形式:

1. 文法式 (Modal) 編碼系統:

這裡最有名的就是 ISO2022 編碼。它的特徵是讓一個字串有「狀態」的區
別,而不同「狀態」的改變則以幾個特殊位元組值來代表。例如當一個字串
中讀到了位元組值 a 與位元組值 b 時,就表示字串進入狀態一,若讀到位
元組值 a 與 c 時就表示字串進入狀態二 .... 等。然後我們可以定義狀態
一以後的字串應解釋為日文 JIS 編碼,狀態二以後應解釋為簡體中文的 GB
編碼 .... 等等。如此便可以有系統地將各國的編碼方式整合進來。

很明顯的,這樣的編碼方式有一個限制,就是可以整合進來的編碼系統不能
與狀態改變標記位元 (如上例的 ab, ac .... 等) 衝突,否則的話就無法
整合進來。我們的 BIG5 編碼就是因為衝突了,所以無法整合到 ISO2022 中。
而且,這樣的編碼方式很複雜,程式實作困難,因此近年來較少有人使用了。

2. 定長式編碼系統:

此編碼系統是仿照 ASCII、ISO8859 的概念,給定一個固定長度的位元數,
試圖將世界上所有的符號都包含進來,並依序給它們編號。此方式最著名的
就是 Unicode (或稱 UCS2) 與 UCS4。以 Unicode 為例,其每個「位元組」
的長度固定為 16 個位元,即拿兩個傳統 8 位元的位元組合併成一個新的位
元組,然後編號從 0 到 65535 全部拿來使用,其中 0 到 127 是與 ASCII
碼相容的。如此方式,大概可以包含世界上主要的語言與符號如歐美地區、
中日韓越、中亞地區 .... 等等。至於 UCS4 則是 Unicode 的再擴充,其每
個「位元組」有 32 個位元,其中 0 到 65535 是與 Unicode 相同的,其餘
則是用來編入世界其他少數語言與罕用文字。

此編碼方式很明顯的好處就是沒有了文法式編碼系統的限制,同時程式容易
實作,只要提供一個適當的轉碼表,我們就可以很容易地將地區常用編碼系
統轉換為 Unicode 或 UCS4。此轉碼表可能相當大,特別是在繁體中文的
情況,但對於目前電腦系統的能力 (記憶體容量與運算速度 .... 等) 而言
已不是問題。因此,此編碼方式也逐漸成為世界的主流。儘管在某些地區仍
然對此編碼方式有意見,但在可預見的未來它將會越來越普遍。

然而,定長式編碼系統並非完全沒有限制的。最明顯的一點就是目前的電腦
硬體與程式語言都是將一個「位元組」定義為 8 個位元長,而非 16 或 32
個位元長,而且考慮到相容性的問題,在可預見的未來恐怕不會有全面性的
改變。同時由於不同 CPU 對於「位元序」的解釋不同,使得同樣是 16 或 32
位元的一個數值,在不同的 CPU 下會呈現不同的值 (例如 32 位元下在 x86
CPU 所呈現的數值 1,若將同樣的位元資料移到其他 RISC CPU 下可能呈現
出 16777216)。因此定長式編碼多半只能用於正執行中的程式內部,以便
於資料處理。若程式需要做資料的輸出入時,最理想的方式是將它轉為適當
的「多位元組編碼」 (見後述) 然後才進行。

B. GNU/Linux 下的編碼與字集:

在很多情況下,我們經常將「編碼 (Encoding)」與「字集 (Character Set)」
混為一談,但實際上它們各自有明確的定義的。所謂的「字集」是一組符號
(或文字) 的集合,而「編碼」則是將這一組符號 (或文字) 以適當的方式編
入位元組中,以便電腦能夠表示與儲存。在許多情況下,由於一個編碼方式所
能容納的字數是有限的,也因此就限定了它所能使用的「字集」,故有時我們
會將「編碼」與「字集」視為同一個東西。

在使用 glibc-2.2.X 系列的 GNU/Linux 系統中,由於已完整支援了 I18N 架
構,使得世界各地的語系資料庫都已完整地建立在系統底層的函式庫 (libc)
中,這其中也包含了各語系所用的字集與其相對應的編碼系統。以我們中文為
例,兩岸三地可能會常用到的編碼系統包括:

1. BIG5: 台灣地區最常見的編碼方式。目前 glibc 中的 BIG5 是以微軟公司
所定義的 CP950 為基礎,外加文鼎公司所採用的倚天外字集與 Unicode
轉換表而成。

2. EUC-TW: 此編碼即等價於教育部所編定的 ISO10646 國家標準編碼,目前
已內含了所有與 UCS4 可以互轉的中文字,其他尚未定義與 UCS4 可互
轉的中文字則要等定義完成後才會納入。

3. BIG5HKSCS: 此為香港地區所使用的 Big5 與香港政府增補字集。它與 BIG5
編碼是相容的,並加以擴充以容納其他罕用字。

4. GB2312: 此為大陸地區最通行的編碼方式,可容納常用的簡體字集。

5. GBK: 此為 GB2312 的擴充,除了容納原本 GB2312 中的簡體字以外,同時
還包含了繁體字與其他罕用字。

6. GB18030: 此為最近新定義出的字集編碼,與 GB2312 相容。

7. Unicode 與 UCS4: 其中 UCS4 在 GNU/Linux 系統中被用作轉碼系統的「基
底字集」(見後述)。

8. UTF-8: 此為 Unicode 的多位元編碼形式,可用於 Unicode 編碼的輸出。

以上的編碼系統在 GNU/Linux 中都有完整的支援,至於教育部新定的 Big5+
目前則不在支援之列。

C. GNU/Linux 的轉碼系統:

在具備完整的 I18N 環境支援的 UNIX 系統中 (包括 GNU/Linux 系統),其編
碼系統的轉換有兩種方式:

1. 一般轉換介面: 此即為 libc 中的 iconv() 編碼轉換介面,它可以做到各
種編碼的轉換。然而,在一般的 UNIX 系統中,並不見得系統同時支援編碼
系統 A 與編碼系統 B, 就一定能做到 A 與 B 之間的互轉,即使 A 與 B
內含相同的 (子) 字集。但在 GNU/Linux 底下,只要 A 與 B 內含相同的
(子) 字集,則可以直接透過 iconv() 做轉換。例如將 BIG5 轉成 BIG5HKSCS
或反過來,或者 GB2312 與 GBK 與 GB18030 之間的互轉。當然,各字集與
UCS4 之間的互轉都不是問題,原因就是 glibc 是以 UCS4 做為轉碼系統的
「基底字集」。

所謂的「基底字集」意指各地區的語文符號在系統內部的表式方式,而且它
還是各地區所有語文符號的集合。根據此定義,表示我們可以將世界各地所
有的編碼方式轉換成「基底字集」來統一處理 (理論上)。而 glibc 所採用
的「基底字集」的編碼方式就是 UCS4, 它是定長編碼系統,採用 32 位元
的位元組來編碼,它是目前已定義最大的一個字集的編碼方式,而且它仍持
續擴編世界各地的文字與符號。

在 GNU/Linux 下如果要透過 iconv() 進行轉碼工作時,它實際上是先將該
編碼轉成基底字集,然後再轉成目的編碼,如此便二確保只要編碼中有共同
的 (子) 字集時,轉換可以順利進行。

便對於沒有共同子字集的情況呢?例如 BIG5 與 GB2312 之間的互轉,在
GNU/Linux 下也提供額外的 iconv() 模組來進行,而這些都統一在 iconv()
介面之下,故在實際使用上不會感到任何差別。然而這類的額外模組目前
並未包含所有可能的轉換情況,但仍持續在建構中。例如 BIG5 與 GB2312
的互轉模組已於日前完成,目前尚在實驗版的 glibc-2.3 測試中,預計不
久的未來等 glibc-2.3 正式發表,並且大部分的 GNU/Linux 系統都換裝
glibc-2.3 之後,中文繁簡轉換的部分將不是問題。

2. 區域資料庫編碼轉換介面: 此轉換介面是與系統區域化資料庫 (locale) 相
關的,也是絕大部分的 I18N 程式會使用的介面。由於不同的區域化資料庫
所採用的編碼系統是固定的,例如我們台灣地區的區域化資料庫名稱為
zh_TW (或 zh_TW.Big5), 其採用的編碼系統即為 BIG5。而區域化資料庫主
要的任務只有在讓同一份程式碼可以處理各地區的語言,因而可以適用於各
地區,而不需要額外修改程式本身,故它的任何不在於處理多國語文的問題。
因此,這個轉碼介面只負責該資料庫所採用的編碼,與系統基底字集所採用的
編碼系統互轉而已。它不包含如上述 iconv() 轉碼介面那樣,可以做到任意
編碼系統間的互轉。

此轉碼介面最大的用途在於方便程式做文字處理。因為地區原本的編碼系統
多半是採用「多位元組」編碼方式,例如 BIG5 就是將兩個 8 位元的位元組
組合起來使用,故稱「多位元組編碼」。但當我們要在字串中區分中文字與
英文字母時就會遇到麻煩,因為前者是以兩個位元組來代表,後者確是以單
一一個位元組來代表,故我們要在程式中額外寫入 BIG5 碼的編碼規則才行,
而這樣的程式就不能適用與其他的編碼方式了。因此,當我們用此轉換介面
將它轉成定長編碼方式時,由於每個字 (不論中英文) 都是故定的長度 (即
位元組數),我們稱之為「寬字元 (wide character)」,這使得我們可以很
方便做字串處理。同時,我們的程式中也不必加入 BIG5 的編碼規則,因為
這些都在此轉碼介面中處理掉了,因此,這樣寫出來的程式就是一個支援 I18N
的程式,可以在不需要修改程式碼本身的情況下適用於各語系與編碼系統。

然而,我們在前面也提過,定長編碼方式 (即寬字元) 只能用於運作中的程式內部,
如果在做資料輸出入時可能因 CPU 的位元序不同與各硬體系統的限制而出錯。因此,
當我們要做資料交換時,最好的方式是將它轉回多位元組編碼,才能確保不會有問
題。這在第 2 點的轉換介面是不成問題的,因為基底字集與其多位元組編碼之間
的對應是定義在區域化資料庫中的;但在第 1 點的轉換介面中,特別是 Unicode
或 UCS4,而且包含了如中日韓等多國語文資料的情況又如何呢?對於 Unicode
而言,其所對應的多位元組編碼為 UTF-8, 它可以將所有 Unicode 碼轉換成不定
長度的位元組,視其 Unicode 碼值而定,而且轉出來的多位元組與 ASCII 編碼相
容,故可以適用於所有的 UNIX 應用程式。至於 UCS4 所對應的多位原組編碼則為
UTF-16, 它是將兩個 UTF-8 字元組合而成的。

參考資料:

1. CJKV: Information Processing, by Ken Lunde,
Publish: O'Reilly, ISBN: 1-56592-224-7

2. Documents of glibc-2.2: man pages and info pages.

利用JavaMail.收發Gmail郵件(SSL)

運用 Java Bean 連結資料庫 - 以 MySQL 為例

因市面上 JSP 的教學書籍在講到 JavaBeans 時大都僅只於其特性介紹,版工日前在某本書上看到實際運用的範例 [1],挑選其中較實用的一則做了適當修改後,提供給網友作為參考。其功能為將資料庫連結的動作寫於一共用元件中,以達到保密性(避免資料庫連結帳號、密碼直接寫在頁面中)、程式碼方便修改的彈性,以及程式碼共用,避免同樣的程式碼重複出現在各個頁面中。在微軟 ASP.NET 中也早在 1.0 版即有相同功能,稱為程式碼後置(Code-Behind),可將資料庫的連結和控制項的處理寫在裡面,其由 IIS(嚴格說起來是.NET Framework)在執行時作處理。而其元件的副檔名為 .vb(VB.net) 或 .cs(C#)。

 將資料庫連結寫在 Java Bean 中還有一項好處,即為可達成一位使用者連線時,只會建立一組資料庫的連結。比較能夠避免因不當的程式撰寫觀念,而導致在各種情況下(分頁功能、使用者重新整理頁面)都不斷重新建立資料庫連結,而嚴重影響網站運作效能、浪費伺服器資源與頻寬。下方分別為 Java Bean 與其被引用 JSP 的程式碼。

DBconn.java


package tw.idv.stephen;

import javax.servlet.http.*;
import java.sql.*;

// HttpSessionBindingListener 用來聆聽 Bean 物件何時會被加入或被移出一個 Session (該介面的實作,以本例而言並非必要)
public class DBconn implements HttpSessionBindingListener {
  private Connection conn = null;
  String stConn = "jdbc:mysql://localhost:3306/資料庫名稱?user=資料庫帳號&password=資料庫密碼&" +
           "useUnicode=true&characterEncoding=MS950";

  public DBconn() {
    //buildConn();  // 亦可在建構子中完成資料庫連結
  }

  private void buildConn() {
    try {
      Class.forName("com.mysql.jdbc.Driver");
      conn = DriverManager.getConnection(stConn);
    } catch(Exception e) {
      System.out.println(e.getMessage());  
// 錯誤訊息顯示在 Web Container 的控制台中
    }
  }

  public Connection
getConn() {  // 傳回型別為Connection
    if (conn == null)
      buildConn();

    return this.conn;
  }

  public void
closeConn() {
    try {
      conn.close();  
// 關閉連結
      conn = null;   // 註記以等待資源回收
    } catch(SQLException ex) {
      System.out.println(ex.getMessage());  
// 亦可用 ex.toString() 顯示更完整訊息
    }
  }

  // 當 Java Bean 物件加入 Session 物件時,將自動執行此內建函數
  public void valueBound(HttpSessionBindingEvent event) {}

  // 當 Java Bean 物件從 Session 中移除時,或 Session 物件消滅時,將自動執行此內建函數
  public void valueUnbound(HttpSessionBindingEvent event) {
    if (conn != null)
      closeConn();
  }
}

 上方 Java Bean 的程式中實作了 HttpSessionBindingListener 介面,該介面定義了 Session 範圍內的物件其生命週期。該聆聽器的主要用途,包括當你的屬性物件本身想知道它被加入一個 Session 範圍的時間點,以便它能同時去讀出後端資料庫的最新資料;還有當 Session 將它移除時,它就可趁機去更改資料庫中的資料 [2]。但在本例中引用該介面,僅用於提示其可應用時機,並未如上述般使用。

 程式員亦應養成好習慣,之後若 Session 內的物件不再使用時,應呼叫 Session 的 removeAttribute() 移除該屬性;而當 Session 本身不再使用時,亦可由程式員主動呼叫 Session 的 invalidate() 終結掉 Container 中的 Session。如此可節省伺服器的記憶體使用,並有助於運作效能。

DBconn.jsp


<%@ page contentType="text/html;charset=MS950" import="java.sql.*" %>
<HTML>
<BODY>
<CENTER>
利用 Java Bean 建立 MySql 資料庫連結。<br>
每一位使用者連線,只會建立一個資料庫連結。<hr>

<!-- JSP 2.0 以後,Java Bean 至少要放在 Web Container 根目錄的再下面一層目錄內 -->
<jsp:useBean id="ConDbBean" scope="session" class="tw.idv.stephen.DBconn"/>

<%
Connection conn = ConDbBean.
getConn();   // 呼叫 Bean 物件的 getConn() 方法,取得已建立完成的資料庫連結
Statement stmt = conn.createStatement();  // 可依需求改為 PreparedStatement
ResultSet rs = stmt.executeQuery("SELECT Title, Price, Page FROM StoreDB");
%>

<TABLE bgcolor=#FFCC66>
<TR bgcolor=#FF9933>
<TD>書 名</TD><TD>價 格</TD><TD>頁 數</TD>
</TR>
<%
while (rs.next()) {
  out.print("<TR bgcolor=LightYellow>");
  out.print("<TD>" + rs.getString(1) + "</TD>");           
// 可用欄位索引值(1,2,3,...)撈取資料,
  out.print("<TD align=right>" + rs.getString("Price") + "</TD>");    // 亦可用欄位名稱撈取。
  out.print("<TD align=right>" + rs.getInt("Page") + "</TD>");    // 此欄位在資料庫中是以整數型別儲存
  out.print("</TR>");
}

rs.close();
stmt.close();
ConDbBean.
closeConn();  // 呼叫 Java Bean 的 closeConn() 方法,關閉資料庫連結
%>

</TABLE>
</CENTER>
</BODY>
</HTML>

 在 Bean 之中通常不會一併完成 Statement 物件的建立,是因為 Statement 物件會對應到一組 SQL 敘述。因此,若用 Bean 物件提供 Statement,則一個連線僅會有一個 Statement 物件。有可能當某個連線正在開啟某 SQL 敘述所產生的 ResultSet 物件時,又有網頁執行另一個 SQL 敘述,如此將導致原來開啟的 ResultSet 物件關閉,因而造成不可預期的錯誤。

 

參考文件 :
[1] 位元文化,2003,「JSP動態網頁入門實務」,文魁出版社,第 12 章。
[2] Bryan Basham、Kathy Sierra、Bert Bates,2005,「Head First Servlets & JSP」,O'Reilly 出版社,第 254 頁。

最近看的書--寫給SA的UML/MDA實務手冊


不錯的書..頁數不多.但內容精要.方便帶在身邊看
<<1198122227215.jpg>>

FW: 利用JavaMail.收發Gmail郵件(SSL)


    利用JavaMail收/發Gmail郵件(SSL)
    Gmail目前已經啓用了POP3和SMTP服務
    與其他郵箱不同的是Gmail提供的POP3和SMTP是使用安全套接字層SSL的,因此常規的JavaMail程式是無法收發郵件的,下面是使用JavaMail如何收取Gmail郵件以及發送郵件的代碼:

    1. 郵件收取
    package lius.javamail.ssl;
    import java.io.UnsupportedEncodingException;
    import java.security.*;
    import java.util.Properties;
    import javax.mail.*;
    import javax.mail.internet.InternetAddress;
    import javax.mail.internet.MimeUtility;

    /**
    *
    用於收取Gmail郵件
    * @author Winter Lau
    */
    public class GmailFetch {

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

    Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
    final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";

    // Get a Properties object
    Properties props = System.getProperties();
    props.setProperty("mail.pop3.socketFactory.class", SSL_FACTORY);
    props.setProperty("mail.pop3.socketFactory.fallback", "false");
    props.setProperty("mail.pop3.port", "995");
    props.setProperty("mail.pop3.socketFactory.port", "995");

    //以下步驟跟一般的JavaMail操作相同
    Session session = Session.getDefaultInstance(props,null);

    //請將紅色部分對應替換成你的郵箱帳號和密碼
    URLName urln = new URLName("pop3","pop.gmail.com",995,null,
    "
    [郵箱帳號]", "[郵箱密碼]");
    Store store = session.getStore(urln);
    Folder inbox = null;
    try {
    store.connect();
    inbox = store.getFolder("INBOX");
    inbox.open(Folder.READ_ONLY);
    FetchProfile profile = new FetchProfile();
    profile.add(FetchProfile.Item.ENVELOPE);
    Message[] messages = inbox.getMessages();
    inbox.fetch(messages, profile);
    System.out.println("
    收件箱的郵件數:" + messages.length);
    for (int i = 0; i < messages.length; i++) {
    //
    郵件發送者
    String from = decodeText(messages[i].getFrom()[0].toString());
    InternetAddress ia = new InternetAddress(from);
    System.out.println("FROM:" + ia.getPersonal()+'('+ia.getAddress()+')');
    //
    郵件標題
    System.out.println("TITLE:" + messages[i].getSubject());
    //
    郵件大小
    System.out.println("SIZE:" + messages[i].getSize());
    //
    郵件發送時間
    System.out.println("DATE:" + messages[i].getSentDate());
    }
    } finally {
    try {
    inbox.close(false);
    } catch (Exception e) {}
    try {
    store.close();
    } catch (Exception e) {}
    }
    }

    protected static String decodeText(String text)
    throws UnsupportedEncodingException {
    if (text == null)
    return null;
    if (text.startsWith("=?GB") || text.startsWith("=?gb"))
    text = MimeUtility.decodeText(text);
    else
    text = new String(text.getBytes("ISO8859_1"));
    return text;
    }

    }
    2. 發送郵件
    package lius.javamail.ssl;
    import java.security.Security;
    import java.util.Date;
    import java.util.Properties;

    import javax.mail.Authenticator;
    import javax.mail.Message;
    import javax.mail.MessagingException;
    import javax.mail.PasswordAuthentication;
    import javax.mail.Session;
    import javax.mail.Transport;
    import javax.mail.internet.AddressException;
    import javax.mail.internet.InternetAddress;
    import javax.mail.internet.MimeMessage;

    /**
    *
    使用Gmail發送郵件
    * @author Winter Lau
    */
    public class GmailSender {

    public static void main(String[] args) throws AddressException, MessagingException {
    Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
    final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
    // Get a Properties object
    Properties props = System.getProperties();
    props.setProperty("mail.smtp.host", "smtp.gmail.com");
    props.setProperty("mail.smtp.socketFactory.class", SSL_FACTORY);
    props.setProperty("mail.smtp.socketFactory.fallback", "false");
    props.setProperty("mail.smtp.port", "465");
    props.setProperty("mail.smtp.socketFactory.port", "465");
    props.put("mail.smtp.auth", "true");
    final String username = "
    [郵箱帳號]";
    final String password = "
    [郵箱密碼]";
    Session session = Session.getDefaultInstance(props, new Authenticator(){
    protected PasswordAuthentication getPasswordAuthentication() {
    return new PasswordAuthentication(username, password);
    }});

    // -- Create a new message --
    Message msg = new MimeMessage(session);

    // -- Set the FROM and TO fields --
    msg.setFrom(new InternetAddress(username + "@mo168.com"));
    msg.setRecipients(Message.RecipientType.TO,
    InternetAddress.parse("
    [收件人地址]",false));
    msg.setSubject("Hello");
    msg.setText("How are you");
    msg.setSentDate(new Date());
    Transport.send(msg);

    System.out.println("Message sent.");
    }
    }

初學者必看--如何學習jsp

初學者必看--如何學習jsp

轉貼==

 初學者必看:如何學習jsp

在網上看到很多問題是關于如何學習jsp的,正好網上看到一篇關于學習jsp的文章,就摘了一部分翻譯過來,希望能對大家學習jsp有點指
導。
一個普通的錯誤是把JSP當作簡化的 Java。它不是,(事實上, JSP 是簡化的 servlets 。)程序員通常試著沒有學習要求的支持技巧而
直接學習 JSP 。JSP 是一個餃接技術,並且成功地連接你需要理解的另外的技術。如果你已經知道 Java , HTML 和 Javascript,這意味著
JSP 將確實是簡單的。
需要成為一個成功的 JSP 程序員可以參考這個時間表。請注意下列︰
*忽略你已經熟悉的步驟。
*訓練的時間只是代表學習好足夠的基礎時間,這樣才能轉移到下一步。
1、建立並且理解你的Web Server。
因為Apache 是免費的並且在大多數平台上工作,為訓練目的推薦 Apache。
安裝時間︰2 天。

2、 保證你理解 HTML / XHTML 。
你將需要了解html基礎, 特別是 HTML 布局中的table的使用。XHTML 不久將代替 HTML ,學習 XHTML 的基礎是一個好主意。許多程序員
通過 HTML IDE 學習 HTML ( 集成開發環境 ) 。因為大多數 HTML IDE產生混亂的HTMl語法,所以花時間學習手工寫作html是很有必要的。因
為你將會使用 JSP 和 HTML 混合編程,精通HTML語法是重要的。所以,你必須能流利地寫 HTML 。
訓練時間︰2 ∼ 4 個星期。

3、開始學習 Java 。
開始學習 Java 1.3 理解 Java 基礎是很重要的。不用擔心學習Swing或 Java 的圖形方面,因為在JSP 中你不會使用這些特征。集中精力
在 Java 工作的細節,學習 Java 的邏輯,也在 Java Bean上花時間。學習Applet是好的, 但是就象Swing, JSP 的大多數應用將不使用小程
序。
訓練時間︰3 ∼ 6 個星期。

3、學習 JavaScript
學習怎麼將 JavaScript在HTML中驗證輸入的Form元素。也學習 JavaScript怎麼能在一 HTML 頁以內修改Form的元素。最後要求你能從一
HTML 頁內的事件中觸發 JavaScript Function。
訓練時間︰一∼ 2 個星期。

4、學習並且理解你的Web Server的更好的細節。
熟悉Web Server的特征,這是很重要的。
訓練時間︰2 天。

5、建立你的 JSP Server
我推薦以Tomcat開始。它可以很好地運行JSP程序。當你不能在生產使用Tomcat時,學習盡可能多的知識以便于更好的運行程序。另外, 許
多 JSP 程序員使用Tomcat。因此當你遇到一個問題時,你將容易發現幫助。
安裝時間︰一∼ 2 天。

6、開始學習 JSP 。
基本的 JSP 學習通過的步驟 1到步驟6可以完成, 然後使用 JSP 對象和腳本寫 JSP 程序來聯系。學習 JSP 的另外一個方面可以學習怎麼創
建一個分布式的應用程序。
訓練時間︰4 ∼ 6 個星期。

7、學習更多的 JSP server。
沒有關于更多的 JSP Server當然也可以運行jsp程序。然而, 許多 JSP server都由自己特殊的特征,可以讓你更好的理解你的JSP 工程。
學習更多的Jsp server如何處理jsp程序是有必要的。同樣也可以優化你的 JSP 應用程序,並且使之運行得更快而不出任何問題。
訓練時間︰2 ∼ 7 天。

8、 學習 JDBC 。
JSP 大多數應用將使用數據庫,JDBC 被用于數據庫連接。經常忽略的一個事實就是,每個 JDBC Driver 所支持的東西是相當不同的。了
解並熟悉在jsp工程上被使用的 JDBC driver的細節是很重要的。
(有時這部分的學習被包含在前面 Java 或JSP的學習中了 。)
訓練時間︰1∼ 2 個星期。
到現在,你已經成為了熟練的 JSP 程序員。仍然有很多需要學習,你可以考慮擴展你的知識比如 DHTML , XML ,java證書, JSP Tag
Libraries 或 Servlets , 看你想要造什麼類型的網站而決定了。
這些訓練是JSP 的核心。你不必都學習上面所有的, 取決于你在工程中分配到什麼任務和你已經有什麼知識。但是這是我成功地訓練程序員
的時間表。關鍵的單元是時間。平均的說, 5 個月時間確實能夠訓練一個人 ( 從開始到完成 ) 成為一個對jsp熟悉程序員。5 個月時間似乎很
長,但要成為一個資深的WEB程序員所學的東西遠遠不止這一些。
也許你認為這樣學習一種語言花費的時間太長了,因為學 ASP 會更快、時間會更短。 但是學習 ASP 不需要學習java的。

如何使用JAVA API中的Collection 來儲存值

轉貼自

通常Vector裡的都是物件的reference,而不是實際的物件

存放在Vector裡的都是物件,所以才會有String和int...等等的資料型態的差異,

例如:String在JAVA裡事物件的型態,可是int...等等,都是數值,而不是物件

把一些物件塞入Vector, 例如:

&nbsp;Vector v = new Vector();

Vector w = new Vector();

String[] firstnames = { "Spencer" , "Benx" , "Jaseph" , "Isao" , "Sidekick" };

String[] lastnames = { "is handsome boy" , "is our manager" , "is sale's father" , "is

tobacco's man" , "is our MIS"};



for( int i =0 ; i < firstnames.length ; i++ )

v.add(firstnames[i]);

for( int k =0 ; k < lastnames.length ; k++)

w.add(lastnames[k]);

for( int j = 0 ; j < v.size() ; j++)

System.out.println(" Odject.co's the best teammates = " + v.get(j) +" "+ w.get(j));

NEW起來一個Vector,然後跑一個for迴圈,把值塞入.

get出來時候,也是以同樣的方式

或者用Iterator來一一取得集合裡的值

Iterator it = v.iterator();

Iterator lit = w.iterator();



while(it.hasNext()){

System.out.println(" Odject.co's the best teammates(Iterators) = " + it.next()+" "+ lit.next());

}

以上面的結果,出來的值:

Odject.co's the best teammates = Spencer is handsome boy

Odject.co's the best teammates = Benx is our manager

Odject.co's the best teammates = Jaseph is sale's father

Odject.co's the best teammates = Isao is tobacco's man

Odject.co's the best teammates = Sidekick is our MIS

Odject.co's the best teammates(Iterators) = Spencer is handsome boy

Odject.co's the best teammates(Iterators) = Benx is our manager

Odject.co's the best teammates(Iterators) = Jaseph is sale's father

Odject.co's the best teammates(Iterators) = Isao is tobacco's man

Odject.co's the best teammates(Iterators) = Sidekick is our MIS

如果是一個陣列其元素是數值(只有字串是物件,塞入集合中只能塞入物件)時:

Vector a = new Vector();
int[] h = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 };
for( int g = 0 ; g < h.length ; g++ )
a.add( new Integer ( h[g] ));
Iterator lv = a.iterator();
while(lv.hasNext()){
System.out.println( " LV的(iterator) "+ lv.next());
}

雜湊表的用法學習:

//雜湊表的塞值和取值

HashMap aMap = new HashMap();

String myKey1 = "key1";

String myKey2 = "key2";

Integer theObject1 = new Integer(12345);

String theObject2 = "塞入的第二個值 theObject2";

aMap.put( myKey1 , theObject1);

aMap.put( myKey2 , theObject2);

if(aMap.put( myKey1 , theObject1) != null ){

System.out.println(" Oh Ya Baby ~~~~ I put an object to HashMap ");

}

System.out.println(aMap.get(myKey1));

System.out.println(aMap.get("key1"));

System.out.println(aMap.get("key2"));


雜湊表中用Iterator的塞key取值法:

Set keys = aMap.keySet();

Iterator keyIter = keys.iterator();

//Iterator keyIter = aMap.keySet().iterator();

while(keyIter.hasNext()){

System.out.println("aMap.Iterator(value) = " + keyIter.next());

}

關於JVM Tuning

寫JAVA時時常會遇到一些無解的問題..也許是效能方面.記憶體使用方便.或者和本身的OS有關.有一次無意中發現resin的管網有提到JVM 的Tuning..就順手把網址記下來.和大家分享一下

<http://www.caucho.com/resin-3.0/performance/jvm-tuning.xtp#memory>



如何在Java中call dll

如何在Java中call dll

自城市胡同


我知道的有两种方法,一种是直接用JNI,另一种是则是用Jacob(实质上也用的是JNI)

先讲讲第一种方法

1.编写java程序TestDll,注意,这个类有两个作用,一个是用来做头文件,另外一个作用就是通过它来调用dll
public class TestDll {
 static
 {
 System.loadLibrary("DLLSample");//载入dll
 }
 public native static int DoubleValue(int i);//函数声明
}

2.编译: javac TestDll
3.生成头文件: javah TestDll
生成TestDll.h文件,这里面只对函数DoubleValue作了声明
/\* DO NOT EDIT THIS FILE - it is machine generated \*/
\#include
/* Header for class TestDll */

\#ifndef \_Included_TestDll
\#define \_Included_TestDll
\#ifdef \__cplusplus
extern "C" {
\

Pratical in Java- 5 多型(polymorphism)優於instanceof

package rule05;

/**
*
* @author Robert Tan
*
* 實踐05:多型(polymorphism)優於instanceof
* 嗯,理解實踐05是很重要的,但小的認為他的設計不好.
* 要是我設計,我不會多一個getBonus方法出來,因為又多一個型態的員工,除getSalary()與getBonus()之外,
* 又有getCommission()時,那不是又要在Employee介面與子類別,各多加一個getCommission方法.
* 依據物件責任指派原則GRASP的資訊專家樣式(指事情交給擁有相關資訊的物件做-Programmer要自己算出薪資),
* 則範例程式應該改寫成...
*/

public class Main {
public static void main(String [] args) {
Employee [] arr = new Employee [2];
arr[0] = new Manager();
arr[1] = new Programmer();

int payroll = 0;
for (int i=0; i < arr.length; i++) {
payroll += arr[i].getSalary();
}

System.out.println("PAYROLL : " + payroll);
//對出納的呈辦人員而言,他不會關心每個人的薪資項目,他所要知道的是公司這個月到底
//要轉出多少錢到銀行,才足夠支付員工薪水
}
}

interface Employee {
public int getSalary();
}

class Manager implements Employee {
public int getSalary() {
return 500;
}
}

class Programmer implements Employee {

private int salary = 300;
private int bonus = 150;

public int getSalary() {
return salary + getBonus();
}

private int getBonus() {
return bonus;
}
}

----OUTPUT:PAYROLL : 950

Pratical in Java- 4 在arrays和vectors之間慎重選擇

Pratical in Java- 4 在arrays和vectors之間慎重選擇

package rule04;

/**
* @author Robert Tan
*
* 實踐04:在arrays和vectors之間慎重選擇
* 嗯,書上寫的很容易理解.
* 由於Vector的內部實作是用Array來完成的,所以要創建Vector時,請用
* Vector(int initialCapacity, int capacityIncrement)這個建構子.
* Vector預設會建立內部大小為十的元素陣列,每次增量也為十個元素
*/

import java.util.Vector;

public class Main {
public static void main(String [] args) {
Vector vector = new Vector();
System.out.println("V SIZE : " + vector.size());
System.out.println("V CAPACITY : " + vector.capacity());

vector.add(new Integer(1));
vector.add(new Integer(2));
vector.add(new Integer(3));
vector.remove(2);
System.out.println("V SIZE : " + vector.size());
System.out.println("V CAPACITY : " + vector.capacity());

Vector vector2 = new Vector(2, 3);
System.out.println("V2 SIZE : " + vector2.size());
System.out.println("V2 CAPACITY : " + vector2.capacity());
vector2.add(new Integer(1));
vector2.add(new Integer(2));
vector2.add(new Integer(3));
System.out.println("V2 SIZE : " + vector2.size());
System.out.println("V2 CAPACITY : " + vector2.capacity());
}
}
  

----
V SIZE : 0
V CAPACITY : 10
V SIZE : 2
V CAPACITY : 10
V2 SIZE : 0
V2 CAPACITY : 2
V2 SIZE : 3
V2 CAPACITY : 5
 

Pratical in Java-3 預設情況下所有non-static函式都可以被覆寫


* 實踐03:預設情況下所有non-static函式都可以被覆寫
* 把class設宣告成final在書中有提到兩個要注意的地方為
* 1.class設計
* 2.執行期效率(Runtime Performance)
* 關於第二點在實踐36會講到,但我還沒讀到所以先忽略
* 關於第一點,以下是小的一點理解
* 若設計一個工具類別,我們通常會把它宣告成final類別,不希望別人繼承再改寫其行為而是只去使用.
* 像java.lang.Math一樣,它提供一些有用的數學運算方便我們使用.

Pratical in Java - 實踐01:引數以by value方式而非by reference方式傳遞




package rule01;

/**
*
* @author Robert Tan
*
* 實踐01:引數以by value方式而非by reference方式傳遞
* 一般Call by Value給人的印像是把值或物件拷貝一份.這句話對Java基本型別是對的,但對Java物件
* 則是不正確的,Java拷貝的不是物件本身,而是如書上所說的物件的參考值.了解書上所寫的之後,我們一
* 定會問說,我要如何拷貝一份物件,而不影響原本的物件呢?答案是利用clone().
* 附註:Thinking in Java,對Cloneable與clone有很詳細的介紹
*/

public class Main {
public static void main(String [] args) {
Student1 Jack = new Student1("Jack");
change1(Jack);
System.out.println("Jack's name is " + Jack.getName());

//implement Cloneable interface and override clone method
Student2 Rose = new Student2("Rose");
change2((Student2) Rose.clone());
System.out.println("Rose's name is " + Rose.getName());
}

public static void change1(Student1 s) {
s.setName("Robert");
}

public static void change2(Student2 s) {
s.setName("Robert");
}
}

class Student1 {
private String name;

public Student1(String name) {
this.name = name;
}

public void setName(String name) {
this.name = name;
}

public String getName() {
return name;
}
}

class Student2 implements Cloneable {
private String name;

public Student2(String name) {
this.name = name;
}

public void setName(String name) {
this.name = name;
}

public String getName() {
return name;
}

public Object clone() {
Object obj = null;
try {
obj = super.clone();
}
catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
}

2008年4月28日 星期一

JDK設定步驟


這篇文章.最適合初學者看吧! 
JDK設定步驟:

   1. java程式的compiler,tools,package甚至是source code都包含在jdk這個套件中,jdk可到sun取得,各平台都有jdk的套件,請依需要選擇然後安裝。
   2. 拿到jdk解開後,會安裝在一個目錄下 如:windows下 c:\jdk1.1 ; 或 unix 下/usr/local/java
   3. 在作業系統下增加JDK路徑,及classpath的環境變數:

      windows:

      ================ AUTOEXEC.BAT ==============

      PATH=%PATH%;C:\JDK1.2\BIN

      SET CLASSPATH = C:\JDK1.2\JRE\LIB;  <== 若為jdk1.2則不須加上此行

      ============================================   

      unix:(bash or sh)

      ================ .profile ==================

      PATH=$PATH:/usr/local/java/bin

      CLASSPATH=/usr/local/java/jre/lib:  <== 若為jdk1.2則不須加上此行

      export PATH

      export CLASSPATH

      ============================================

   4. 當寫好程式 InstanceSample.java , 必須要將程式編譯(compile)

      windows: 

      c:\jdk1.2>javac InstanceSample.java

      unix:

      # javac InstanceSample.java

   5. 程式編譯後,會產生 ClassSample1.class 的bytecode, 如果要執行,可打

      windows: 

      c:\>java InstanceSample

      unix:

      # java InstanceSample

如何執行Applet:

     假設Applet程式放在 ExtendsSample.html中

     c:\>appletviewer ExtendsSample.html

Bible:

     寫程式最重要的是參考資料,因此jdk內的package用法必須到 Sun

     下載jdk的document(java api),才能知道如何使用

     %%% very important %%%

#end


This message and any attachments (together, the "message") are intended only for use of the addressee and may contain information that is privileged and confidential. If you are not the intended recipient, you are hereby notified that any use or dissemination of the message is strictly prohibited. Any use not in accordance with its purpose, any dissemination or disclosure, either in whole or in part, shall be prohibited except with our prior approval. If you have received this message in error or you have doubts about the authenticity of it, please notify us immediately by reply and delete this message from your system. We do not represent that messages may not be interfered with, may contain computer viruses or other defects and may not be successfully replicated on other systems. We shall not be liable for the message if modified. We also reserve the right to intercept and monitor the content of email message to and from our system.

Java 學習之道

值得一看之文章
轉自蔡學鏞之網站

目前 Java 可以說是產業界和學術界最熱門的語言,許多讀者都很急切想把 Java 學好。除非像電影「駭課任務」(Matrix)一樣能夠把需要的專業技巧下載到腦海(女主角只花了幾秒下載資料,就馬上具備飛行員的技巧),或是武俠小說 中的運功傳送內力的方式,否則花上一段時間苦學是免不了的。花時間,不打緊,就怕方法錯誤,事倍功半。我認為,學習 Java 包括了下列的範疇:

* 認識 Java 的原理
* 學會 Java 語言
* 物件導向的思維
* API
* 開發工具的用法

下面分別就這幾點詳細闡述。

學習 Java 的原理
瞭解 Java 的原理,才有可能真正體會 Java 的一切,學習任何事情,只要能掌握原理,就會比較順利。我發現許多人使用 Java 已經逾年,卻連 Java 最基本的特質都不清楚。

一般來說,資訊系畢業的學生,對於資訊的新事物學習的速度,會比其他科系來得快, 就是因為四年的教育為他們奠定了學習新事物的基礎,這也是資訊系課程的價值所在。

除非你受過的基礎訓練夠紮實,否則在學習或使用 Java 的過程中, 你一定會需要參考到這些資訊系學科的知識。我建議各位可以在閒暇之餘,把資訊系課本的書拿來讀一讀。 這是治本之道,絕非立竿見影的特效藥,必須要有耐心。

學習 Java 語言
學習 Java 語言很簡單,畢竟 Java 語言也只包含五十多個關鍵字(keyword)與幾十個算符(operator),再加上 Java 語法(syntax)也很簡單,所以一般人可以很快就學會 Java 語言。危險的是,很多人認為已經完全掌控 Java 語言,但其實對於內部的運作機制仍不能掌握,這些盲點有時候會讓你無法完全掌控 Java 語言。克服這些盲點的方式是看「The Java Language Specification, 2nd Ed.」(沒有中文版)來徹底弄懂 Java 程式語言,並看「Inside the Java Virtual Machine, 2nd Ed.」(有中文版,但翻譯得@#%$)來徹底掌握 Java 虛擬機器的運作方式。

學習物件導向的思維
學會了語言,並不代表就可以設計出好的物件導向系統架構。想要成為物件導向的專家,往往需要:

* 多看相關的書,特別是 Design Pattern 和 Refactoring 的書。
* 多觀摩別人的程式(例如 Java API 的 design 與 implementation)
* 多寫程式

學習 API
學會 Java 語言之後,還需要學會一些 API 才能寫出有用的程式。Java 的 API 非常多,必須規劃好一個學習路徑,才不會在浩瀚的 API 大海中迷失。必備的 API 包括了:IO、New IO、Collection Framework、Network、RMI、JAXP... 等。至於其他的 API,就看你的需求而定,大致上分成:

* GUI 類:JavaBean -> Swing -> JavaHelp -> Java2D -> Image IO -> JAI -> Java 3D ...
* Enterprise 類:JDBC -> JDO -> Servlet -> JSP -> EJB -> JMS -> JTA/JTS...
* J2ME 類(這一類不是我的專長,無法提供學習順序建議)

學習開發工具的用法
只用 JDK,是很難寫出大型程式的,所以通常程式員會使用 JBuilder/VisualAge/ VisualCafe 等工具,這些軟體賣這麼貴,一定有它的價值,值得花一些時間好好學習這些開發工具的使用方式, 你將會發現開發工具所提供的許多功能,可以讓你在開發過程中生產力大增。

本文作者:蔡學鏞
張貼日期:9/19/01

為Java菜鳥幫解答問題二十例


問題1、在我的系統中有幾個字處理程式,我應該選擇哪一個來編輯java根源程式呢?

解答:只要能將檔保存為不帶任何特殊格式的文字檔案,選擇哪個都可以。

問題2、如果每條語句之後都要加上分號,那為什麼在象//hello這樣的語句後面不用加上分號呢?

解答:注釋是完全被編譯器忽略的。如果在你的程式中某一行加上了"//"號,這就告訴編譯器忽略"//"右邊的一行語句。

問題3、什麼是字元

解答:字元就是單個字母、數位、標點符號或其他符號。例如,T,5以及%。字元以原文的形式存儲在變數中。

問題4、當我在使用javac編譯程序時,出現"Invalid argument"的錯誤資訊,我該如何改正它?

解答:你可能在鍵入javac命令時遺漏了.java副檔名。此外,當前目錄必須是要編譯的java原始檔案所在的目錄。

問題5、在編譯器提示出錯的語句中,我找不著錯誤,該怎麼辦?

解答:錯誤資訊顯示的行號通常並不是真正出錯的地方。檢查錯誤行號前面的語句,或許你可以找到錯誤。錯誤通常是在同一個程式塊中。

問題6、我在Web頁面上可以執行一個有用的程式,我能脫離流覽器獨立執行它嗎?

解答:在大多數情況下是不可以的。標準的Java的程式均執行在Web頁面上或是命令行中。可以寫一個支援這兩項的java程式,大部分你能找到的程式都不能兼顧這兩個功能。

問題7、java程式有著跨平臺的特性,那為什麼有些java編譯器(象Sunsoft Java工作平臺)只能執行在特定的系統上?

解答:java程式有可能在特殊的系統中受到限制(象相容的PC機),因為java程式為了達到某種目的可能包含一些非java的程式。一個java程式可能調用另一個由C++寫成的程式以達到某些特性。

問題8、我的電腦在網頁中執行java程式,是否會帶來病毒?

解 答:由於網頁程式語言的嚴格限制,對其控制下的讀寫和對檔的修改也是受控制的,不存在任何從頁面上讀java程式而導致病毒的途徑,除非在你的系統中由 你賦予這些程式完全的許可。允許他人在你的硬碟上進行操作。那些下載後在命令行中執行的java程式則有著與其他程式相同的風險,如果你使用從網上下載的 程式,你必須用好的防毒軟體進行檢查,並做到經常有規律地進行。

問題9、單個java程式可以既是應用程式又是java Applet嗎?

解 答:是可以的。如果程式不是很簡單的話,這樣做通常是很笨拙的。只要Applet加上main()模組就可以當作應用程式執行,但在應用程式中,卻無法加 上init()塊和paint()塊而使它以java Applet的形式執行。大多數程式或者是應用程式或者是Applet,而不會兩者皆是。

問題10、所有傳遞給應用程式的參數都必須是字串嗎?

解答:當程式執行時,java將所有的參數當作字串存儲。

問題11、java程式中的行是否同語句一回事?

解答:不。儘管一般創建程式都是一行一個語句,但這只是為了使程式更易於理解,這不是必須的。java編譯器在它對程式進行編譯時並不考慮行、空格和其他格式的形式,編譯器只要求在語句的結束處有分號。你可以在一行上寫多個語句,儘管我們通常不提倡這樣做。
問題12、為什麼變數名的第一個字母要小寫,比如gameOver?

解答:這是為了很容易將變數和Java中其他元素區分開來。另外,在命名變數時遵循一致的風格的好處是當你在程式中的幾個不同的地方使用同一個變數時可以避免發生的錯誤。

問題13、是否可以讓具有相同字母的兩個變數名大小寫不同?比如ABC和abc

解 答:不同大小寫的變數名被看成是不同的變數,Java對大小寫是敏感的。因此象上面那樣讓相同的名字使用兩次是可以的。但是,當你或某個其他人要指出程式 是如何工作的時候,這樣做很容易引起許多混亂。而且這樣也增加了程式中使用錯誤變數名的可能性,而這個錯誤編譯器是不能發現的。這樣的錯誤只會影響程式的 執行結果,我們稱這樣的錯誤為邏輯錯誤。這種錯誤必須由一個注意力集中的程式師在測試時發現。

問題14、我編的程式在其他機子上流覽時,總是說***.class not found,我想問一下,對於在系統介面中的class,我怎樣知道我的程式使用了哪些類.?

解答:編譯的時候用選項 javac -verbose ******** 可以看你的程式都用到了類庫中的哪些類。

問題15、String s1 = "123",如何轉換成整數 int a1 = 123,相反的轉換又如何實現?

解答:a1=Integer.parseInt(s1),s1=Integer.toString(a1),還有很多方法,參見幫助檔。

問題16、System.out.print()為什麼不輸出?

解答:輸出流收到一個換行符才將緩衝區中的內容輸出,在程式最後加一行 System.out.flush() 應該就可以。

問題17、 把一個陣列作為入口參數傳給一函數,函數中如何知道該陣列的大小?

解答:陣列物件都有 .length屬性,如:int count = yourArray.length ;

問題18、程式執行時總是出現NullPointerException,可編譯也能通過,不知為什麼?

解答:使用變數前別忘了new之,多數情況都是因為這個問題。

問題19、為什麼使用編譯器javac編譯時,出現'javac' 不是內部或外部命令,也不是可執行的程式或批次檔案(javac: Command not found)的錯誤?

解 答:產生的原因是沒有設定好環境變數path。Win98下在autoexce.bat中加入path=%path%;c:\jdk1.3\bin, Win2000下則控制台->系統->進階->環境變數->系統變數...看到了變數Path,雙擊Path,在後面加上c: \jdk1.3\bin。當然我們假設JDK安裝在c:\jdk1.3目錄下。

問題20、使用java執行class文件時,出現如下的錯誤:
java Helloworld.class
Exception in thread "main"
java.lang.NoClassDefFoundError:
HelloWorld/class

解答:執行程式的時候不需要也不應該加上.class副檔名,java解釋器會把前面的類名作為一個包名來解釋的。

Java 相關的編譯技術

轉貼===

Java 相關的編譯技術

除了 Java 的編譯器和虛擬機器之外,還有一些相關的編譯技術,本文章試圖做一個簡單的說明。

JIT 編譯器
傳統的 Java 虛擬機器很愚蠢,將一道 bytecode 的指令翻譯成機器碼之後,馬上執行這些機器碼,執行完這批機器碼之後,就把這些機器碼丟了,接著再翻譯下一道 bytecode 的指令,繼續下去 ...。即使下次執行到以前執行過的 bytecode 指令,依然要重新翻譯成機器碼才能執行,如此一來,效率當然不好。

使用 JIT 編譯器(Just-In-Time compiler)技術的虛擬機器比較聰明,會把常常執行的部分在第一次先翻譯好放在記憶體,以後再次執行到這裡時,就不用再翻譯,直接從記憶體取出機器 碼即可執行。這麼一來,只要你的記憶體夠大,JIT 編譯器的技術夠好,你的 Java bytecode 執行速度也可以逼近純編譯式的程式。

其它程式語言的編譯器
任何檔案只要符合 Java bytecode 的格式,就可以被 Java 虛擬機器執行。製造出 Java bytecode 的方式有許多種,不一定要使用 Java 語言來寫程式,才能編譯成 Java bytecode。Java 是語言也是平台,你可以不使用 Java 語言(也就是 Java 編譯器),只使用 Java 平台(也就是 Java 虛擬機器)。只要某語言有提供編譯器,能將該語言的原始碼編譯成 Java bytecode 格式,就可以在 Java 平台上執行。

據我所知,目前已經有下列語言提供相容於 Java 平台的編譯器(以英文字母順序排列):Aardappel,Ada,Agora 98,BAMBOO,Basic,Bistro,Bolero,C,C++,CLIPS,COBOL,Correlate,Dawn,E, EcmaScript,Eiffel,Foo,Forth,Fortran,Funnel,Haskel,Hojo,JavaScript, Jickle,JIF,Jinni,Lisp,LL,LLP,Logo,Luck,MINERVA,Mini,ML,Modula-2,NetRexx, Nice,Oberon-2,Pascal,PLAN,Pnuts,Prolog,PS3I,Python,Sather,Scheme,SELF, Simkin,Small Talk,Tcl,WebL,Yassl,Yoix,Yoyo。

原生編譯器
如果你不在乎 Java 程式能否跨平台,你希望 Java 程式能如同 C/C++ 一般被編譯成機器碼而非 Java bytecode,那麼你可以使用 Java 原生編譯器(native compiler)。目前已經有不少這樣的產品可以使用。

Java 原生編譯器有兩大類,一類可以把 Java 的原始碼編譯成機器碼,另一類則可以把 Java bytecode 編譯成機器碼。

反編譯與混淆器
Java bytecode 因為檔案格式簡單,資訊保留完整,且指令是最簡單的堆疊式(stack-based)架構等因素,所以很容易被反編譯(de-compilation)。 反編譯指的是和編譯相反的過程,對 Java 來說,反編譯就是把 Java bytecode 轉換成 Java 原始碼的過程。

為了防止你辛苦地開發出來的 Java bytecode 被他人反編譯成原始碼,你可以透過混淆器(obfuscator)將你的 Java bytecode 轉換成更混亂的 Java bytecode,執行起來效果一樣,但是被混淆過的 Java bytecode 比較不容易被反編譯。你通常要為此付出一點代價,因為混淆過的程式執行速度通常會變慢。且混淆器只能增加反編譯的難度,不能保證你的程式一定無法被反編譯 成功。畢竟道高一尺,魔高一丈,如果有人願意花許多時間和精力反編譯你的 Java bytecode,你根本就無法攔阻。

組譯與反組譯
組合語言(assembly)是一種非常接近機器碼的語言。將組合語言轉成機器碼的工具稱為組譯器(assembler),反過來將機器碼轉成組合語言的工具稱為反組譯器(dissembler)

對於 Java 虛擬機器來說,Java bytecode 就如同它的機器碼,有沒有一種語言是很接近 Java bytecode 的呢?也就是說,Java 有沒有的組合語言呢?基本上,Sun 並未定義 Java 的標準組合語言,但是有一些人定義了自己的 Java 組合語言,並提供 Java 的組譯器(甚至反組譯器)。例如 Jasmin 以及 javaa 都是 Java 組譯器。

Java 的中文問題

轉貼==
事實上..有時真的為了中文的問題題滿煩惱.這篇JAVA的中文問題.

漫談Java中的中文問題

摘要:關於Java應用在處理中文時所存在問題的討論已經相當多了,與大部分的討論不同,本文將從中文字元的輸入和輸出的角度來討論Java語言處理中文時所存在的問題。

  儘管關於Java在處理中文字元時所存在的問題的討論已不乏其數,但由於Java技術涉及內容廣(J2EE包含了十幾種相關技術),技術供應商 繁多,面向Java的Web伺服器、應用伺服器以及JDBC資料庫驅動等都沒有官方的標準,所以Java應用在處理中文時出了存在固有的問題外也會隨著選 用的伺服器、驅動程式的不同產生一些與平臺相關的問題。也就是說,在處理中文問題時,Java代碼的可攜性打了折扣。

  總的看來,Java的中文處理問題較為集中地出現在JSP技術應用和Java的資料庫訪問過程中。這是因為無論是JSP應用還是基於JDBC的 資料庫訪問都涉及到了Java程式與另外一種應用系統的交互,這種交互不可避免的要求系統之間進行資料的交互和參數的傳遞,而Java處理中文出現問題的 地方往往就是這些資料讀入和輸出的地方。

  JSP程式所應該注意的中文問題

  以Tomcat 3.2.1的JSP應用為例,一般遇到中文問題可以使用如下的編碼強制轉換函數進行內碼的轉換。

public static String toChinese(String strvalue)
{
try{
if(strvalue==null)
return null;
else
{
strvalue = new String(strvalue.getBytes("ISO8859_1"), "GBK");
return strvalue;
}
}catch(Exception e){
return null;
}
}

  注意,在使用該函數前,我們需要分析中文無法正確輸出的原因到底是什麼,而不能將所有的中文處理的問題都用這個方法來解決。例如,如果是由於忘 記將 JSP的輸出代碼定義為GB2312或GBK而產生的中文無法正確輸出就不能用這個函數來解決。一個好的習慣是在我們編寫每一個JSP頁面時都在檔的第一 行定義程式所要輸出的字元集,如

<@ page contentType="text/html; charset=GBK" >或<@ page contentType="text/html; charset=GB2312" >

  對於一些不支持定義輸出的字元集的JSP版本,我們也可以作如下的設置:

<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=gb2312">

  另外還需要注意的是,這個函數是用來解決那些確實出現了無法正確輸出中文的代碼,而不是一個通用的用來保證中文字元正確輸出的函數。由於中文字 元無法正確的輸出或讀入的原因都是因為這個字元的編碼和系統缺省的字元集編碼(或者是應用所要輸出的字元集,二者一般情況下是相同的)的不同引起的,所以 在應用該函數前我們必須確定我們所要讀入或輸出的字元的編碼到底與系統缺省的字元集編碼是否相同。
下面的例子將給出該函數的正確和錯誤使用的情況。例子所採用的JSP的系統為Tomcat 3.2.1,用戶端和伺服器端的運行環境都是中文的Windows2000。

  例1



<@ page contentType="text/html; charset=GBK" >

<html>

<head>

 <title>

  testJSP

 </title>

</head>

<body>

<h1>

<

 class testChina extends Object{

  public String toChinese(String strvalue)

  {

   try
{     if(strvalue==null)      return null;     else     
{      strvalue = new String(strvalue.getBytes("ISO8859_1"), "GBK");      return strvalue;     }

  }catch(Exception e)
{    return null;   }
 }

 public void test(){

 }

}

testChina testC = new testChina();

String str1 =new String("這是一個對中文支持的測試".getBytes("GBK"));

String str2=new String("這是一個對中文支持的測試".getBytes("GBK"),"ISO-8859-1");

String str3 =new String(testC.toChinese(str2));

out.println("Begin<br>");

out.println("str1");

out.println(str1+"<br>");

out.println("str2");

out.println(str2+"<br>");

out.println("str3");

out.println(str3+"<br>");

out.println("End<br>");

System.getProperties().list(System.out);

>

</h1>

</body>

</html>


  我們知道,Java編程語言默認的編碼方式是UNICODE但Java編譯器所使用的字元集則是作業系統的默認字元集,中文的Windows 是GBK,英文系統則是ISO-8895-1。對於例1來講,系統的默認字元集是GBK,JSP的輸出字元集也是GBK,二者是一致的。對於str1,我 們令其採用系統默認的字元集編碼;對於str2我們刻意的將其轉換為ISO-8895-1編碼以產生中文無法正確輸出的結果;str3是testC類的 toChinese函數的一個不正確用法,它將原本正確的字元輸出轉化成了與系統字元集不符合的字元編碼,反而引起了造成了中文輸出的錯誤;str3則是 testC類toChinese函數的一個正確用法,它將str2的字元輸出錯誤糾正了過來。所以我們一定要正確分析字元輸出不正常的原因再使用 toChinese函數。那麼我們如何來區分那些字元可能出現問題呢。下面有幾個主要的原則需要注意:

  1) 主要考慮字元變數情況。由於變數的字元編碼形式較為隱蔽,多次的變數間數值的改變和運算可能會引起字元集的改變;在變數與頁面所提交資料的各種操作中,較容易發生不同編碼格式字元進行運算的情況。

  2) 注意字元的讀入,讀出。大部分字元的編碼格式與目標編碼格式發生衝突的情況勢發生在字元的讀入和輸出過程。例如Form的提交,URL的得到以及控制項內容的顯示(如List控制項)等等。

  3) 必要的時候需要作測試。由於Java的中文問題的產生隨著Web伺服器,流覽器,運行環境和開發工具的不同都可能發生變化,所以為了更好的避免問題的發生,我們必須作一些針對性的測試。

  當然解決Java中文問題的方法並不局限於強制編碼輸出這一種。我們也可以採用下面的方法練解決:

  1) 以javac -encoding big5 sourcefile.java 或者javac -encoding gb2312 sourcefile.java的方式編譯根源程式。

  2) 採用Java2 JDK的中文本地化版本(http://java.sun.com/products/jdk/1.2/chinesejdk.html),但該版本是一種非官方的版本,Sun公司並不保證它的升級。

  資料庫訪問過程中的中文問題

  經過了上面的討論,對於資料庫訪問過程中所存在的中文問題也就不太難理解了。

  目前,大部分的JDBC驅動程式並不是針對中文系統來設計的(中文資料大都採用ISO-8859-1編碼方式),所以在資料讀寫過程中往往需要字元編碼的轉化。

  如果系統運行在中文作業系統平臺下,則:

  1) 中文字元的讀入,可以採用如下的代碼:



strChinese= new String(String(rs.getObject(j).toString().getBytes("ISO-8859-1"));

  對於Win2000平臺下,採用Weblogic 6.0所提供的JDBC驅動來讀入中文代碼可如下來編寫(例子中進行了字元運算):

Driver myDriver = (Driver) Class.forName("weblogic.jdbc.mssqlserver4.Driver").newInstance();

conn = myDriver.connect("jdbc:weblogic:mssqlserver4", props);

conn.setCatalog("labmanager");

Statement st = conn.createStatement();

[file://execute] a query

String testStr;

String testTempStr = new String() ;

testStr = new String(testTempStr.getBytes("ISO-8859-1"));//編碼轉化

DatabaseMetaData DBMetaData =conn.getMetaData();

ResultSet rs = DBMetaData.getTables(null, null,null,new String\[\]
{"TABLE"}
);
while (rs.next())
{ for(int j=1; j<=rs.getMetaData().getColumnCount(); j++)
{ testStr = testStr \+String(rs.getObject(j).toString().getBytes("ISO-8859-1")); }

}

  2) 中文的輸出。中文的輸出與讀入正好是個逆過程。我們需要將字元的系統缺省編碼轉化為JDBC支援的ISO-8859-1編碼。代碼可以如下編寫:

tempBytes=strInput.getText().getBytes();

SQLstr=new String(tempBytes,"ISO-8859-1");

  需要注意的是,不同的JDBC驅動對相同的資料庫的支援並不同,而同一類JDBC驅動對不同的資料庫的支援也不相同,也就是說我們的字元轉化代 碼在 JDBC驅動改變的時候必須作必要的測試才能確定其是否工作正常,不然我們反而會變成了畫蛇添足。例如對於i-net 的Una 2000 Driver Version 2.03 for MS SQL Server,我們根本就不需要做任何的編碼轉化就可以實現中文的正常操作。但是,由於很多的JDBC的驅動並沒有明確的給出其對中文字元的支援情況,所 以建議在使用JDBC時都作一下測試。

  結論

  事實上,Java中文處理之所以存在問題,其根本原因是由於被操作的中文字元(變數)的編碼格式與目標的編碼格式不同造成的,所有這些問題其實都是發生在字元的讀入、輸出過程中的,只要我們把握住這一環節,就可以更好的理解和處理Java的中文問題了。

JAVA 上加密演算法的實作範例

==轉貼自IBM網站==

最近有需要用到加密演算法的程式.看了好多資料.發現這篇說明得滿詳細的.

對稱式加密系統是一種傳統的加密演算法,是指訊息的傳送方和接收方共同使用同一把金鑰進行加解密。通常,使用的加密演算法比較簡便效率高,金鑰簡 短,加解密速度快,破解極其困難。但是加密的安全性依靠金鑰保管的安全性,在公開的電腦網路上安全地傳送和保管金鑰是一個困難的問題,並且如果在多使用者 的情況下金鑰的保管安全性也是一個問題。
第1章基礎知識

1.1. 對稱式加密系統

對稱式加密系統是一種傳統的加密演算法,是指訊息的傳送方和接收方共同使用同一把金鑰進行加解密。

通常,使用的加密演算法比較簡便效率高,金鑰簡短,加解密速度快,破解極其困難。但是加密的安全性依靠金鑰保管的安全性,在公開的電腦網路上安全地傳送和保管金鑰是一個困難的問題,並且如果在多使用者的情況下金鑰的保管安全性也是一個問題。

對稱式加密系統的代表是美國的DES

1.2. 訊息摘要

一個訊息摘要就是一個資料區塊的數位指紋。即對一個任意長度的一個資料區塊進行計算,產生一個唯一指印(對於SHA1是產生一個20位二進位元組)。

訊息摘要有兩個基本屬性︰

兩個不同的文件難以產生相同的摘要
難以對指定的摘要產生一個文件,而由該文件反推算出該指定的摘要
代表︰美國國家標準技術研究所的SHA1和麻省理工學院Ronald Rivest提出的MD5

1.3. Diffie-Hellman金鑰協議協定

金鑰協議協定是由公開金鑰加密系統的創始人Diffie和Hellman所提出的一種想法。

先決條件,容許兩名使用者在公開媒體上交換訊息以產生"一致"的,可以共享的金鑰

代表︰指數金鑰協議協定(Exponential Key Agreement Protocol)

1.4. 非對稱式加密演算法與公開金鑰架構

1976年,Dittie和Hellman為解決金鑰管理問題,在他們的奠基性的工作"密碼學的新方向"一文中,提出一種金鑰交換協定,容許在不安 全的媒體上透過通訊雙方交換訊息,安全地傳送私密金鑰。在此新想法的基礎上,很快出現了非對稱式金鑰加密系統,即公開金鑰加密系統。在公開金鑰系統中,加 密金鑰不同於解密金鑰,加密金鑰對大眾公開,誰都可以使用;解密金鑰只有解密人自己知道。它們分別稱為公開金鑰(Public key)和私密金鑰(Private key)。

迄今為止的所有公開金鑰加密架構中,RSA系統是最著名、最多使用的一種。RSA公開金鑰加密系統是由R.Rivest、A.Shamir和L.Adleman 教授於1977年提出的。RSA的取名就是來自於這三位發明者的姓的第一個字母

1.5. 數位簽章

所謂數位簽章就是訊息傳送者用其私密金鑰對從所傳送之文件中提取出的特性資料(或稱數位指紋)進行RSA演算法作業,以保證發信人無法抵賴曾發過該 訊息(即不可否認性),同時也確保訊息文件在經簽章後未被篡改(即完整性)。當訊息接收者收到文件後,就可以用傳送者的公開金鑰對數位簽章進行驗證。 

在數位簽章中有重要作用的數位指紋是透過一類別特殊的雜湊函式(HASH函式)產生的,對這些HASH函式的特殊要求是︰

接受的輸入文件資料沒有長度限制;
對任何輸入文件資料產生固定長度的摘要(數位指紋)輸出
從文件能方便地算出摘要;
難以對指定的摘要產生一個文件,而由該文件反推算出該指定的摘要;
兩個不同的文件難以產生相同的摘要
代表︰DSA


回到頂端

第2章在JAVA中的實作

2.1. 相關

Diffie-Hellman金鑰協議協定和DES程式需要JCE工具庫的支援,可以到 http://java.sun.com/security/index.html 下載JCE,並進行安裝。簡易安裝把 jce1.2.1\lib 下的所有內容複製到 %java_home%\lib\ext下,如果沒有ext目錄自行建立,再把jce1_2_1.jar和sunjce_provider.jar新增到 CLASSPATH內,更詳細說明請看相應使用者手冊

2.2. 訊息摘要MD5和SHA的使用

使用方法:

首先用產生一個MessageDigest類別,確定計算方法

java.security.MessageDigest alga=java.security.MessageDigest.getInstance("SHA-1");

加入要進行計算摘要的訊息

alga.update(myinfo.getBytes());

計算出摘要

byte[] digesta=alga.digest();

傳送給其它人你的訊息和摘要

其它人用相同的方法初始化,加入訊息,最後進行比較摘要是否相同

algb.isEqual(digesta,algb.digest())

相關API

java.security.MessageDigest 類別

static getInstance(String algorithm)

傳回一個MessageDigest物件,它實作指定的演算法

參數:演算法名稱,如 SHA-1 或MD5

void update (byte input)

void update (byte[] input)

void update(byte[] input, int offset, int len)

加入要進行計算摘要的訊息

byte[] digest()

完成計算,傳回計算得到的摘要(對於MD5是16位,SHA是20位)

void reset()

重設

static boolean isEqual(byte[] digesta, byte[] digestb)

Exception handling

Exception handling is very crucial part often overlooked in web application development that has ramifications far beyond deployment. You know how to handle exceptions using the built-in Java construct to catch one and handle it appropriately. But what is appropriate? The basic rationale behind exception handling is to catch errors and report them. What is the level of detail needed in reporting the exception? How should the user be notified of the exception? How should customer support handle problem reports and track and trace the exception from the logs? As a developer where do you handle the exceptions? These are some of the major questions we will answer in this chapter first from a generic way and then as applicable to Struts applications.

Under normal circumstances when you catch the exception in a method, you print the stack trace using the printStacktrace() method or declare the method to throw the exception. In a production system, when an exception is thrown it's likely that the system is unable to process end user's request. When such an exception occurs, the end user normally expects the following:

§ A message indicating that an error has occurred

§ A unique error identifier that he can use while reporting it to customer support.

§ Quick resolution of the problem.

The customer support should have access to back-end mechanisms to resolve the problem. The customer service team should, for example, receive immediate error notification, so that the service representative is aware of the problem before the customer calls for resolution. Furthermore, the service representative should be able to use the unique error identifier (reported by the user) to lookup the production log files for quick identification of the problem - preferably up to the exact line number (or at least the exact method). In order to provide both the end user and the support team with the tools and services they need, you as a developer must have a clear picture, as you are building a system, of everything that can go wrong with it once it is deployed.

18.1 Exception Handling Basics

It is common usage by the developers to put System.out.println() to track the exception and flow through the code. While they come in handy, they have to be avoided due to the following reasons:1. System.out.println is expensive. These calls are synchronized for the duration of disk I/O, which significantly slows throughput.

2. By default, stack traces are logged to the console. But browsing the console for an exception trace isn't feasible in a production system.

3. In addition, they aren't guaranteed to show up in the production system, because system administrators can map System.out and System.errs to ' ' [>nul]on NT and dev/nul on UNIX. Moreover, if you're running the J2EE app server as an NT service, you won't even have a console.

4. Even if you redirect the console log to an output file, chances are that the file will be overwritten when the production J2EE app servers are restarted.

5. UsingSystem.out.println during testing and then removing them before production isn't an elegant solution either, because doing so means your production code will not function the same as your test code.What you need is a mechanism to declaratively control logging so that your test code and your production code are the same, and performance overhead incurred in production is minimal when logging is declaratively turned off. The obvious solution here is to use a logging utility. It is pretty customary these days to use a utility like Log4J (http://jakarta.apache.org/log4j) for logging. With the right coding conventions in place, a logging utility will pretty much take care of recording any type of messages, whether a system error or some warning. However it is up to you as a developer to make the best use of the utilities. It requires a lot of forethought to handle exceptions effectively. In this chapter we will use Log4J to log exceptions effectively. Hence we will review Log4J before proceeding to look at some commonly accepted principles of Exception handling in Java.

2008年4月25日 星期五

Java程式三十條規則

這是一些相當不錯的忠告!每個規則都很有分量!都是長期經驗積累的總結,希望能對您有所幫助,使您寫出高質素的JAVA程式碼。

(1)類別別名稱首字母應該大寫。欄位、方法以及物件(控制碼)的首字母應小寫。對於所有識別字,其中包含的所有單詞都應緊靠在一起,而且大寫中間單詞的首字母。例如:
ThisIsAClassName
thisIsMethodOrFieldName
若在定義中出現了常數初始化字元,則大寫static final基本類別型識別字中的所有字母。這樣便可標誌出它們屬於編譯期的常數。
Java包(Package)屬於一種特殊情況:它們全都是小寫字母,即便中間的單詞亦是如此。對於功能變數名稱副檔名稱,如com,org,net或者edu等,全部都應小寫(這也是Java 1.1和Java 1.2的區別之一)。

(2) 爲了常規用途而建立一個類別別時,請採取"經典形式",並包含對下述元素的定義:

equals()
hashCode()
toString()
clone()(implement Cloneable)
implement Serializable

(3) 對於自己建立的每一個類別別,都考慮置入一個main(),其中包含了用於測試那個類別別的程式碼。爲使用一個專案中的類別別,我們沒必要刪除測試程式碼。若進行了任何形式的改動,可方便地返回測試。這些程式碼也可作爲如何使用類別的一個範例使用。

(4) 應將方法設計成簡要的、功能性單元,用它描述和實現一個不連續的類別別介面部分。理想情況下,方法應簡明扼要。若長度很大,可考慮通過某種方式將其分割成 較短的幾個方法。這樣做也便於類別別內程式碼的重復使用(有些時候,方法必須非常大,但它們仍應只做同樣的一件事情)。

(5) 設計一個類別時,請設身處地爲客戶程式師考慮一下(類別的使用方法應該是非常明確的)。然後,再設身處地爲管理程式碼的人考慮一下(預計有可能進行哪些形式的修改,想想用什麽方法可把它們變得更簡單)。
(6) 使類別盡可能短小精悍,而且只解決一個特定的問題。下面是對類別設計的一些建議:
■一個複雜的開關語句:考慮採用"多形"機制
■數量衆多的方法涉及到類別型差別極大的操作:考慮用幾個類別來分別實現
■許多成員變數在特徵上有很大的差別:考慮使用幾個類別

(7) 讓一切東西都盡可能地"私有"

private。可使庫的某一部分"公共化"(一個方法、類別或者一個欄位等等),就永遠不能把它拿出。若強行拿出,就可能破壞其他人現有的程式 碼,使他們不得不重新編寫和設計。若只公佈自己必須公佈的,就可放心大膽地改變其他任何東西。在多執行緒環境中,隱私是特別重要的一個因素,只有 private欄位才能在非同步使用的情況下受到保護。

(8) 謹惕"巨大物件綜合症"。對一些習慣于程序性思維、且初涉OOP領域的新手,往往喜歡先寫一個順序執行的程式,再把它嵌入一個或兩個巨大的物件裏。根據程序性原理,物件表達的應該是應用程式的概念,而非應用程式本身。

(9) 若不得已進行一些不太雅觀的程式,至少應該把那些程式碼置於一個類別的內部。

(10) 任何時候只要發現類別與類別之間結合得非常緊密,就需要考慮是否採用內部類別,從而改善編碼及維護工作(參見第14章14.1.2小節的"用內部類別改進程式碼")。

(11) 盡可能細緻地加上注釋,並用javadoc注釋文檔語法生成自己的程式文件。

(12) 避免使用"魔術數字",這些數字很難與程式碼很好地配合。如以後需要修改它,無疑會成爲一場噩夢,因爲根本不知道"100"到底是指"陣列大小"還是"其 他全然不同的東西"。所以,我們應建立一個常數,並爲其使用具有說服力的描述性名稱,並在整個程式中都採用常數識別字。這樣可使程式更易理解以及更易維 護。

(13) 涉及編譯器和異常的時候,通常希望重新丟棄在編譯器中捕獲的任何異常------如果它造成了那個物件的建立失敗。這樣一來,調用者就不會以爲那個物件已正確地建立,從而盲目地繼續。

(14) 當客戶程式師用完物件以後,若你的類別要求進行任何清除工作,可考慮將清除程式碼置於一個良好定義的方法裏,採用類別別似於cleanup()這樣的名 字,明確表 明自己的用途。除此以外,可在類別別內放置一個boolean(布林)標記,指出物件是否已被清除。在類別別的finalize()方法裏,請確定物件已 被清除, 並已丟棄了從RuntimeException繼承的一個類別(如果還沒有的話),從而指出一個編程錯誤。在採取象這樣的方案之前,請確定 finalize ()能夠在自己的系統中工作(可能需要調用System.runFinalizersOnExit(true),從而確保這一行爲)。

(15) 在一個特定的作用域內,若一個物件必須清除(非由垃圾收集機制處理),請採用下述方法:初始化物件;若成功,則立即進入一個含有finally從句的try塊,開始清除工作。

(16) 若在初始化過程中需要覆蓋(取消)finalize(),請記住採用super.finalize()(若Object屬於我們的直接超類別別,則無此必 要)。在對finalize()進行覆蓋的過程中,對super.finalize()的調用應屬於最後一個行動,而不應是第一個行動,這樣可確保在需要 基礎類別元件的時候它們依然有效。

(17) 建立大小固定的物件集合時,請將它們傳輸至一個陣列(若準備從一個方法裏返回這個集合,更應如此操作)。這樣一來,我們就可享受到陣列在編譯期進行類別型檢查的好處。此外,爲使用它們,陣列的接收者也許並不需要將物件"造型"到陣列裏。

(18) 儘量使用interfaces,不要使用abstract類別別。若已知某樣東西準備成爲一個基礎類別別,那麽第一個選擇應是將其變成一個 interface(介面)。只有在不得不使用方法定義或者成員變數的時候,才需要將其變成一個abstract(抽象)類別別。介面主要描述了客戶希望 做什麽事情,而一個類別則致力於(或允許)具體的實施細節。

(19) 在編譯器內部,只進行那些將物件設爲正確狀態所需的工作。盡可能地避免調用其他方法,因爲那些方法可能被其他人覆蓋或取消,從而在構建過程中産生不可預知的結果(參見第7章的詳細說明)。

(20) 物件不應只是簡單地容納一些資料;它們的行爲也應得到良好的定義。

(21) 在現成類別的基礎上建立新類別時,請首先選擇"新建"或"創作"。只有自己的設計要求必須繼承時,才應考慮這方面的問題。若在本來允許新建的場合使用了繼承,則整個設計會變得沒有必要地複雜。

(22) 用繼承及方法覆蓋來表示行爲間的差異,而用欄位表示狀態間的區別。一個非常極端的例子是通過對不同類別的繼承來表示顔色,這是絕對應該避免的:應直接使用一個"顔色"欄位。

(23) 爲避免編程時遇到麻煩,請保證在自己類別路徑指到的任何地方,每個名字都僅對應一個類別。否則,編譯器可能先找到同名的另一個類別,並報告出錯消息。若懷疑自己碰到了類別路徑問題,請試試在類別路徑的每一個起點,搜索一下同名的.class文件。

(24) 在Java 1.1 AWT中使用事件"適配器"時,特別容易碰到一個陷阱。若覆蓋了某個適配器方法,同時拼寫方法沒有特別講究,最後的結果就是新添加一個方法,而不是覆蓋現 成方法。然而,由於這樣做是完全合法的,所以不會從編譯器或運行期系統獲得任何出錯提示------只不過程式碼的工作就變得不正常了。

(25) 用合理的設計方案消除"僞功能"。也就是說,假若只需要建立類別的一個物件,就不要提前限制自己使用應用程式,並加上一條"只生成其中一個"注釋。請考慮 將 其封裝成一個"獨生子"的形式。若在主程序裏有大量散亂的程式碼,用於建立自己的物件,請考慮採納一種創造性的方案,將些程式碼封裝起來。

(26) 警惕"分析癱瘓"。請記住,無論如何都要提前瞭解整個專案的狀況,再去考察其中的細節。由於把握了全局,可快速認識自己未知的一些因素,防止在考察細節的時候陷入"死邏輯"中。

(27) 警惕"過早優化"。首先讓它運行起來,再考慮變得更快------但只有在自己必須這樣做、而且經證實在某部分程式碼中的確存在一個性能瓶頸的時候,才應 進行優化。除非用專門的工具分析瓶頸,否則很有可能是在浪費自己的時間。性能提升的隱含代價是自己的程式碼變得難於理解,而且難於維護。

(28) 請記住,閱讀程式碼的時間比寫程式碼的時間多得多。思路清晰的設計可獲得易於理解的程式,但注釋、細緻的解釋以及一些示例往往具有不可估量的價值。無論對 你自己,還是對後來的人,它們都是相當重要的。如對此仍有懷疑,那麽請試想自己試圖從聯機Java文檔裏找出有用資訊時碰到的挫折,這樣或許能將你說服。

(29) 如認爲自己已進行了良好的分析、設計或者實施,那麽請稍微更換一下思維角度。試試邀請一些外來人士------並不一定是專家,但可以是來自本公司其他部 門的人。 請他們用完全新鮮的眼光考察你的工作,看看是否能找出你一度熟視無睹的問題。採取這種方式,往往能在最適合修改的階段找出一些關鍵性的問題,避免産品發行 後再解決問題而造成的金錢及精力方面的損失。

(30) 良好的設計能帶來最大的回報。簡言之,對於一個特定的問題,通常會花較長的時間才能找到一種最恰當的解決方案。但一旦找到了正確的方法,以後的工作就輕鬆 多了,再也不用經歷數小時、數天或者數月的痛苦掙扎。我們的努力工作會帶來最大的回報(甚至無可估量)。而且由於自己傾注了大量心血,最終獲得一個出色的 設計方案,成功的快感也是令人心動的。堅持抵制草草完工的誘惑------那樣做往往得不償失。