2008年4月28日 星期一

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