2006年11月23日
?現在有不少網站在用戶填寫表單時,同時要求填寫驗證碼,驗證碼的一個目的就是防范一些惡意的網站下載軟件,這些軟件能通過遍歷鏈接而將網站的所有網頁下載。還可以防止用戶不經過本網站的頁面而使用網站的資源。所以現在有不少網站都使用了驗證碼技術,驗證碼通常是一個在WEB服務器上生成的隨機字符串,同時以某種方式保存起來,比如保存到與當前的Session中,然后在用戶提交網頁時與用戶輸入的驗證比較是否一致,然而如果直接以明文的方式,還是不能防范一些功能較強的自動填寫表格的軟件。所以一般將驗證碼以圖片的形式顯示出來,同時可以將在圖片中顯示的字符串進行一些處理,比如使用旋轉字符,添加背景紋理等技術以增大被軟件識別的難度。下面簡要介紹一下如果實現這種驗證碼: 首先實現一個servlet用來生成圖片(當然也可以用jsp實現): import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import com.sun.image.codec.jpeg.*;
import java.awt.*;
import com.sun.image.codec.jpeg.*;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.geom.GeneralPath;
import javax.swing.*;
import java.math.*;
public class Servlet1
extends HttpServlet {
//Process the HTTP Get request
public void doGet(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
response.setContentType(CONTENT_TYPE);
response.setContentType("image/jpeg"); //必須設置ContentType為image/jpeg
int length = 4; //設置默認生成4個數字
Date d = new Date();
long lseed = d.getTime();
java.util.Random r = new Random(lseed); //設置隨機種子
if (request.getParameter("length") != null) {
try {
length = Integer.parseInt(request.getParameter("length"));
}
catch (NumberFormatException e) {
}
}
StringBuffer str = new StringBuffer();
for (int i = 0; i <length; i++) {
str.append(r.nextInt(9)); //生成隨機數字
}
//可以在此加入保存驗證碼的代碼
//創建內存圖像
BufferedImage bi = new BufferedImage(40, 16, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bi.createGraphics();
g.clearRect(0, 0, 16, 40);
g.setColor(Color.green.CYAN);
g.drawString(str.toString(), 4, 12);
try {
//使用JPEG編碼,輸出到response的輸出流
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(response.
getOutputStream());
JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(bi);
param.setQuality(1.0f, false);
encoder.setJPEGEncodeParam(param);
encoder.encode(bi);
}
catch (Exception ex) {
}
}
} |
然后在需求顯示驗證碼的加入以下代碼就可以了
<img alt="" src="/WebModule1/servlet1" width="40" height="16"/> |
將/WebModule1/servlet1替換成你用來生成驗證碼的servlet的全路徑。
第一,談談final, finally, finalize的區別。 第二,Anonymous Inner Class (匿名內部類) 是否可以extends(繼承)其它類,是否可以implements(實現)interface(接口)? 第三,Static Nested Class 和 Inner Class的不同,說得越多越好(面試題有的很籠統)。 第四,&和&&的區別。 第五,HashMap和Hashtable的區別。 第六,Collection 和 Collections的區別。 第七,什么時候用assert。 第八,GC是什么? 為什么要有GC? 第九,String s = new String("xyz");創建了幾個String Object? 第十,Math.round(11.5)等於多少? Math.round(-11.5)等於多少? 第十一,short s1 = 1; s1 = s1 + 1;有什么錯? short s1 = 1; s1 += 1;有什么錯? 第十二,sleep() 和 wait() 有什么區別? 第十三,Java有沒有goto? 第十四,數組有沒有length()這個方法? String有沒有length()這個方法? 第十五,Overload和Override的區別。Overloaded的方法是否可以改變返回值的類型? 第十六,Set里的元素是不能重復的,那么用什么方法來區分重復與否呢? 是用==還是equals()? 它們有何區別? 第十七,給我一個你最常見到的runtime exception。 第十八,error和exception有什么區別? 第十九,List, Set, Map是否繼承自Collection接口? 第二十,abstract class和interface有什么區別? 第二十一,abstract的method是否可同時是static,是否可同時是native,是否可同時是synchronized? 第二十二,接口是否可繼承接口? 抽象類是否可實現(implements)接口? 抽象類是否可繼承實體類(concrete class)? 第二十三,啟動一個線程是用run()還是start()? 第二十四,構造器Constructor是否可被override? 第二十五,是否可以繼承String類? 第二十六,當一個線程進入一個對象的一個synchronized方法后,其它線程是否可進入此對象的其它方法? 第二十七,try {}里有一個return語句,那么緊跟在這個try后的finally {}里的code會不會被執行,什么時候被執行,在return前還是后? 第二十八,編程題: 用最有效率的方法算出2乘以8等於幾? 第二十九,兩個對象值相同(x.equals(y) == true),但卻可有不同的hash code,這句話對不對? 第三十,當一個對象被當作參數傳遞到一個方法后,此方法可改變這個對象的屬性,并可返回變化后的結果,那么這里到底是值傳遞還是引用傳遞? 第三十一,swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上? 第三十二,編程題: 寫一個Singleton出來。
===========================
以下是答案:
===========================
第一,談談final, finally, finalize的區別。 final—修飾符(關鍵字)如果一個類被聲明為final,意味著它不能再派生出新的子類,不能作為父類被繼承。因此一個類不能既被聲明為 abstract的,又被聲明為final的。將變量或方法聲明為final,可以保證它們在使用中不被改變。被聲明為final的變量必須在聲明時給定初值,而在以后的引用中只能讀取,不可修改。被聲明為final的方法也同樣只能使用,不能重載 finally—再異常處理時提供 finally 塊來執行任何清除操作。如果拋出一個異常,那么相匹配的 catch 子句就會執行,然后控制就會進入 finally 塊(如果有的話)。 finalize—方法名。Java 技術允許使用 finalize() 方法在垃圾收集器將對象從內存中清除出去之前做必要的清理工作。這個方法是由垃圾收集器在確定這個對象沒有被引用時對這個對象調用的。它是在 Object 類中定義的,因此所有的類都繼承了它。子類覆蓋 finalize() 方法以整理系統資源或者執行其他清理工作。finalize() 方法是在垃圾收集器刪除對象之前對這個對象調用的。 第二,Anonymous Inner Class (匿名內部類) 是否可以extends(繼承)其它類,是否可以implements(實現)interface(接口)? 匿名的內部類是沒有名字的內部類。不能extends(繼承) 其它類,但一個內部類可以作為一個接口,由另一個內部類實現。 第三,Static Nested Class 和 Inner Class的不同,說得越多越好(面試題有的很籠統)。 Nested Class (一般是C++的說法),Inner Class (一般是JAVA的說法)。Java內部類與C++嵌套類最大的不同就在于是否有指向外部的引用上。具體可見http: //www.frontfree.net/articles/services/view.asp?id=704&page=1 注: 靜態內部類(Inner Class)意味著1創建一個static內部類的對象,不需要一個外部類對象,2不能從一個static內部類的一個對象訪問一個外部類對象 Hashtable繼承自Dictionary類,而HashMap是Java1.2引進的Map interface的一個實現
HashMap允許將null作為一個entry的key或者value,而Hashtable不允許
還有就是,HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因為contains方法容易讓人引起誤解。
最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在 多個線程訪問Hashtable時,不需要自己為它的方法實現同步,而HashMap 就必須為之提供外同步。 第四,&和&&的區別。 &是位運算符。&&是布爾邏輯運算符。 第五,HashMap和Hashtable的區別。 都屬于Map接口的類,實現了將惟一鍵映射到特定的值上。 HashMap 類沒有分類或者排序。它允許一個 null 鍵和多個 null 值。 Hashtable 類似于 HashMap,但是不允許 null 鍵和 null 值。它也比 HashMap 慢,因為它是同步的。 Hashtable繼承自Dictionary類,而HashMap是Java1.2引進的Map interface的一個實現
還有就是,HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因為contains方法容易讓人引起誤解。
最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在 多個線程訪問Hashtable時,不需要自己為它的方法實現同步,而HashMap 就必須為之提供外同步。
第六,Collection 和 Collections的區別。 Collections是個java.util下的類,它包含有各種有關集合操作的靜態方法。 Collection是個java.util下的接口,它是各種集合結構的父接口. 第七,什么時候用assert。 斷言是一個包含布爾表達式的語句,在執行這個語句時假定該表達式為 true。如果表達式計算為 false,那么系統會報告一個 AssertionError。它用于調試目的: assert(a > 0); // throws an AssertionError if a <= 0 斷言可以有兩種形式: assert Expression1 ; assert Expression1 : Expression2 ; Expression1 應該總是產生一個布爾值。 Expression2 可以是得出一個值的任意表達式。這個值用于生成顯示更多調試信息的 String 消息。 斷言在默認情況下是禁用的。要在編譯時啟用斷言,需要使用 source 1.4 標記: javac -source 1.4 Test.java 要在運行時啟用斷言,可使用 -enableassertions 或者 -ea 標記。 要在運行時選擇禁用斷言,可使用 -da 或者 -disableassertions 標記。 要系統類中啟用斷言,可使用 -esa 或者 -dsa 標記。還可以在包的基礎上啟用或者禁用斷言。 可以在預計正常情況下不會到達的任何位置上放置斷言。斷言可以用于驗證傳遞給私有方法的參數。不過,斷言不應該用于驗證傳遞給公有方法的參數,因為不管是否啟用了斷言,公有方法都必須檢查其參數。不過,既可以在公有方法中,也可以在非公有方法中利用斷言測試后置條件。另外,斷言不應該以任何方式改變程序的狀態。 第八,GC是什么? 為什么要有GC? (基礎)。 GC是垃圾收集器。Java 程序員不用擔心內存管理,因為垃圾收集器會自動進行管理。要請求垃圾收集,可以調用下面的方法之一: System.gc() Runtime.getRuntime().gc() 第九,String s = new String("xyz");創建了幾個String Object? 兩個對象,一個是“xyx”,一個是指向“xyx”的引用對象s。 第十,Math.round(11.5)等於多少? Math.round(-11.5)等於多少? Math.round(11.5)返回(long)12,Math.round(-11.5)返回(long)-11; 第十一,short s1 = 1; s1 = s1 + 1;有什么錯? short s1 = 1; s1 += 1;有什么錯? short s1 = 1; s1 = s1 + 1;有錯,s1是short型,s1+1是int型,不能顯式轉化為short型。可修改為s1 =(short)(s1 + 1) 。short s1 = 1; s1 += 1正確。 第十二,sleep() 和 wait() 有什么區別? 搞線程的最愛 sleep()方法是使線程停止一段時間的方法。在sleep 時間間隔期滿后,線程不一定立即恢復執行。這是因為在那個時刻,其它線程可能正在運行而且沒有被調度為放棄執行,除非(a)“醒來”的線程具有更高的優先級 (b)正在運行的線程因為其它原因而阻塞。 wait()是線程交互時,如果線程對一個同步對象x 發出一個wait()調用,該線程會暫停執行,被調對象進入等待狀態,直到被喚醒或等待時間到。 第十三,Java有沒有goto? Goto—java中的保留字,現在沒有在java中使用。 第十四,數組有沒有length()這個方法? String有沒有length()這個方法? 數組沒有length()這個方法,有length的屬性。 String有有length()這個方法。 第十五,Overload和Override的區別。Overloaded的方法是否可以改變返回值的類型? 方法的重寫Overriding和重載Overloading是Java多態性的不同表現。重寫Overriding是父類與子類之間多態性的一種表現,重載Overloading是一個類中多態性的一種表現。如果在子類中定義某方法與其父類有相同的名稱和參數,我們說該方法被重寫 (Overriding)。子類的對象使用這個方法時,將調用子類中的定義,對它而言,父類中的定義如同被“屏蔽”了。如果在一個類中定義了多個同名的方法,它們或有不同的參數個數或有不同的參數類型,則稱為方法的重載(Overloading)。Overloaded的方法是可以改變返回值的類型。 第十六,Set里的元素是不能重復的,那么用什么方法來區分重復與否呢? 是用==還是equals()? 它們有何區別? Set里的元素是不能重復的,那么用iterator()方法來區分重復與否。equals()是判讀兩個Set是否相等。 equals()和==方法決定引用值是否指向同一對象equals()在類中被覆蓋,為的是當兩個分離的對象的內容和類型相配的話,返回真值。 第十七,給我一個你最常見到的runtime exception。 ArithmeticException, ArrayStoreException, BufferOverflowException, BufferUnderflowException, CannotRedoException, CannotUndoException, ClassCastException, CMMException, ConcurrentModificationException, DOMException, EmptyStackException, IllegalArgumentException, IllegalMonitorStateException, IllegalPathStateException, IllegalStateException, ImagingOpException, IndexOutOfBoundsException, MissingResourceException, NegativeArraySizeException, NoSuchElementException, NullPointerException, ProfileDataException, ProviderException, RasterFormatException, SecurityException, SystemException, UndeclaredThrowableException, UnmodifiableSetException, UnsupportedOperationException 第十八,error和exception有什么區別? error 表示恢復不是不可能但很困難的情況下的一種嚴重問題。比如說內存溢出。不可能指望程序能處理這樣的情況。 exception 表示一種設計或實現問題。也就是說,它表示如果程序運行正常,從不會發生的情況。 第十九,List, Set, Map是否繼承自Collection接口? List,Set是 Map不是 第二十,abstract class和interface有什么區別? 聲明方法的存在而不去實現它的類被叫做抽象類(abstract class),它用于要創建一個體現某些基本行為的類,并為該類聲明方法,但不能在該類中實現該類的情況。不能創建abstract 類的實例。然而可以創建一個變量,其類型是一個抽象類,并讓它指向具體子類的一個實例。不能有抽象構造函數或抽象靜態方法。Abstract 類的子類為它們父類中的所有抽象方法提供實現,否則它們也是抽象類為。取而代之,在子類中實現該方法。知道其行為的其它類可以在類中實現這些方法。 接口(interface)是抽象類的變體。在接口中,所有方法都是抽象的。多繼承性可通過實現這樣的接口而獲得。接口中的所有方法都是抽象的,沒有一個有程序體。接口只可以定義static final成員變量。接口的實現與子類相似,除了該實現類不能從接口定義中繼承行為。當類實現特殊接口時,它定義(即將程序體給予)所有這種接口的方法。然后,它可以在實現了該接口的類的任何對象上調用接口的方法。由于有抽象類,它允許使用接口名作為引用變量的類型。通常的動態聯編將生效。引用可以轉換到接口類型或從接口類型轉換,instanceof 運算符可以用來決定某對象的類是否實現了接口。 第二十一,abstract的method是否可同時是static,是否可同時是native,是否可同時是synchronized? 都不能 第二十二,接口是否可繼承接口? 抽象類是否可實現(implements)接口? 抽象類是否可繼承實體類(concrete class)? 接口可以繼承接口。抽象類可以實現(implements)接口,抽象類是否可繼承實體類,但前提是實體類必須有明確的構造函數。 第二十三,啟動一個線程是用run()還是start()? 啟動一個線程是調用start()方法,使線程所代表的虛擬處理機處于可運行狀態,這意味著它可以由JVM調度并執行。這并不意味著線程就會立即運行。run()方法可以產生必須退出的標志來停止一個線程。 第二十四,構造器Constructor是否可被override? 構造器Constructor不能被繼承,因此不能重寫Overriding,但可以被重載Overloading。 第二十五,是否可以繼承String類? String類是final類故不可以繼承。 第二十六,當一個線程進入一個對象的一個synchronized方法后,其它線程是否可進入此對象的其它方法? 不能,一個對象的一個synchronized方法只能由一個線程訪問。 第二十七,try {}里有一個return語句,那么緊跟在這個try后的finally {}里的code會不會被執行,什么時候被執行,在return前還是后? 會執行,在return前執行。 第二十八,編程題: 用最有效率的方法算出2乘以8等於幾? 有C背景的程序員特別喜歡問這種問題。 2 << 3 第二十九,兩個對象值相同(x.equals(y) == true),但卻可有不同的hash code,這句話對不對? 不對,有相同的hash code。 第三十,當一個對象被當作參數傳遞到一個方法后,此方法可改變這個對象的屬性,并可返回變化后的結果,那么這里到底是值傳遞還是引用傳遞? 是值傳遞。Java 編程語言只由值傳遞參數。當一個對象實例作為一個參數被傳遞到方法中時,參數的值就是對該對象的引用。對象的內容可以在被調用的方法中改變,但對象的引用是永遠不會改變的。 第三十一,swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上? switch(expr1)中,expr1是一個整數表達式。因此傳遞給 switch 和 case 語句的參數應該是 int、 short、 char 或者 byte。long,string 都不能作用于swtich。 第三十二,編程題: 寫一個Singleton出來。 Singleton模式主要作用是保證在Java應用程序中,一個類Class只有一個實例存在。 一般Singleton模式通常有幾種種形式: 第一種形式: 定義一個類,它的構造函數為private的,它有一個static的private的該類變量,在類初始化時實例話,通過一個public的getInstance方法獲取對它的引用,繼而調用其中的方法。 public class Singleton { private Singleton(){} //在自己內部定義自己一個實例,是不是很奇怪? //注意這是private 只供內部調用 private static Singleton instance = new Singleton(); //這里提供了一個供外部訪問本class的靜態方法,可以直接訪問 public static Singleton getInstance() { return instance; } } 第二種形式: public class Singleton { private static Singleton instance = null; public static synchronized Singleton getInstance() { //這個方法比上面有所改進,不用每次都進行生成對象,只是第一次 //使用時生成實例,提高了效率! if (instance==null) instance=new Singleton(); return instance; } } 其他形式: 定義一個類,它的構造函數為private的,所有方法為static的。 一般認為第一種形式要更加安全 ====================================
以下是PCOnline的網友評論:http://cmt.pconline.com.cn/topic.jsp?id=115964&reverse=1
|
16
樓
匿名 發表于:2005-01-06 11:47:56 |
0
0
|
? |
以下答案有問題:? "第二十六,當一個線程進入一個對象的一個synchronized方法后,其它線程是否可進入此對象的其它方法? 不能,一個對象的一個synchronized方法只能由一個線程訪問。" 如果另一個方法沒有實現synchronized則可以被其他線程訪問.
|
|
|
20
樓
? 發表于:2005-01-10 21:06:23 |
1
0
|
? |
計算?2?*?8?最快的方法顯然是?return?16; |
|
|
21
樓
撒旦盧 發表于:2005-01-13 10:46:01  |
0
0
|
? |
????有沒有好好研究過Java虛擬機規范呀? ????我不用試就知道?2<<3的速度要比2×8速度慢。測試結果也是這樣的。Java1.4?已經把乘除法優化了。 |
|
|
26
樓
wht 發表于:2005-02-17 11:46:04 |
0
0
|
? |
回復?21?樓(撒旦盧):再怎么優化,+-運算都要變成移位和與或,這是由芯片決定得. |
|
|
28
樓
PracticalJava 發表于:2005-03-05 14:32:50 |
0
0
|
? |
對,22樓主說得對,第二十六題的確有問題,答案是錯的。應該是能執行該對象的其他方法。因為synchornized只要獲得的是不同對象作為的機鎖,就可以運行一個被synchornized的方法。 |
|
|
30
樓
zhuzhou 發表于:2005-03-10 15:35:44 |
1
0
|
? |
測試代碼如下: ????????long?t1?=0; ????????long?t2?=0; ????????int?k?=?2; ????????int?m?=?3; ????????int?n?=?0; ????????int?max?=?100000000;
????????t1?=?new?Date().getTime(); ????????for(int?i=0;i<max;i++)? ????????????n?=?k^m; ????????t2?=?new?Date().getTime(); ????????System.out.println("采用乘方花了?"+(t2-t1)+"?秒"); ???????? //????????t1?=?new?Date().getTime(); //????????for(int?i=0;i<max;i++)? //????????????n?=?k*k*k; //????????t2?=?new?Date().getTime(); //????????System.out.println("采用連乘花了?"+(t2-t1)+"?秒"); ???????? //????????t1?=?new?Date().getTime(); //????????for(int?i=0;i<max;i++)? //????????????n?=?k<<m; //????????t2?=?new?Date().getTime(); //????????System.out.println("采用移位花了?"+(t2-t1)+"?秒");
測試的時候只開放其中一段代碼 結果如下: 乘方 移位 連乘 1億次 機器甲 219 375 422 機器甲 235 375 422 機器甲 219 375 422 機器甲 219 391 438 機器甲 234 375 422 1億次 機器乙 281 297 297 機器乙 281 297 277 機器乙 282 312 296 機器乙 297 297 297 機器乙 281 312 297 機器乙 282 296 312 10億次 機器乙 2796 3015 2985 機器乙 2813 3079 2985 機器乙 2812 3109 3062 機器乙 2812 3016 3000 機器乙 2812 3031 2985
好像移位不夠快哦
|
|
1. 介紹
1)DOM(JAXP?Crimson解析器) ??????? DOM是用與平臺和語言無關的方式表示XML文檔的官方W3C標準。DOM是以層次結構組織的節點或信息片斷的集合。這個層次結構允許開發人員在樹中尋找特定信息。分析該結構通常需要加載整個文檔和構造層次結構,然后才能做任何工作。由于它是基于信息層次的,因而DOM被認為是基于樹或基于對象的。DOM以及廣義的基于樹的處理具有幾個優點。首先,由于樹在內存中是持久的,因此可以修改它以便應用程序能對數據和結構作出更改。它還可以在任何時候在樹中上下導航,而不是像SAX那樣是一次性的處理。DOM使用起來也要簡單得多。
2)SAX
??????? SAX處理的優點非常類似于流媒體的優點。分析能夠立即開始,而不是等待所有的數據被處理。而且,由于應用程序只是在讀取數據時檢查數據,因此不需要將數據存儲在內存中。這對于大型文檔來說是個巨大的優點。事實上,應用程序甚至不必解析整個文檔;它可以在某個條件得到滿足時停止解析。一般來說,SAX還比它的替代者DOM快許多。 ? 選擇DOM還是選擇SAX? 對于需要自己編寫代碼來處理XML文檔的開發人員來說,? 選擇DOM還是SAX解析模型是一個非常重要的設計決策。 DOM采用建立樹形結構的方式訪問XML文檔,而SAX采用的事件模型。
DOM解析器把XML文檔轉化為一個包含其內容的樹,并可以對樹進行遍歷。用DOM解析模型的優點是編程容易,開發人員只需要調用建樹的指令,然后利用navigation?APIs訪問所需的樹節點來完成任務??梢院苋菀椎奶砑雍托薷臉渲械脑?。然而由于使用DOM解析器的時候需要處理整個XML文檔,所以對性能和內存的要求比較高,尤其是遇到很大的XML文件的時候。由于它的遍歷能力,DOM解析器常用于XML文檔需要頻繁的改變的服務中。
SAX解析器采用了基于事件的模型,它在解析XML文檔的時候可以觸發一系列的事件,當發現給定的tag的時候,它可以激活一個回調方法,告訴該方法制定的標簽已經找到。SAX對內存的要求通常會比較低,因為它讓開發人員自己來決定所要處理的tag。特別是當開發人員只需要處理文檔中所包含的部分數據時,SAX這種擴展能力得到了更好的體現。但用SAX解析器的時候編碼工作會比較困難,而且很難同時訪問同一個文檔中的多處不同數據。
3)JDOM????????? ?http://www.jdom.org
????????? JDOM的目的是成為Java特定文檔模型,它簡化與XML的交互并且比使用DOM實現更快。由于是第一個Java特定模型,JDOM一直得到大力推廣和促進。正在考慮通過“Java規范請求JSR-102”將它最終用作“Java標準擴展”。從2000年初就已經開始了JDOM開發。
JDOM與DOM主要有兩方面不同。首先,JDOM僅使用具體類而不使用接口。這在某些方面簡化了API,但是也限制了靈活性。第二,API大量使用了Collections類,簡化了那些已經熟悉這些類的Java開發者的使用。
JDOM文檔聲明其目的是“使用20%(或更少)的精力解決80%(或更多)Java/XML問題”(根據學習曲線假定為20%)。JDOM對于大多數Java/XML應用程序來說當然是有用的,并且大多數開發者發現API比DOM容易理解得多。JDOM還包括對程序行為的相當廣泛檢查以防止用戶做任何在XML中無意義的事。然而,它仍需要您充分理解XML以便做一些超出基本的工作(或者甚至理解某些情況下的錯誤)。這也許是比學習DOM或JDOM接口都更有意義的工作。
JDOM自身不包含解析器。它通常使用SAX2解析器來解析和驗證輸入XML文檔(盡管它還可以將以前構造的DOM表示作為輸入)。它包含一些轉換器以將JDOM表示輸出成SAX2事件流、DOM模型或XML文本文檔。JDOM是在Apache許可證變體下發布的開放源碼。
4)DOM4J?http://dom4j.sourceforge.net? ???????????? ??????? 雖然DOM4J代表了完全獨立的開發結果,但最初,它是JDOM的一種智能分支。它合并了許多超出基本XML文檔表示的功能,包括集成的XPath支持、XML Schema支持以及用于大文檔或流化文檔的基于事件的處理。它還提供了構建文檔表示的選項,它通過DOM4J API和標準DOM接口具有并行訪問功能。從2000下半年開始,它就一直處于開發之中。
為支持所有這些功能,DOM4J使用接口和抽象基本類方法。DOM4J大量使用了API中的Collections類,但是在許多情況下,它還提供一些替代方法以允許更好的性能或更直接的編碼方法。直接好處是,雖然DOM4J付出了更復雜的API的代價,但是它提供了比JDOM大得多的靈活性。
在添加靈活性、XPath集成和對大文檔處理的目標時,DOM4J的目標與JDOM是一樣的:針對Java開發者的易用性和直觀操作。它還致力于成為比JDOM更完整的解決方案,實現在本質上處理所有Java/XML問題的目標。在完成該目標時,它比JDOM更少強調防止不正確的應用程序行為。
DOM4J是一個非常非常優秀的Java XML API,具有性能優異、功能強大和極端易用使用的特點,同時它也是一個開放源代碼的軟件。如今你可以看到越來越多的Java軟件都在使用DOM4J來讀寫XML,特別值得一提的是連Sun的JAXM也在用DOM4J。
2.. 比較
1)DOM4J性能最好,連Sun的JAXM也在用DOM4J。目前許多開源項目中大量采用DOM4J,例如大名鼎鼎的Hibernate也用DOM4J來讀取XML配置文件。如果不考慮可移植性,那就采用DOM4J.
2)JDOM和DOM在性能測試時表現不佳,在測試10M文檔時內存溢出。在小文檔情況下還值得考慮使用DOM和JDOM。雖然JDOM的開發者已經說明他們期望在正式發行版前專注性能問題,但是從性能觀點來看,它確實沒有值得推薦之處。另外,DOM仍是一個非常好的選擇。DOM實現廣泛應用于多種編程語言。它還是許多其它與XML相關的標準的基礎,因為它正式獲得W3C推薦(與基于非標準的Java模型相對),所以在某些類型的項目中可能也需要它(如在JavaScript中使用DOM)。
3)SAX表現較好,這要依賴于它特定的解析方式-事件驅動。一個SAX檢測即將到來的XML流,但并沒有載入到內存(當然當XML流被讀入時,會有部分文檔暫時隱藏在內存中)。
3. 四種xml操作方式的基本使用方法
xml文件:
<?xml version="1.0" encoding="GB2312"?> <RESULT> ??? <VALUE> ????? <NO>A1234</NO> ???? <ADDR>四川省XX縣XX鎮XX路X段XX號</ADDR> ??? </VALUE> ??? <VALUE> ????? <NO>B1234</NO> ???? <ADDR>四川省XX市XX鄉XX村XX組</ADDR> ??? </VALUE> </RESULT>
1)DOM
import java.io.*; import java.util.*; import org.w3c.dom.*; import javax.xml.parsers.*;
public class MyXMLReader{ public static void main(String arge[]){
long lasting =System.currentTimeMillis(); try{? File f=new File("data_10k.xml"); DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance(); DocumentBuilder builder=factory.newDocumentBuilder(); Document doc = builder.parse(f); NodeList nl = doc.getElementsByTagName("VALUE"); for (int i=0;i<nl.getLength();i++){ System.out.print("車牌號碼:" + doc.getElementsByTagName("NO").item(i).getFirstChild().getNodeValue()); System.out.println("車主地址:" + doc.getElementsByTagName("ADDR").item(i).getFirstChild().getNodeValue()); } }catch(Exception e){ e.printStackTrace(); }
2)SAX
import org.xml.sax.*; import org.xml.sax.helpers.*; import javax.xml.parsers.*;
public class MyXMLReader extends DefaultHandler {
java.util.Stack tags = new java.util.Stack(); public MyXMLReader() { super(); ?? }
public static void main(String args[]) { long lasting = System.currentTimeMillis(); try { SAXParserFactory sf = SAXParserFactory.newInstance(); SAXParser sp = sf.newSAXParser(); MyXMLReader reader = new MyXMLReader(); sp.parse(new InputSource("data_10k.xml"), reader); } catch (Exception e) { e.printStackTrace(); }
System.out.println("運行時間:" + (System.currentTimeMillis() - lasting) + "毫秒");} public void characters(char ch[], int start, int length) throws SAXException { String tag = (String) tags.peek(); if (tag.equals("NO")) {? System.out.print("車牌號碼:" + new String(ch, start, length)); ??? } ??? if (tag.equals("ADDR")) { System.out.println("地址:" + new String(ch, start, length)); ??? } ?? }
public void startElement(String uri,String localName,String qName,Attributes attrs) { tags.push(qName);} }?
3) JDOM
import java.io.*; import java.util.*; import org.jdom.*; import org.jdom.input.*;
public class MyXMLReader {
public static void main(String arge[]) { long lasting = System.currentTimeMillis(); try { SAXBuilder builder = new SAXBuilder();? Document doc = builder.build(new File("data_10k.xml"));? Element foo = doc.getRootElement();? List allChildren = foo.getChildren();? for(int i=0;i<allChildren.size();i++) {? System.out.print("車牌號碼:" + ((Element)allChildren.get(i)).getChild("NO").getText()); System.out.println("車主地址:" + ((Element)allChildren.get(i)).getChild("ADDR").getText()); } } catch (Exception e) { e.printStackTrace(); }
}
4)DOM4J
import java.io.*; import java.util.*; import org.dom4j.*; import org.dom4j.io.*;
public class MyXMLReader {
public static void main(String arge[]) { long lasting = System.currentTimeMillis(); try { File f = new File("data_10k.xml"); SAXReader reader = new SAXReader(); Document doc = reader.read(f); Element root = doc.getRootElement(); Element foo; for (Iterator i = root.elementIterator("VALUE"); i.hasNext();) { foo = (Element) i.next(); System.out.print("車牌號碼:" + foo.elementText("NO")); System.out.println("車主地址:" + foo.elementText("ADDR")); } } catch (Exception e) { e.printStackTrace(); ??? } }
摘要: 第
1
章
對象入門
Smalltalk
的五大基本特征
... 閱讀全文
??????1.加載基類,初始化基類中的靜態成員變量。
????? 2.加載派生類,初始化派生類中的靜態成員變量。 ????? 3.運行派生類的main方法。 ????? 4.初始化基類中的其他成員變量(static類型的除外)。 ????? 5.調用基類構造函數。 ????? 6.初始化派生類中的其他成員變量(static類型的除外)。 ????? 7.調用派生類構造函數。 for example: Beetle.java class Insect { ??? private int i = 9; ??? private int l = print("Insect.l initialized"); ??? protected int j ; ??? public Insect(){ ??????? System.out.println("i="+i+" j="+j); ??????? j = 39; ??? } ??? private static int x1 = print("static Insect.x1 initialized"); ??? static int print(String s){ ??????? System.out.println(s); ??????? return 47; ??? } } public class Beetle extends Insect{ ??? private int k = print("Beetle.k initialized"); ??? //private int j=100;
??? public Beetle(){ ??????? System.out.println("k = "+k); ??????? System.out.println("j = "+j); ??? } ??? private static int y = print("Beetle.y initialized"); ??? public static void main(String[] args) { ??????? System.out.println("Beetle constructor"); ??????? Beetle b = new Beetle(); ??????? //Insect i = new Insect(); ??? } }
運行輸出: static Insect.x1 initialized Beetle.y initialized Beetle constructor Insect.l initialized i=9 j=0 Beetle.k initialized k = 47 j = 39
摘要: ===========
?
Chap1
對象簡介
... 閱讀全文
------------------------------------------------------------------------------------------------- 1. 簡單類型是按值傳遞的 Java 方法的參數是簡單類型的時候,是按值傳遞的 (pass by value)。這一點我們可以通過一個簡單的例子來說明: /* 例 1 */ /** * @(#) Test.java * @author fancy */ public class Test {
public static void test(boolean test) {
test = ! test;
System.out.println("In test(boolean) : test = " + test);
}
public static void main(String[] args) {
boolean test = true;
System.out.println("Before test(boolean) : test = " + test);
test(test);
System.out.println("After test(boolean) : test = " + test);
}
}
運行結果: Before test(boolean) : test = true In test(boolean) : test = false After test(boolean) : test = true 不難看出,雖然在 test(boolean) 方法中改變了傳進來的參數的值,但對這個參數源變量本身并沒有影響,即對 main(String[]) 方法里的 test 變量沒有影響。那說明,參數類型是簡單類型的時候,是按值傳遞的。以參數形式傳遞簡單類型的變量時,實際上是將參數的值作了一個拷貝傳進方法函數的,那么在方法函數里再怎么改變其值,其結果都是只改變了拷貝的值,而不是源值。 2. 什么是引用 Java 是傳值還是傳引用,問題主要出在對象的傳遞上,因為 Java 中簡單類型沒有引用。既然爭論中提到了引用這個東西,為了搞清楚這個問題,我們必須要知道引用是什么。 簡單的說,引用其實就像是一個對象的名字或者別名 (alias),一個對象在內存中會請求一塊空間來保存數據,根據對象的大小,它可能需要占用的空間大小也不等。訪問對象的時候,我們不會直接是訪問對象在內存中的數據,而是通過引用去訪問。引用也是一種數據類型,我們可以把它想象為類似 C 語言中指針的東西,它指示了對象在內存中的地址——只不過我們不能夠觀察到這個地址究竟是什么。 如果我們定義了不止一個引用指向同一個對象,那么這些引用是不相同的,因為引用也是一種數據類型,需要一定的內存空間來保存。但是它們的值是相同的,都指示同一個對象在內存的中位置。比如 String a = "Hello"; String b = a; 這里,a 和 b 是不同的兩個引用,我們使用了兩個定義語句來定義它們。但它們的值是一樣的,都指向同一個對象 "Hello"。也許你還覺得不夠直觀,因為 String 對象的值本身是不可更改的 (像 b = "World"; b = a; 這種情況不是改變了 "World" 這一對象的值,而是改變了它的引用 b 的值使之指向了另一個 String 對象 a)。那么我們用 StringBuffer 來舉一個例子: /* 例 2 */ /** * @(#) Test.java
* @author fancy */
public class Test {
public static void main(String[] args) {
StringBuffer a = new StringBuffer("Hello");
StringBuffer b = a;
b.append(", World");
System.out.println("a is " + a);
}
}
運行結果: a is Hello, World 這個例子中 a 和 b 都是引用,當改變了 b 指示的對象的值的時候,從輸出結果來看,a 所指示的對象的值也改變了。所以,a 和 b 都指向同一個對象即包含 "Hello" 的一個 StringBuffer 對象。 這里我描述了兩個要點:
- 引用是一種數據類型,保存了對象在內存中的地址,這種類型即不是我們平時所說的簡單數據類型也不是類實例(對象);
- 不同的引用可能指向同一個對象,換句話說,一個對象可以有多個引用,即該類類型的變量。
3. 對象是如何傳遞的呢 關于對象的傳遞,有兩種說法,即“它是按值傳遞的”和“它是按引用傳遞的”。這兩種說法各有各的道理,但是它們都沒有從本質上去分析,即致于產生了爭論。 既然現在我們已經知道了引用是什么東西,那么現在不妨來分析一下對象作是參數是如何傳遞的。還是先以一個程序為例: /* 例 3 */ /** * @(#) Test.java * @author fancy */ public class Test {
public static void test(StringBuffer str) {
str.append(", World!");
}
public static void main(String[] args) {
StringBuffer string = new StringBuffer("Hello");
test(string);
System.out.println(string);
}
}
運行結果: Hello, World!
test(string) 調用了 test(StringBuffer) 方法,并將 string 作為參數傳遞了進去。這里 string 是一個引用,這一點是勿庸置疑的。前面提到,引用是一種數據類型,而且不是對象,所以它不可能按引用傳遞,所以它是按值傳遞的,它么它的值究竟是什么呢?是對象的地址。 由此可見,對象作為參數的時候是按值傳遞的,對嗎?錯!為什么錯,讓我們看另一個例子: /* 例 4 */ /** * @(#) Test.java * @author fancy */ public class Test {
public static void test(String str) {
str = "World";
}
public static void main(String[] args) {
String string = "Hello";
test(string);
System.out.println(string);
}
}
運行結果: Hello 為什么會這樣呢?因為參數 str 是一個引用,而且它與 string 是不同的引用,雖然它們都是同一個對象的引用。str = "World" 則改變了 str 的值,使之指向了另一個對象,然而 str 指向的對象改變了,但它并沒有對 "Hello" 造成任何影響,而且由于 string 和 str 是不同的引用,str 的改變也沒有對 string 造成任何影響,結果就如例中所示。 其結果是推翻了參數按值傳遞的說法。那么,對象作為參數的時候是按引用傳遞的了?也錯!因為上一個例子的確能夠說明它是按值傳遞的。 結果,就像光到底是波還是粒子的問題一樣,Java 方法的參數是按什么傳遞的問題,其答案就只能是:即是按值傳遞也是按引用傳遞,只是參照物不同,結果也就不同。 4. 正確看待傳值還是傳引用的問題 要正確的看待這個問題必須要搞清楚為什么會有這樣一個問題。 實際上,問題來源于 C,而不是 Java。 C 語言中有一種數據類型叫做指針,于是將一個數據作為參數傳遞給某個函數的時候,就有兩種方式:傳值,或是傳指針,它們的區別,可以用一個簡單的例子說明: /* 例 5 */ /** * @(#) test.c * @author fancy */ void SwapValue(int a, int b) {
int t = a;
a = b;
b = t;
}
void SwapPointer(int * a, int * b) {
int t = * a;
* a = * b;
* b = t;
}
void main() {
int a = 0, b = 1;
printf("1 : a = %d, b = %d\n", a, b);
SwapValue(a, b);
printf("2 : a = %d, b = %d\n", a, b);
SwapPointer(&a, &b);
printf("3 : a = %d, b = %d\n", a, b);
}
運行結果: 1 : a = 0, b = 1 2 : a = 0, b = 1 3 : a = 1, b = 0 大家可以明顯的看到,按指針傳遞參數可以方便的修改通過參數傳遞進來的值,而按值傳遞就不行。 當 Java 成長起來的時候,許多的 C 程序員開始轉向學習 Java,他們發現,使用類似 SwapValue 的方法仍然不能改變通過參數傳遞進來的簡單數據類型的值,但是如果是一個對象,則可能將其成員隨意更改。于是他們覺得這很像是 C 語言中傳值/傳指針的問題。但是 Java 中沒有指針,那么這個問題就演變成了傳值/傳引用的問題??上⑦@個問題放在 Java 中進行討論并不恰當。 討論這樣一個問題的最終目的只是為了搞清楚何種情況才能在方法函數中方便的更改參數的值并使之長期有效。 Java 中,改變參數的值有兩種情況,第一種,使用賦值號“=”直接進行賦值使其改變,如例 1 和例 4;第二種,對于某些對象的引用,通過一定途徑對其成員數據進行改變,如例 3。對于第一種情況,其改變不會影響到方法該方法以外的數據,或者直接說源數據。而第二種方法,則相反,會影響到源數據——因為引用指示的對象沒有變,對其成員數據進行改變則實質上是改變的該對象。 5. 如何實現類似 swap 的方法 傳值還是傳引用的問題,到此已經算是解決了,但是我們仍然不能解決這樣一個問題:如果我有兩個 int 型的變量 a 和 b,我想寫一個方法來交換它們的值,應該怎么辦? 結論很讓人失望——沒有辦法!因此,我們只能具體情況具體討論,以經常使用交換方法的排序為例: /** 例 6 */ /** * @(#) Test.java * @author fancy */ public class Test {
public static void swap(int[] data, int a, int b) {
int t = data[a];
data[a] = data[b];
data[b] = t;
}
public static void main(String[] args) {
int[] data = newint[10];
for (int i = 0; i < 10; i++) {
data[i] = (int) (Math.random() * 100);
System.out.print(" " + data[i]);
}
System.out.println();
for (int i = 0; i < 9; i++) {
for (int j = i; j < 10; j++) {
if (data[i] > data[j]) {
swap(data, i, j);
}
}
}
for (int i = 0; i < 10; i++) {
System.out.print(" " + data[i]);
}
System.out.println();
}
}
運行結果(情況之一): 78 69 94 38 95 31 50 97 84 1 1 31 38 50 69 78 84 94 95 97 swap(int[] data, int a, int b) 方法在內部實際上是改變了 data 所指示的對象的成員數據,即上述討論的第二種改變參數值的方法。希望大家能夠舉一反三,使用類似的方法來解決相關問題。 ---------------------------------------------------------------------------------------
上面的文字是網上找的,我覺得對象傳遞還是屬于引用傳遞,對于上面的兩段代碼,都可以按引用傳遞的方式說明:
/* 例 3 */ /** * @(#) Test.java * @author fancy */ public class Test {
public static void test(StringBuffer str) {
str.append(", World!");
}
public static void main(String[] args) {
StringBuffer string = new StringBuffer("Hello");
test(string);
System.out.println(string);
}
}
運行結果: Hello, World! 1.string 引用傳遞給test 方法---->2.str 和string 引用指向同一個對象"Hello" ----->3.str引用改變了它和string引用指向的同一對象"Hello"----->4.所以System.out.println(string);后的對象也變了
對于:
/* 例 4 */ /** * @(#) Test.java * @author fancy */ public class Test {
public static void test(String str) {
str = "World";
}
public static void main(String[] args) {
String string = "Hello";
test(string);
System.out.println(string);
}
}
運行結果: Hello
1.string 引用傳遞給test 方法---->2.str 和string 引用指向同一個對象"Hello" ----->3.str引象指向了別的對象"World",而沒有影響string引用對象的對象"Hello" ---->4.所以System.out.println(string);后的對象還是Hello
對于:
public class Test { String name; public void setName(String s) { name = s; } public String getName(){ return name; } public void call(Test t) { Test t2 = new Test(); t2.setName("cba"); t.setName("abc"); t = t2 ; } public static void main(String[] arg) { Test obj = new Test(); obj.call (obj) ; System.out.println("obj's name: "+obj.getName()); } }
也可以類似的理解,結果為:obj's name: abc
在JDBC應用中,如果你已經是稍有水平開發者,你就應該始終以PreparedStatement代替Statement.也就是說,在任何時候都不要使用Statement. 基于以下的原因: 一.代碼的可讀性和可維護性. 雖然用PreparedStatement來代替Statement會使代碼多出幾行,但這樣的代碼無論從可讀性還是可維護性上來說.都比直接用Statement的代碼高很多檔次:
stmt.executeUpdate("insert into tb_name (col1,col2,col2,col4) values ('"+var1+"','"+var2+"',"+var3+",'"+var4+"')");
perstmt = con.prepareStatement("insert into tb_name (col1,col2,col2,col4) values (?,?,?,?)"); perstmt.setString(1,var1); perstmt.setString(2,var2); perstmt.setString(3,var3); perstmt.setString(4,var4); perstmt.executeUpdate();
不用我多說,對于第一種方法.別說其他人去讀你的代碼,就是你自己過一段時間再去讀,都會覺得傷心.
二.PreparedStatement盡最大可能提高性能. 每一種數據庫都會盡最大努力對預編譯語句提供最大的性能優化.因為預編譯語句有可能被重復調用.所以語句在被DB的編譯器編譯后的執行代碼被緩存下來,那么下次調用時只要是相同的預編譯語句就不需要編譯,只要將參數直接傳入編譯過的語句執行代碼中(相當于一個涵數)就會得到執行.這并不是說只有一個Connection中多次執行的預編譯語句被緩存,而是對于整個DB中,只要預編譯的語句語法和緩存中匹配.那么在任何時候就可以不需要再次編譯而可以直接執行.而statement的語句中,即使是相同一操作,而由于每次操作的數據不同所以使整個語句相匹配的機會極小,幾乎不太可能匹配.比如: insert into tb_name (col1,col2) values ('11','22'); insert into tb_name (col1,col2) values ('11','23'); 即使是相同操作但因為數據內容不一樣,所以整個個語句本身不能匹配,沒有緩存語句的意義.事實是沒有數據庫會對普通語句編譯后的執行代碼緩存.
當然并不是所以預編譯語句都一定會被緩存,數據庫本身會用一種策略,比如使用頻度等因素來決定什么時候不再緩存已有的預編譯結果.以保存有更多的空間存儲新的預編譯語句.
三.最重要的一點是極大地提高了安全性.
即使到目前為止,仍有一些人連基本的惡義SQL語法都不知道. String sql = "select * from tb_name where name= '"+varname+"' and passwd='"+varpasswd+"'"; 如果我們把[' or '1' = '1]作為varpasswd傳入進來.用戶名隨意,看看會成為什么?
select * from tb_name = '隨意' and passwd = '' or '1' = '1'; 因為'1'='1'肯定成立,所以可以任何通過驗證.更有甚者: 把[';drop table tb_name;]作為varpasswd傳入進來,則: select * from tb_name = '隨意' and passwd = '';drop table tb_name;有些數據庫是不會讓你成功的,但也有很多數據庫就可以使這些語句得到執行.
而如果你使用預編譯語句.你傳入的任何內容就不會和原來的語句發生任何匹配的關系.只要全使用預編譯語句,你就用不著對傳入的數據做任何過慮.而如果使用普通的statement,有可能要對drop,;等做費盡心機的判斷和過慮.
上面的幾個原因,還不足讓你在任何時候都使用PreparedStatement嗎?
|
|
| 日 | 一 | 二 | 三 | 四 | 五 | 六 |
---|
29 | 30 | 31 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|
常用鏈接
留言簿(2)
隨筆分類(6)
隨筆檔案(8)
相冊
搜索
最新評論

閱讀排行榜
評論排行榜
|
|