final 用于聲明屬性,方法和類,分別表示屬性不可變,方法不可覆蓋,類不可繼承。finally是異常處理語句結(jié)構(gòu)的一部分,表示總是執(zhí)行。finalize是 Object類的一個方法,在垃圾收集器執(zhí)行的時候會調(diào)用被回收對象的此方法,可以覆蓋此方法提供垃圾收集時的其他資源回收,例如關(guān)閉文件等。
第二,Anonymous Inner Class (匿名內(nèi)部類) 是否可以extends(繼承)其它類,是否可以implements(實現(xiàn))interface(接口)?
可以繼承其他類或完成其他接口,在swing編程中常用此方式。
第三,Static Nested Class 和 Inner Class的不同,說得越多越好(面試題有的很籠統(tǒng))。
Static Nested Class是被聲明為靜態(tài)(static)的內(nèi)部類,它可以不依賴于外部類實例被實例化。而通常的內(nèi)部類需要在外部類實例化后才能實例化。
第四,&和&&的區(qū)別。
&是位運(yùn)算符,表示按位與運(yùn)算,&&是邏輯運(yùn)算符,表示邏輯與(and).
第五,HashMap和Hashtable的區(qū)別。
HashMap是Hashtable的輕量級實現(xiàn)(非線程安全的實現(xiàn)),他們都完成了Map接口,主要區(qū)別在于HashMap允許空(null)鍵值(key),由于非線程安全,效率上可能高于Hashtable.
第六,Collection 和 Collections的區(qū)別。
Collection是集合類的上級接口,繼承與他的接口主要有Set 和List.Collections是針對集合類的一個幫助類,他提供一系列靜態(tài)方法實現(xiàn)對各種集合的搜索、排序、線程安全化等操作。
第七,什么時候用assert。
1.4新增關(guān)鍵字(語法),用于測試boolean表達(dá)式狀態(tài),可用于調(diào)試程序。使用方法 assert ,表示如果表達(dá)式為真(true),則下面的語句執(zhí)行,否則拋出AssertionError。另外的使用方式assert < boolean表達(dá)式>:,表示如果表達(dá)式為真,后面的表達(dá)式忽略,否則后面表達(dá)式的值用于AssertionError的構(gòu)建參數(shù)。注意編譯時要增加-source 1.4 參數(shù),否則報錯。]運(yùn)行時要增加 –ea參數(shù),否則assert行被忽略
第八,GC是什么? 為什么要有GC?
GC是垃圾收集的意思(Gabage Collection),內(nèi)存處理是編程人員容易出現(xiàn)問題的地方,忘記或者錯誤的內(nèi)存回收會導(dǎo)致程序或系統(tǒng)的不穩(wěn)定甚至崩潰,Java提供的GC功能可以自動監(jiān)測對象是否超過作用域從而達(dá)到自動回收內(nèi)存的目的,Java語言沒有提供釋放已分配內(nèi)存的顯示操作方法。
第九,String s = new String("xyz");創(chuàng)建了幾個String Object?
兩個
第十,Math.round(11.5)等於多少? Math.round(-11.5)等於多少?
Math.round(11.5)==12Math.round(-11.5)==-11round方法返回與參數(shù)最接近的長整數(shù),參數(shù)加1/2后求其floor.
第十一,short s1 = 1; s1 = s1 + 1;有什么錯? short s1 = 1; s1 += 1;有什么錯?
short s1 = 1; s1 = s1 + 1; (s1+1運(yùn)算結(jié)果是int型,需要強(qiáng)制轉(zhuǎn)換類型)short s1 = 1; s1 += 1;(可以正確編譯)
第十二,sleep() 和 wait() 有什么區(qū)別?
sleep是線程類(Thread)的方法,導(dǎo)致此線程暫停執(zhí)行指定時間,給執(zhí)行機(jī)會給其他線程,但是監(jiān)控狀態(tài)依然保持,到時后會自動恢復(fù)。調(diào)用 sleep不會釋放對象鎖。wait是Object類的方法,對此對象調(diào)用wait方法導(dǎo)致本線程放棄對象鎖,進(jìn)入等待此對象的等待鎖定池,只有針對此對象發(fā)出notify方法(或notifyAll)后本線程才進(jìn)入對象鎖定池準(zhǔn)備獲得對象鎖進(jìn)入運(yùn)行狀態(tài)。
第十三,Java有沒有g(shù)oto?
沒有 很十三的問題,如果哪個面試的問到這個問題,我勸你還是別進(jìn)這家公司。
第十四,數(shù)組有沒有l(wèi)ength()這個方法? String有沒有l(wèi)ength()這個方法?
數(shù)組沒有l(wèi)ength()這個方法,有l(wèi)ength的屬性。
String有有l(wèi)ength()這個方法。
第十五,Overload和Override的區(qū)別。Overloaded的方法是否可以改變返回值的類型?
方法的重寫Overriding和重載Overloading是Java多態(tài)性的不同表現(xiàn)。重寫Overriding是父類與子類之間多態(tài)性的一種表現(xiàn), 重載Overloading是一個類中多態(tài)性的一種表現(xiàn)。如果在子類中定義某方法與其父類有相同的名稱和參數(shù),我們說該方法被重寫 (Overriding)。子類的對象使用這個方法時,將調(diào)用子類中的定義,對它而言,父類中的定義如同被“屏蔽”了。如果在一個類中定義了多個同名的方法,它們或有不同的參數(shù)個數(shù)或有不同的參數(shù)類型,則稱為方法的重載(Overloading)。Overloaded的方法是可以改變返回值的類型。
第十六,Set里的元素是不能重復(fù)的,那么用什么方法來區(qū)分重復(fù)與否呢? 是用==還是equals()? 它們有何區(qū)別?
Set里的元素是不能重復(fù)的,那么用iterator()方法來區(qū)分重復(fù)與否。equals()是判讀兩個Set是否相等。
equals()和==方法決定引用值是否指向同一對象equals()在類中被覆蓋,為的是當(dāng)兩個分離的對象的內(nèi)容和類型相配的話,返回真值。
第十七,給我一個你最常見到的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有什么區(qū)別?
error 表示恢復(fù)不是不可能但很困難的情況下的一種嚴(yán)重問題。比如說內(nèi)存溢出。不可能指望程序能處理這樣的情況。
exception 表示一種設(shè)計或?qū)崿F(xiàn)問題。也就是說,它表示如果程序運(yùn)行正常,從不會發(fā)生的情況。
第十九,List, Set, Map是否繼承自Collection接口?
List,Set是
Map不是
第二十,abstract class和interface有什么區(qū)別?
聲明方法的存在而不去實現(xiàn)它的類被叫做抽象類(abstract class),它用于要創(chuàng)建一個體現(xiàn)某些基本行為的類,并為該類聲明方法,但不能在該類中實現(xiàn)該類的情況。不能創(chuàng)建abstract 類的實例。然而可以創(chuàng)建一個變量,其類型是一個抽象類,并讓它指向具體子類的一個實例。不能有抽象構(gòu)造函數(shù)或抽象靜態(tài)方法。Abstract 類的子類為它們父類中的所有抽象方法提供實現(xiàn),否則它們也是抽象類為。取而代之,在子類中實現(xiàn)該方法。知道其行為的其它類可以在類中實現(xiàn)這些方法。
接口(interface)是抽象類的變體。在接口中,所有方法都是抽象的。多繼承性可通過實現(xiàn)這樣的接口而獲得。接口中的所有方法都是抽象的,沒有一個 有程序體。接口只可以定義static final成員變量。接口的實現(xiàn)與子類相似,除了該實現(xiàn)類不能從接口定義中繼承行為。當(dāng)類實現(xiàn)特殊接口時,它定義(即將程序體給予)所有這種接口的方法。然后,它可以在實現(xiàn)了該接口的類的任何對象上調(diào)用接口的方法。由于有抽象類,它允許使用接口名作為引用變量的類型。通常的動態(tài)聯(lián)編將生效。引用可以轉(zhuǎn)換到接口類型或從接口類型轉(zhuǎn)換,instanceof 運(yùn)算符可以用來決定某對象的類是否實現(xiàn)了接口。
第二十一,abstract的method是否可同時是static,是否可同時是native,是否可同時是synchronized?
都不能
第二十二,接口是否可繼承接口? 抽象類是否可實現(xiàn)(implements)接口? 抽象類是否可繼承實體類(concrete class)?
接口可以繼承接口。抽象類可以實現(xiàn)(implements)接口,抽象類是否可繼承實體類,但前提是實體類必須有明確的構(gòu)造函數(shù)。
第二十三,啟動一個線程是用run()還是start()?
啟動一個線程是調(diào)用start()方法,使線程所代表的虛擬處理機(jī)處于可運(yùn)行狀態(tài),這意味著它可以由JVM調(diào)度并執(zhí)行。這并不意味著線程就會立即運(yùn)行。run()方法可以產(chǎn)生必須退出的標(biāo)志來停止一個線程。
第二十四,構(gòu)造器Constructor是否可被override?
構(gòu)造器Constructor不能被繼承,因此不能重寫Overriding,但可以被重載Overloading。
第二十五,是否可以繼承String類?
String類是final類故不可以繼承。
第二十六,當(dāng)一個線程進(jìn)入一個對象的一個synchronized方法后,其它線程是否可進(jìn)入此對象的其它方法?
不能,一個對象的一個synchronized方法只能由一個線程訪問。
第二十七,try {}里有一個return語句,那么緊跟在這個try后的finally {}里的code會不會被執(zhí)行,什么時候被執(zhí)行,在return前還是后?
會執(zhí)行,在return前執(zhí)行。
第二十八,編程題: 用最有效率的方法算出2乘以8等於幾?
有C背景的程序員特別喜歡問這種問題。
2 < < 3
第二十九,兩個對象值相同(x.equals(y) == true),但卻可有不同的hash code,這句話對不對?
不對,有相同的hash code。
第三十,當(dāng)一個對象被當(dāng)作參數(shù)傳遞到一個方法后,此方法可改變這個對象的屬性,并可返回變化后的結(jié)果,那么這里到底是值傳遞還是引用傳遞?
是值傳遞。Java 編程語言只由值傳遞參數(shù)。當(dāng)一個對象實例作為一個參數(shù)被傳遞到方法中時,參數(shù)的值就是對該對象的引用。對象的內(nèi)容可以在被調(diào)用的方法中改變,但對象的引用是永遠(yuǎn)不會改變的。
第三十一,swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上?
switch(expr1)中,expr1是一個整數(shù)表達(dá)式。因此傳遞給 switch 和 case 語句的參數(shù)應(yīng)該是 int、 short、 char 或者 byte。long,string 都不能作用于swtich。
第三十二,編程題: 寫一個Singleton出來。
Singleton模式主要作用是保證在Java應(yīng)用程序中,一個類Class只有一個實例存在。
一般Singleton模式通常有幾種種形式:
第一種形式: 定義一個類,它的構(gòu)造函數(shù)為private的,它有一個static的private的該類變量,在類初始化時實例話,通過一個public的getInstance方法獲取對它的引用,繼而調(diào)用其中的方法。
public class Singleton {
private Singleton(){}
//在自己內(nèi)部定義自己一個實例,是不是很奇怪?
//注意這是private 只供內(nèi)部調(diào)用
private static Singleton instance = new Singleton();
//這里提供了一個供外部訪問本class的靜態(tài)方法,可以直接訪問
public static Singleton getInstance() {
return instance;
}
}
第二種形式:
public class Singleton {
private static Singleton instance = null;
public static synchronized Singleton getInstance() {
//這個方法比上面有所改進(jìn),不用每次都進(jìn)行生成對象,只是第一次
//使用時生成實例,提高了效率!
if (instance==null)
instance=new Singleton();
return instance; }
}
其他形式:
定義一個類,它的構(gòu)造函數(shù)為private的,所有方法為static的。
一般認(rèn)為第一種形式要更加安全些
其中也存在一些問題,下面補(bǔ)充一下.希望大家繼續(xù)補(bǔ)充
1.最好介紹一下final修飾變量的情形(分開說明基本類型變量和引用類型變量)。
2.不知道如何描述比較好,匿名內(nèi)部類本身是一個實現(xiàn)了特定接口或繼承了其他類的類。
3.非靜態(tài)類不能有static的方法。參考一下java collections framework的代碼,還能說出很多東西來。
4.&既是位運(yùn)算符,也是邏輯操作符。
&和&&都是邏輯運(yùn)算符,它們的區(qū)別是,&&進(jìn)行短路計算。
6.可以補(bǔ)充一下,參考文檔。
11.說一下具體的原因可能更好。
13.java有g(shù)oto這個keyword,不過還沒有使用。
15.overload和多態(tài)沒有任何關(guān)系。多態(tài)是面向?qū)ο蟮幕咎卣鳎呛屠^承、overriding相關(guān)的。原來以為把overload當(dāng)作多態(tài)的一種是從C++來的,后來翻了基本c++的經(jīng)典的書,也強(qiáng)調(diào)多態(tài)只是和繼承,虛函數(shù),overriding相關(guān),所以不知道這個錯誤思想從哪來的了.
16.public interface Set <E>extends Collection <E>一個不包含重復(fù)元素的 collection。更確切地講,set 不包含滿足 e1.equals(e2) 的元素對 e1 和 e2,并且最多包含一個 null 元素。正如其名稱所暗示的,此接口模仿了數(shù)學(xué)上的 set 抽象。
看文檔就是了.
22."抽象類是否可繼承實體類,但前提是實體類必須有明確的構(gòu)造函數(shù)。"什么意思?誰規(guī)定的?
27.這個比較復(fù)雜,例如
public class Main {
public static void main(String[] args) {
System.out.println(test());
}
public static boolean test() {
boolean b = false;
try {
return b = 4 > 3;
} finally {
System.out.println(b);
//return false;
}
}
}
這個例子中可以看出,return后面的表達(dá)式已經(jīng)計算了,感覺return語句這里被掛起了,如果finally中沒有return,這個return就返回.
如果finally也有return,那么就是finally中的return返回了.
29.碰上不守規(guī)矩的程序員誰也沒辦法.
31.還有枚舉呢
32.注釋有問題.
1. 概述
本文主要包括以下幾個方面:編碼基本知識,java,系統(tǒng)軟件,url,工具軟件等。
在下面的描述中,將以"中文"兩個字為例,經(jīng)查表可以知道其GB2312編碼是"d6d0 cec4",Unicode編碼為"4e2d 6587",UTF編碼就是"e4b8ad e69687"。注意,這兩個字沒有iso8859-1編碼,但可以用iso8859-1編碼來"表示"。
2. 編碼基本知識
最早的編碼是iso8859-1,和ascii編碼相似。但為了方便表示各種各樣的語言,逐漸出現(xiàn)了很多標(biāo)準(zhǔn)編碼,重要的有如下幾個。
2.1. iso8859-1
屬于單字節(jié)編碼,最多能表示的字符范圍是0-255,應(yīng)用于英文系列。比如,字母'a'的編碼為0x61=97。
很明顯,iso8859-1編碼表示的字符范圍很窄,無法表示中文字符。但是,由于是單字節(jié)編碼,和計算機(jī)最基礎(chǔ)的表示單位一致,所以很多時候,仍舊使用iso8859-1編碼來表示。而且在很多協(xié)議上,默認(rèn)使用該編碼。比如,雖然"中文"兩個字不存在iso8859-1編碼,以gb2312編碼為例,應(yīng)該是"d6d0 cec4"兩個字符,使用iso8859-1編碼的時候則將它拆開為4個字節(jié)來表示:"d6 d0 ce c4"(事實上,在進(jìn)行存儲的時候,也是以字節(jié)為單位處理的)。而如果是UTF編碼,則是6個字節(jié)"e4 b8 ad e6 96 87"。很明顯,這種表示方法還需要以另一種編碼為基礎(chǔ)。
2.2. GB2312/GBK
這就是漢子的國標(biāo)碼,專門用來表示漢字,是雙字節(jié)編碼,而英文字母和iso8859-1一致(兼容iso8859-1編碼)。其中g(shù)bk編碼能夠用來同時表示繁體字和簡體字,而gb2312只能表示簡體字,gbk是兼容gb2312編碼的。
2.3. unicode
這是最統(tǒng)一的編碼,可以用來表示所有語言的字符,而且是定長雙字節(jié)(也有四字節(jié)的)編碼,包括英文字母在內(nèi)。所以可以說它是不兼容iso8859-1編碼的,也不兼容任何編碼。不過,相對于iso8859-1編碼來說,uniocode編碼只是在前面增加了一個0字節(jié),比如字母'a'為"00 61"。
需要說明的是,定長編碼便于計算機(jī)處理(注意GB2312/GBK不是定長編碼),而unicode又可以用來表示所有字符,所以在很多軟件內(nèi)部是使用unicode編碼來處理的,比如java。
2.4. UTF
考慮到unicode編碼不兼容iso8859-1編碼,而且容易占用更多的空間:因為對于英文字母,unicode也需要兩個字節(jié)來表示。所以unicode不便于傳輸和存儲。因此而產(chǎn)生了utf編碼,utf編碼兼容iso8859-1編碼,同時也可以用來表示所有語言的字符,不過,utf編碼是不定長編碼,每一個字符的長度從1-6個字節(jié)不等。另外,utf編碼自帶簡單的校驗功能。一般來講,英文字母都是用一個字節(jié)表示,而漢字使用三個字節(jié)。
注意,雖然說utf是為了使用更少的空間而使用的,但那只是相對于unicode編碼來說,如果已經(jīng)知道是漢字,則使用GB2312/GBK無疑是最節(jié)省的。不過另一方面,值得說明的是,雖然utf編碼對漢字使用3個字節(jié),但即使對于漢字網(wǎng)頁,utf編碼也會比unicode編碼節(jié)省,因為網(wǎng)頁中包含了很多的英文字符。
3. java對字符的處理
在java應(yīng)用軟件中,會有多處涉及到字符集編碼,有些地方需要進(jìn)行正確的設(shè)置,有些地方需要進(jìn)行一定程度的處理。
3.1. getBytes(charset)
這是java字符串處理的一個標(biāo)準(zhǔn)函數(shù),其作用是將字符串所表示的字符按照charset編碼,并以字節(jié)方式表示。注意字符串在java內(nèi)存中總是按unicode編碼存儲的。比如"中文",正常情況下(即沒有錯誤的時候)存儲為"4e2d 6587",如果charset為"gbk",則被編碼為"d6d0 cec4",然后返回字節(jié)"d6 d0 ce c4"。如果charset為"utf8"則最后是"e4 b8 ad e6 96 87"。如果是"iso8859-1",則由于無法編碼,最后返回 "3f 3f"(兩個問號)。
3.2. new String(charset)
這是java字符串處理的另一個標(biāo)準(zhǔn)函數(shù),和上一個函數(shù)的作用相反,將字節(jié)數(shù)組按照charset編碼進(jìn)行組合識別,最后轉(zhuǎn)換為unicode存儲。參考上述getBytes的例子,"gbk" 和"utf8"都可以得出正確的結(jié)果"4e2d 6587",但iso8859-1最后變成了"003f 003f"(兩個問號)。
因為utf8可以用來表示/編碼所有字符,所以new String( str.getBytes( "utf8" ), "utf8" ) === str,即完全可逆。
3.3. setCharacterEncoding()
該函數(shù)用來設(shè)置http請求或者相應(yīng)的編碼。
對于request,是指提交內(nèi)容的編碼,指定后可以通過getParameter()則直接獲得正確的字符串,如果不指定,則默認(rèn)使用iso8859-1編碼,需要進(jìn)一步處理。參見下述"表單輸入"。值得注意的是在執(zhí)行setCharacterEncoding()之前,不能執(zhí)行任何getParameter()。java doc上說明:This method must be called prior to reading request parameters or reading input using getReader()。而且,該指定只對POST方法有效,對GET方法無效。分析原因,應(yīng)該是在執(zhí)行第一個getParameter()的時候,java將會按照編碼分析所有的提交內(nèi)容,而后續(xù)的getParameter()不再進(jìn)行分析,所以setCharacterEncoding()無效。而對于GET方法提交表單是,提交的內(nèi)容在URL中,一開始就已經(jīng)按照編碼分析所有的提交內(nèi)容,setCharacterEncoding()自然就無效。
對于response,則是指定輸出內(nèi)容的編碼,同時,該設(shè)置會傳遞給瀏覽器,告訴瀏覽器輸出內(nèi)容所采用的編碼。
3.4. 處理過程
下面分析兩個有代表性的例子,說明java對編碼有關(guān)問題的處理方法。
3.4.1. 表單輸入
User input *(gbk:d6d0 cec4) browser *(gbk:d6d0 cec4) web server iso8859-1(00d6 00d 000ce 00c4) class,需要在class中進(jìn)行處理:getbytes("iso8859-1")為d6 d0 ce c4,new String("gbk")為d6d0 cec4,內(nèi)存中以unicode編碼則為4e2d 6587。
l 用戶輸入的編碼方式和頁面指定的編碼有關(guān),也和用戶的操作系統(tǒng)有關(guān),所以是不確定的,上例以gbk為例。
l 從browser到web server,可以在表單中指定提交內(nèi)容時使用的字符集,否則會使用頁面指定的編碼。而如果在url中直接用?的方式輸入?yún)?shù),則其編碼往往是操作系統(tǒng)本身的編碼,因為這時和頁面無關(guān)。上述仍舊以gbk編碼為例。
l Web server接收到的是字節(jié)流,默認(rèn)時(getParameter)會以iso8859-1編碼處理之,結(jié)果是不正確的,所以需要進(jìn)行處理。但如果預(yù)先設(shè)置了編碼(通過request. setCharacterEncoding ()),則能夠直接獲取到正確的結(jié)果。
l 在頁面中指定編碼是個好習(xí)慣,否則可能失去控制,無法指定正確的編碼。
3.4.2. 文件編譯
假設(shè)文件是gbk編碼保存的,而編譯有兩種編碼選擇:gbk或者iso8859-1,前者是中文windows的默認(rèn)編碼,后者是linux的默認(rèn)編碼,當(dāng)然也可以在編譯時指定編碼。
Jsp *(gbk:d6d0 cec4) java file *(gbk:d6d0 cec4) compiler read uincode(gbk: 4e2d 6587; iso8859-1: 00d6 00d 000ce 00c4) compiler write utf(gbk: e4b8ad e69687; iso8859-1: *) compiled file unicode(gbk: 4e2d 6587; iso8859-1: 00d6 00d 000ce 00c4) class。所以用gbk編碼保存,而用iso8859-1編譯的結(jié)果是不正確的。
class unicode(4e2d 6587) system.out / jsp.out gbk(d6d0 cec4) os console / browser。
l 文件可以以多種編碼方式保存,中文windows下,默認(rèn)為ansi/gbk。
l 編譯器讀取文件時,需要得到文件的編碼,如果未指定,則使用系統(tǒng)默認(rèn)編碼。一般class文件,是以系統(tǒng)默認(rèn)編碼保存的,所以編譯不會出問題,但對于jsp文件,如果在中文windows下編輯保存,而部署在英文linux下運(yùn)行/編譯,則會出現(xiàn)問題。所以需要在jsp文件中用pageEncoding指定編碼。
l Java編譯的時候會轉(zhuǎn)換成統(tǒng)一的unicode編碼處理,最后保存的時候再轉(zhuǎn)換為utf編碼。
l 當(dāng)系統(tǒng)輸出字符的時候,會按指定編碼輸出,對于中文windows下,System.out將使用gbk編碼,而對于response(瀏覽器),則使用jsp文件頭指定的contentType,或者可以直接為response指定編碼。同時,會告訴browser網(wǎng)頁的編碼。如果未指定,則會使用iso8859-1編碼。對于中文,應(yīng)該為browser指定輸出字符串的編碼。
l browser顯示網(wǎng)頁的時候,首先使用response中指定的編碼(jsp文件頭指定的contentType最終也反映在response上),如果未指定,則會使用網(wǎng)頁中meta項指定中的contentType。
3.5. 幾處設(shè)置
對于web應(yīng)用程序,和編碼有關(guān)的設(shè)置或者函數(shù)如下。
3.5.1. jsp編譯
指定文件的存儲編碼,很明顯,該設(shè)置應(yīng)該置于文件的開頭。例如:<%@page pageEncoding="GBK"%>。另外,對于一般class文件,可以在編譯的時候指定編碼。
3.5.2. jsp輸出
指定文件輸出到browser是使用的編碼,該設(shè)置也應(yīng)該置于文件的開頭。例如:<%@ page contentType="text/html; charset= GBK" %>。該設(shè)置和response.setCharacterEncoding("GBK")等效。
3.5.3. meta設(shè)置
指定網(wǎng)頁使用的編碼,該設(shè)置對靜態(tài)網(wǎng)頁尤其有作用。因為靜態(tài)網(wǎng)頁無法采用jsp的設(shè)置,而且也無法執(zhí)行response.setCharacterEncoding()。例如:<
如果同時采用了jsp輸出和meta設(shè)置兩種編碼指定方式,則jsp指定的優(yōu)先。因為jsp指定的直接體現(xiàn)在response中。
需要注意的是,apache有一個設(shè)置可以給無編碼指定的網(wǎng)頁指定編碼,該指定等同于jsp的編碼指定方式,所以會覆蓋靜態(tài)網(wǎng)頁中的meta指定。所以有人建議關(guān)閉該設(shè)置。
3.5.4. form設(shè)置
當(dāng)瀏覽器提交表單的時候,可以指定相應(yīng)的編碼。例如:<form accept-charset= "gb2312">。一般不必不使用該設(shè)置,瀏覽器會直接使用網(wǎng)頁的編碼。
4. 系統(tǒng)軟件
下面討論幾個相關(guān)的系統(tǒng)軟件。
4.1. mysql數(shù)據(jù)庫
很明顯,要支持多語言,應(yīng)該將數(shù)據(jù)庫的編碼設(shè)置成utf或者unicode,而utf更適合與存儲。但是,如果中文數(shù)據(jù)中包含的英文字母很少,其實unicode更為適合。
數(shù)據(jù)庫的編碼可以通過mysql的配置文件設(shè)置,例如default-character-set=utf8。還可以在數(shù)據(jù)庫鏈接URL中設(shè)置,例如: useUnicode=true&characterEncoding=UTF-8。注意這兩者應(yīng)該保持一致,在新的sql版本里,在數(shù)據(jù)庫鏈接URL里可以不進(jìn)行設(shè)置,但也不能是錯誤的設(shè)置。
4.2. apache
appache和編碼有關(guān)的配置在httpd.conf中,例如AddDefaultCharset UTF-8。如前所述,該功能會將所有靜態(tài)頁面的編碼設(shè)置為UTF-8,最好關(guān)閉該功能。
另外,apache還有單獨(dú)的模塊來處理網(wǎng)頁響應(yīng)頭,其中也可能對編碼進(jìn)行設(shè)置。
4.3. linux默認(rèn)編碼
這里所說的linux默認(rèn)編碼,是指運(yùn)行時的環(huán)境變量。兩個重要的環(huán)境變量是LC_ALL和LANG,默認(rèn)編碼會影響到j(luò)ava URLEncode的行為,下面有描述。
建議都設(shè)置為"zh_CN.UTF-8"。
4.4. 其它
為了支持中文文件名,linux在加載磁盤時應(yīng)該指定字符集,例如:mount /dev/hda5 /mnt/hda5/ -t ntfs -o iocharset=gb2312。
另外,如前所述,使用GET方法提交的信息不支持request.setCharacterEncoding(),但可以通過tomcat的配置文件指定字符集,在tomcat的server.xml文件中,形如:<Connector ... URIEncoding="GBK"/>。這種方法將統(tǒng)一設(shè)置所有請求,而不能針對具體頁面進(jìn)行設(shè)置,也不一定和browser使用的編碼相同,所以有時候并不是所期望的。
5. URL地址
URL地址中含有中文字符是很麻煩的,前面描述過使用GET方法提交表單的情況,使用GET方法時,參數(shù)就是包含在URL中。
5.1. URL編碼
對于URL中的一些特殊字符,瀏覽器會自動進(jìn)行編碼。這些字符除了"/?&"等外,還包括unicode字符,比如漢子。這時的編碼比較特殊。
IE有一個選項"總是使用UTF-8發(fā)送URL",當(dāng)該選項有效時,IE將會對特殊字符進(jìn)行UTF-8編碼,同時進(jìn)行URL編碼。如果改選項無效,則使用默認(rèn)編碼"GBK",并且不進(jìn)行URL編碼。但是,對于URL后面的參數(shù),則總是不進(jìn)行編碼,相當(dāng)于UTF-8選項無效。比如"中文.html?a=中文",當(dāng)UTF-8選項有效時,將發(fā)送鏈接"%e4%b8%ad%e6%96%87.html?a=\x4e\x2d\x65\x87";而UTF-8選項無效時,將發(fā)送鏈接"\x4e\x2d\x65\x87.html?a=\x4e\x2d\x65\x87"。注意后者前面的"中文"兩個字只有4個字節(jié),而前者卻有18個字節(jié),這主要時URL編碼的原因。
當(dāng)web server(tomcat)接收到該鏈接時,將會進(jìn)行URL解碼,即去掉"%",同時按照ISO8859-1編碼(上面已經(jīng)描述,可以使用URLEncoding來設(shè)置成其它編碼)識別。上述例子的結(jié)果分別是"\ue4\ub8\uad\ue6\u96\u87.html?a=\u4e\u2d\u65\u87"和"\u4e\u2d\u65\u87.html?a=\u4e\u2d\u65\u87",注意前者前面的"中文"兩個字恢復(fù)成了6個字符。這里用"\u",表示是unicode。
所以,由于客戶端設(shè)置的不同,相同的鏈接,在服務(wù)器上得到了不同結(jié)果。這個問題不少人都遇到,卻沒有很好的解決辦法。所以有的網(wǎng)站會建議用戶嘗試關(guān)閉UTF-8選項。不過,下面會描述一個更好的處理辦法。
5.2. rewrite
熟悉的人都知道,apache有一個功能強(qiáng)大的rewrite模塊,這里不描述其功能。需要說明的是該模塊會自動將URL解碼(去除%),即完成上述web server(tomcat)的部分功能。有相關(guān)文檔介紹說可以使用[NE]參數(shù)來關(guān)閉該功能,但我試驗并未成功,可能是因為版本(我使用的是apache
rewrite本身似乎完全是采用字節(jié)處理的方式,而不考慮字符串的編碼,所以不會帶來編碼問題。
5.3. URLEncode.encode()
這是Java本身提供對的URL編碼函數(shù),完成的工作和上述UTF-8選項有效時瀏覽器所做的工作相似。值得說明的是,java已經(jīng)不贊成不指定編碼來使用該方法(deprecated)。應(yīng)該在使用的時候增加編碼指定。
當(dāng)不指定編碼的時候,該方法使用系統(tǒng)默認(rèn)編碼,這會導(dǎo)致軟件運(yùn)行結(jié)果得不確定。比如對于"中文",當(dāng)系統(tǒng)默認(rèn)編碼為"gb2312"時,結(jié)果是"%4e%2d%65%87",而默認(rèn)編碼為"UTF-8",結(jié)果卻是"%e4%b8%ad%e6%96%87",后續(xù)程序?qū)㈦y以處理。另外,這兒說的系統(tǒng)默認(rèn)編碼是由運(yùn)行tomcat時的環(huán)境變量LC_ALL和LANG等決定的,曾經(jīng)出現(xiàn)過tomcat重啟后就出現(xiàn)亂碼的問題,最后才郁悶的發(fā)現(xiàn)是因為修改修改了這兩個環(huán)境變量。
建議統(tǒng)一指定為"UTF-8"編碼,可能需要修改相應(yīng)的程序。
5.4. 一個解決方案
上面說起過,因為瀏覽器設(shè)置的不同,對于同一個鏈接,web server收到的是不同內(nèi)容,而軟件系統(tǒng)有無法知道這中間的區(qū)別,所以這一協(xié)議目前還存在缺陷。
針對具體問題,不應(yīng)該僥幸認(rèn)為所有客戶的IE設(shè)置都是UTF-8有效的,也不應(yīng)該粗暴的建議用戶修改IE設(shè)置,要知道,用戶不可能去記住每一個web server的設(shè)置。所以,接下來的解決辦法就只能是讓自己的程序多一點(diǎn)智能:根據(jù)內(nèi)容來分析編碼是否UTF-8。
比較幸運(yùn)的是UTF-8編碼相當(dāng)有規(guī)律,所以可以通過分析傳輸過來的鏈接內(nèi)容,來判斷是否是正確的UTF-8字符,如果是,則以UTF-8處理之,如果不是,則使用客戶默認(rèn)編碼(比如"GBK"),下面是一個判斷是否UTF-8的例子,如果你了解相應(yīng)規(guī)律,就容易理解。
public static boolean isValidUtf8(byte[] b,int aMaxCount){
int lLen=b.length,lCharCount=0;
for(int i=0;i<lLen && lCharCount<aMaxCount;++lCharCount){
byte lByte=b[i++];//to fast operation, ++ now, ready for the following for(;;)
if(lByte>=0) continue;//>=0 is normal ascii
if(lByte<(byte)0xc0 || lByte>(byte)0xfd) return false;
int lCount=lByte>(byte)0xfc?5:lByte>(byte)0xf8?4
:lByte>(byte)0xf0?3:lByte>(byte)0xe0?2:1;
if(i+lCount>lLen) return false;
for(int j=0;j<lCount;++j,++i) if(b[i]>=(byte)0xc0) return false;
}
return true;
}
相應(yīng)地,一個使用上述方法的例子如下:
public static String getUrlParam(String aStr,String aDefaultCharset)
throws UnsupportedEncodingException{
if(aStr==null) return null;
byte[] lBytes=aStr.getBytes("ISO-8859-1");
return new String(lBytes,StringUtil.isValidUtf8(lBytes)?"utf8":aDefaultCharset);
}
不過,該方法也存在缺陷,如下兩方面:
l 沒有包括對用戶默認(rèn)編碼的識別,這可以根據(jù)請求信息的語言來判斷,但不一定正確,因為我們有時候也會輸入一些韓文,或者其他文字。
l 可能會錯誤判斷UTF-8字符,一個例子是"學(xué)習(xí)"兩個字,其GBK編碼是" \xd1\xa7\xcf\xb0",如果使用上述isValidUtf8方法判斷,將返回true。可以考慮使用更嚴(yán)格的判斷方法,不過估計效果不大。
PATH
)。
代碼如下:
//import java.util.*;
import java.util.Properties;
import java.util.Enumeration;
import java.util.Map;
import java.util.Set;
import java.util.Iterator;
public class GetSysPropertiesAndEnv
{
public static void main(String [] args)
{
//獲取所有系統(tǒng)屬性
Properties prpt = System.getProperties();
Enumeration enm = prpt.propertyNames(); //返回系統(tǒng)屬性列表中所有鍵的枚舉
String key = "";
System.out.println("當(dāng)前系統(tǒng)屬性如下:=========");
while(enm.hasMoreElements())
{
key = (String) enm.nextElement();
System.out.println(key+":"+System.getProperty(key,"undefined"));
}
//獲取所有環(huán)境變量的設(shè)置
Map map = System.getenv(); //返回系統(tǒng)環(huán)境變量的字符串映射視圖。
Set set = map.keySet(); //返回映射中包含的鍵的 set 視圖
System.out.println("當(dāng)前環(huán)境變量如下:=========");
Iterator itr = set.iterator();
while(itr.hasNext())
{
key = (String) itr.next();
System.out.println(key+":"+map.get(key));
}
}
}
其中個人認(rèn)為比較重要的系統(tǒng)屬性如下:
java.home:Java 安裝目錄 (比如D:\JavaStudio\jdk15\jre)
java.class.path:Java 類路徑
java.vm.version:Java虛擬機(jī)版本號(比如:1.5.0-b64)
java.version:Java版本號(比如:1.5.0)
os.name:操作系統(tǒng)的名稱(比如:Windows XP)
os.version:操作系統(tǒng)的版本(比如:Windows XP 的版本為5.1)
user.home:用戶的主目錄 (比如:C:\Documents and Settings\zhangsan)
user.name:當(dāng)前用戶名
user.country:當(dāng)前用戶所屬國家
user.dir:用戶的當(dāng)前工作目錄
比較重要的環(huán)境變量(Windows操作系統(tǒng)):
%COMPUTERNAME% 返回計算機(jī)的名稱。
%COMSPEC% 返回命令行解釋器可執(zhí)行程序的準(zhǔn)確路徑。
%WINDIR% 返回操作系統(tǒng)目錄的位置。
%OS% 返回操作系統(tǒng)的名稱。Windows 2000 將操作系統(tǒng)顯示為 Windows_NT。
%PATH% 指定可執(zhí)行文件的搜索路徑。
%USERDOMAIN% 返回包含用戶帳戶的域的名稱。
%USERNAME% 返回當(dāng)前登錄的用戶的名稱。
在JAVA的util包中有兩個所有集合的父接口Collection和Map,它們的父子關(guān)系:
java.util
+Collection 這個接口extends自 --java.lang.Iterable接口
+List 接口
-ArrayList 類
-LinkedList 類
-Vector 類 此類是實現(xiàn)同步的
+Queue 接口
+不常用,在此不表.
+Set 接口
+SortedSet 接口
-TreeSet 類
-HashSet
+Map 接口
-HashMap 類 (除了不同步和允許使用 null 鍵/值之外,與 Hashtable 大致相同.)
-Hashtable 類 此類是實現(xiàn)同步的,不允許使用 null 鍵值
+SortedMap 接口
-TreeMap 類
以下對眾多接口和類的簡單說明:首先不能不先說一下數(shù)組(Array)
一、Array , Arrays
Java所有“存儲及隨機(jī)訪問一連串對象”的做法,array是最有效率的一種。
1、
效率高,但容量固定且無法動態(tài)改變。
array還有一個缺點(diǎn)是,無法判斷其中實際存有多少元素,length只是告訴我們array的容量。
2、Java中有一個Arrays類,專門用來操作array。
arrays中擁有一組static函數(shù),
equals():比較兩個array是否相等。array擁有相同元素個數(shù),且所有對應(yīng)元素兩兩相等。
fill():將值填入array中。
sort():用來對array進(jìn)行排序。
binarySearch():在排好序的array中尋找元素。
System.arraycopy():array的復(fù)制。
二、Collection , Map
若撰寫程序時不知道究竟需要多少對象,需要在空間不足時自動擴(kuò)增容量,則需要使用容器類庫,array不適用。
1、Collection 和 Map 的區(qū)別
容器內(nèi)每個為之所存儲的元素個數(shù)不同。
Collection類型者,每個位置只有一個元素。
Map類型者,持有 key-value pair,像個小型數(shù)據(jù)庫。
2、Java2容器類類庫的用途是“保存對象”,它分為兩類,各自旗下的子類關(guān)系
Collection
--List:將以特定次序存儲元素。所以取出來的順序可能和放入順序不同。
--ArrayList / LinkedList / Vector
--Set : 不能含有重復(fù)的元素
--HashSet /TreeSet
Map
--HashMap
--HashTable
--TreeMap
Map----一組成對的“鍵值對”對象,即其元素是成對的對象,最典型的應(yīng)用就是數(shù)據(jù)字典,并且還有其它廣泛的應(yīng)用。另外,Map可以返回其所
有鍵組成的Set和其所有值組成的Collection,或其鍵值對組成的Set,并且還可以像數(shù)組一樣擴(kuò)展多維Map,只要讓Map中鍵值對的每個“值
”是一個Map即可。
Collection下 1.迭代器
迭代器是一種設(shè)計模式,它是一個對象,它可以遍歷并選擇序列中的對象,而開發(fā)人員不需要了解該序列的底層結(jié)構(gòu)。迭代器通常被稱為“輕量級”對象,因為創(chuàng)建它的代價小。
Java中的Iterator功能比較簡單,并且只能單向移動:
(1) 使用方法iterator()要求容器返回一個Iterator。第一次調(diào)用Iterator的next()方法時,它返回序列的第一個元素。注意:iterator()方法是java.lang.Iterable接口,被Collection繼承。
(2) 使用next()獲得序列中的下一個元素。
(3) 使用hasNext()檢查序列中是否還有元素。
(4) 使用remove()將迭代器新返回的元素刪除。
Iterator是Java迭代器最簡單的實現(xiàn),為List設(shè)計的ListIterator具有更多的功能,它可以從兩個方向遍歷List,也可以從List中插入和刪除元素。
2.List的功能方法
List(interface):
次序是List最重要的特點(diǎn);它確保維護(hù)元素特定的順序。List為Collection添加了許多方法,使得能夠向List中間插入與移除元素(只推薦
LinkedList使用)。一個List可以生成ListIterator,使用它可以從兩個方向遍歷List,也可以從List中間插入和刪除元素。
ArrayList: 由數(shù)組實現(xiàn)的List。它允許對元素進(jìn)行快速隨機(jī)訪問,但是向List中間插入與移除元素的速度很慢。ListIterator只應(yīng)該用來由后向前遍歷ArrayList,而不是用來插入和刪除元素,因為這比LinkedList開銷要大很多。
LinkedList:
由列表實現(xiàn)的List。對順序訪問進(jìn)行了優(yōu)化,向List中間插入與刪除得開銷不大,隨機(jī)訪問則相對較慢(可用ArrayList代替)。它具有方法
addFirst()、addLast()、getFirst()、getLast()、removeFirst()、removeLast(),這些方
法(沒有在任何接口或基類中定義過)使得LinkedList可以當(dāng)作堆棧、隊列和雙向隊列使用。
3.Set的功能方法
Set(interface):
存入Set的每個元素必須是唯一的,這也是與List不同的,因為Set不保存重復(fù)元素。加入Set的Object必須定義equals()方法以確保對
象的唯一性。Set與Collection有完全一樣的接口。Set接口不保證維護(hù)元素的次序。
HashSet: HashSet能快速定位一個元素,存入HashSet的對象必須定義hashCode()。
TreeSet: 保持次序的Set,底層為樹結(jié)構(gòu)。使用它可以從Set中提取有序的序列。
LinkedHashSet: 具有HashSet的查詢速度,且內(nèi)部使用鏈表維護(hù)元素的順序(插入的次序)。于是在使用迭代器遍歷Set時,結(jié)果會按元素插入的次序顯示。
HashSet采用散列函數(shù)對元素進(jìn)行排序,這是專門為快速查詢而設(shè)計的;TreeSet采用紅黑樹的數(shù)據(jù)結(jié)構(gòu)進(jìn)行排序元
素;LinkedHashSet內(nèi)部使用散列以加快查詢速度,同時使用鏈表維護(hù)元素的次序,使得看起來元素是以插入的順序保存的。需要注意的是,生成自己
的類時,Set需要維護(hù)元素的存儲順序,因此要實現(xiàn)Comparable接口并定義compareTo()方法。
3、其他特征
* List,Set,Map將持有對象一律視為Object型別。
* Collection、List、Set、Map都是接口,不能實例化。
繼承自它們的 ArrayList, Vector, HashTable, HashMap是具象class,這些才可被實例化。
* vector容器確切知道它所持有的對象隸屬什么型別。vector不進(jìn)行邊界檢查。
三、Collections
Collections是針對集合類的一個幫助類。提供了一系列靜態(tài)方法實現(xiàn)對各種集合的搜索、排序、線程完全化等操作。
相當(dāng)于對Array進(jìn)行類似操作的類——Arrays。
如,Collections.max(Collection coll); 取coll中最大的元素。
Collections.sort(List list); 對list中元素排序
四、如何選擇?
1、容器類和Array的區(qū)別、擇取
* 容器類僅能持有對象引用(指向?qū)ο蟮闹羔槪皇菍ο笮畔opy一份至數(shù)列某位置。
* 一旦將對象置入容器內(nèi),便損失了該對象的型別信息。
2、
* 在各種Lists中,最好的做法是以ArrayList作為缺省選擇。當(dāng)插入、刪除頻繁時,使用LinkedList();
Vector總是比ArrayList慢,所以要盡量避免使用。
* 在各種Sets中,HashSet通常優(yōu)于HashTree(插入、查找)。只有當(dāng)需要產(chǎn)生一個經(jīng)過排序的序列,才用TreeSet。
HashTree存在的唯一理由:能夠維護(hù)其內(nèi)元素的排序狀態(tài)。
* 在各種Maps中
HashMap用于快速查找。
* 當(dāng)元素個數(shù)固定,用Array,因為Array效率是最高的。
結(jié)論:最常用的是ArrayList,HashSet,HashMap,Array。而且,我們也會發(fā)現(xiàn)一個規(guī)律,用TreeXXX都是排序的。
注意:
1、Collection沒有g(shù)et()方法來取得某個元素。只能通過iterator()遍歷元素。
2、Set和Collection擁有一模一樣的接口。
3、List,可以通過get()方法來一次取出一個元素。使用數(shù)字來選擇一堆對象中的一個,get(0)...。(add/get)
4、一般使用ArrayList。用LinkedList構(gòu)造堆棧stack、隊列queue。
5、Map用 put(k,v) / get(k),還可以使用containsKey()/containsValue()來檢查其中是否含有某個key/value。
HashMap會利用對象的hashCode來快速找到key。
* hashing
哈希碼就是將對象的信息經(jīng)過一些轉(zhuǎn)變形成一個獨(dú)一無二的int值,這個值存儲在一個array中。
我們都知道所有存儲結(jié)構(gòu)中,array查找速度是最快的。所以,可以加速查找。
發(fā)生碰撞時,讓array指向多個values。即,數(shù)組每個位置上又生成一個梿表。
6、Map中元素,可以將key序列、value序列單獨(dú)抽取出來。
使用keySet()抽取key序列,將map中的所有keys生成一個Set。
使用values()抽取value序列,將map中的所有values生成一個Collection。
為什么一個生成Set,一個生成Collection?那是因為,key總是獨(dú)一無二的,value允許重復(fù)。