2009年6月1日
#
Jsp頁(yè)面在URL中傳遞參數(shù)會(huì)出現(xiàn)亂碼可以采用以下方法解決:一、使用更改Tomcat的方法。二、使用JSP的scriptLet view plaincopy to clipboardprint?三、使用自定義JSTL。
Jsp頁(yè)面在URL中傳遞參數(shù)會(huì)出現(xiàn)亂碼
解決方法如下:
一、使用更改Tomcat的方法。這個(gè)方法簡(jiǎn)單,但是需要改動(dòng)的地方是服務(wù)器軟件級(jí)別的,如果稍微變動(dòng)系統(tǒng)將無(wú)法正確轉(zhuǎn)碼,移植性不高。
1、來(lái)到tomcat目錄,找到conf目錄下的server.xml問(wèn)價(jià),打開(kāi),找到<Connector>標(biāo)簽,在最后添加URIEncoding=’GBK’,效果如下:
view plaincopy to clipboardprint?
<Connector
port="8080"maxThreads="150"minSpareThreads="25"maxSpareThreads="75"
enableLookups="false"redirectPort="8443"acceptCount="100"
debug="0"connectionTimeout="20000"
disableUploadTimeout="true"URIEncoding=″GBK″/>
<Connectorport="8080" maxThreads="150" minSpareThreads="25" maxSpareThreads="75"enableLookups="false" redirectPort="8443" acceptCount="100"debug="0" connectionTimeout="20000"disableUploadTimeout="true" URIEncoding=″GBK″/>
這種方法對(duì)get這個(gè)方法測(cè)試成功。
2、在每個(gè)Jsp頁(yè)面添加如下代碼
view plaincopy to clipboardprint?
<%@pagepageEncoding=”gb2312″%>
<%@pagecontentType=”text/html;charset=gb2312″%>
<%request.setCharacterEncoding(”gb2312″);%>
<%@ page pageEncoding=”gb2312″%><%@ page contentType=”text/html;charset=gb2312″%><%request.setCharacterEncoding(”gb2312″);%>
編碼方式也可以改成GBK,支持繁體中文。重啟Tomcat,這一步很重要,否則可能看不到效果。
這種方法對(duì)post測(cè)試成功。建議兩種同步使用。
二、使用JSP的scriptLet
view plaincopy to clipboardprint?
<%=newString(elValue.getBytes("iso-8859-1"),"GBK")%>
<%=new String(elValue.getBytes("iso-8859-1"),"GBK")%>
三、使用自定義JSTL
這是我們的重點(diǎn)所在,這里要詳細(xì)說(shuō)明.
第一,編寫(xiě)自定義標(biāo)簽
第二編寫(xiě)tag文件
第三部署tag文件
第四使用吧;)很簡(jiǎn)單的.
方法覆蓋必須滿足下列條件
(1) 子類的方法的名稱及參數(shù)必須和所覆蓋的方法相同
(2) 子類的方法返回類型必須和所覆蓋的方法相同
(3) 子類方法不能縮小所覆蓋方法的訪問(wèn)權(quán)限
(4) 子類方法不能拋出比所覆蓋方法更多的異常
重載方法必須滿足下列條件
(1) 方法名必須相同
(2) 方法的參數(shù)簽名必須相同
(3) 方法的返回類型和方法的修飾符可以不相同
順便說(shuō)說(shuō)方法覆蓋和重載的區(qū)別
初次見(jiàn)到這兩個(gè)單詞并沒(méi)有什么特別的感覺(jué),但是時(shí)間長(zhǎng)了,卻發(fā)現(xiàn)書(shū)上一會(huì)兒用override,一會(huì)兒又用overload,搞得我的迷迷糊。于是就做了個(gè)總結(jié),希望能對(duì)和我一樣對(duì)這兩個(gè)概念模糊不清的網(wǎng)友有一個(gè)幫助。
override可以翻譯為覆蓋,從字面就可以知道,它是覆蓋了一個(gè)方法并且對(duì)其重寫(xiě),以求達(dá)到不同的作用。對(duì)我們來(lái)說(shuō)最熟悉的覆蓋就是對(duì)接口方法的實(shí)現(xiàn),在接口中一般只是對(duì)方法進(jìn)行了聲明,而我們?cè)趯?shí)現(xiàn)時(shí),就需要實(shí)現(xiàn)接口聲明的所有方法。除了這個(gè)典型的用法以外,我們?cè)诶^承中也可能會(huì)在子類覆蓋父類中的方法。在覆蓋要注意以下的幾點(diǎn):
1、覆蓋的方法的標(biāo)志必須要和被覆蓋的方法的標(biāo)志完全匹配,才能達(dá)到覆蓋的效果;
2、覆蓋的方法的返回值必須和被覆蓋的方法的返回一致;
3、覆蓋的方法所拋出的異常必須和被覆蓋方法的所拋出的異常一致,或者是其子類;
4、被覆蓋的方法不能為private,否則在其子類中只是新定義了一個(gè)方法,并沒(méi)有對(duì)其進(jìn)行覆蓋。
overload對(duì)我們來(lái)說(shuō)可能比較熟悉,可以翻譯為重載,它是指我們可以定義一些名稱相同的方法,通過(guò)定義不同的輸入?yún)?shù)來(lái)區(qū)分這些方法,然后再調(diào)用時(shí),VM就會(huì)根據(jù)不同的參數(shù)樣式,來(lái)選擇合適的方法執(zhí)行。在使用重載要注意以下的幾點(diǎn):
1、在使用重載時(shí)只能通過(guò)不同的參數(shù)樣式。例如,不同的參數(shù)類型,不同的參數(shù)個(gè)數(shù),不同的參數(shù)順序(當(dāng)然,同一方法內(nèi)的幾個(gè)參數(shù)類型必須不一樣,例如可以是fun(int,float),但是不能為fun(int,int));
2、不能通過(guò)訪問(wèn)權(quán)限、返回類型、拋出的異常進(jìn)行重載;
3、方法的異常類型和數(shù)目不會(huì)對(duì)重載造成影響;
4、對(duì)于繼承來(lái)說(shuō),如果某一方法在父類中是訪問(wèn)權(quán)限是priavte,那么就不能在子類對(duì)其進(jìn)行重載,如果定義的話,也只是定義了一個(gè)新方法,而不會(huì)達(dá)到重載的效果。
下面是對(duì)override和overload的測(cè)試程序,其中注釋中的內(nèi)容都是會(huì)產(chǎn)生編譯錯(cuò)誤的代碼,我們將注釋去掉,看看在編譯時(shí)會(huì)產(chǎn)生什么效果。
//對(duì)overload測(cè)試的文件:OverloadTest.java
publicnewOverrideTest1();
try{
test.fun();
test.fun1();
}catch(Exceptione){}
}
}
classOverrideTest1extendsOverrideTest{
//以下正常Override
publicvoidfun()throwsTestException2{
System.out.println("funinOverrideTest1");
}
//不能Override父類中的方法,因?yàn)樗x了不同的異常類型和
//返回值。
//publicintfun()throwsTestException1{
//System.out.println("methodfuninTest");
//return1;
//}
//不能Override父類中的方法,因?yàn)樗鼟伋隽吮雀割愔蟹欠ǚ秶?
//更大的異常。
//publicvoidfun()throwsException{
//System.out.println("funinOverrideTest1");
//}
//這個(gè)方法并沒(méi)有Override父類中的fun1方法,因?yàn)檫@個(gè)方法在
//父類是private類型,所以這里只是相當(dāng)于定義了一個(gè)新方法。
publicvoidfun1(){
System.out.println("methodfun1inTest");
}
}
classTestExceptionextendsException{
publicTestException(Stringmsg){
super(msg);
}
}
classTestException1extendsTestException{
publicTestException1(Stringmsg){
super(msg);
}
}
classTestException2extendsTestException{
publicTestException2(Stringmsg){
super(msg);
}
}
參考資料:http://zhidao.baidu.com/question/1996221.html
5.1 是否可以繼承String類?
String類是final類故不可以繼承。
5.2 swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上?
switch(expr1)中,expr1是一個(gè)整數(shù)表達(dá)式。因此傳遞給 switch 和 case 語(yǔ)句的參數(shù)應(yīng)該是 int、 short、 char 或者 byte.long,string 都不能作用于swtich.
5.3 try {}里有一個(gè)return語(yǔ)句,那么緊跟在這個(gè)try后的finally {}里的code會(huì)不會(huì)被執(zhí)行,什么時(shí)候被執(zhí)行,在return前還是后?
會(huì)執(zhí)行,在return前執(zhí)行。
5.4 編程題: 用最有效率的方法算出2乘以8等於幾?
2 << 3
5.5 兩個(gè)對(duì)象值相同(x.equals(y) == true),但卻可有不同的hash code,這句話對(duì)不對(duì)?
不對(duì),有相同的hash code.
5.6 當(dāng)一個(gè)對(duì)象被當(dāng)作參數(shù)傳遞到一個(gè)方法后,此方法可改變這個(gè)對(duì)象的屬性,并可返回變化后的結(jié)果,那么這里到底是值傳遞還是引用傳遞?
是值傳遞。Java 編程語(yǔ)言只有值傳遞參數(shù)。當(dāng)一個(gè)對(duì)象實(shí)例作為一個(gè)參數(shù)被傳遞到方法中時(shí),參數(shù)的值就是對(duì)該對(duì)象的引用。對(duì)象的內(nèi)容可以在被調(diào)用的方法中改變,但對(duì)象的引用是永遠(yuǎn)不會(huì)改變的。
5.7 當(dāng)一個(gè)線程進(jìn)入一個(gè)對(duì)象的一個(gè)synchronized方法后,其它線程是否可進(jìn)入此對(duì)象的其它方法?
不能,一個(gè)對(duì)象的一個(gè)synchronized方法只能由一個(gè)線程訪問(wèn)。
(這個(gè)有點(diǎn)疑問(wèn):?jiǎn)柕氖沁@個(gè)對(duì)象的其他方法,而非synchonized方法。)
5.8 編程題: 寫(xiě)一個(gè)Singleton出來(lái)。
Singleton模式主要作用是保證在Java應(yīng)用程序中,一個(gè)類Class只有一個(gè)實(shí)例存在。
一般Singleton模式通常有幾種種形式:
第一種形式: 定義一個(gè)類,它的構(gòu)造函數(shù)為private的,它有一個(gè)static的private的該類變量,在類初始化時(shí)實(shí)例話,通過(guò)一個(gè)public的getInstance方法獲取對(duì)它的引用,繼而調(diào)用其中的方法。
package test;
public class Singleton {
private Singleton() {
}
// 在自己內(nèi)部定義自己一個(gè)實(shí)例,是不是很奇怪?
// 注意這是private 只供內(nèi)部調(diào)用
private static Singleton instance = new Singleton();
// 這里提供了一個(gè)供外部訪問(wèn)本class的靜態(tài)方法,可以直接訪問(wèn)
public static Singleton getInstance() {
return instance;
}
}
|
第二種形式:
package test;
public class Singleton {
private static Singleton instance = null;
public static synchronized Singleton getInstance() {
// 這個(gè)方法比上面有所改進(jìn),不用每次都進(jìn)行生成對(duì)象,只是第一次
// 使用時(shí)生成實(shí)例,提高了效率!
if (instance == null)
instance = new Singleton();
return instance;
}
}
|
其他形式:
定義一個(gè)類,它的構(gòu)造函數(shù)為private的,所有方法為static的。
一般認(rèn)為第一種形式要更加安全些
5.9 Java的接口和C++的虛類的相同和不同處。
由于Java不支持多繼承,而有可能某個(gè)類或?qū)ο笠褂梅謩e在幾個(gè)類或?qū)ο罄锩娴姆椒ɑ驅(qū)傩裕F(xiàn)有的單繼承機(jī)制就不能滿足要求。與繼承相比,接口有更高的靈活性,因?yàn)榻涌谥袥](méi)有任何實(shí)現(xiàn)代碼。當(dāng)一個(gè)類實(shí)現(xiàn)了接口以后,該類要實(shí)現(xiàn)接口里面所有的方法和屬性,并且接口里面的屬性在默認(rèn)狀態(tài)下面都是public static,所有方法默認(rèn)情況下是public.一個(gè)類可以實(shí)現(xiàn)多個(gè)接口。
5.10 Java中的異常處理機(jī)制的簡(jiǎn)單原理和應(yīng)用。
當(dāng)Java程序違反了Java的語(yǔ)義規(guī)則時(shí),Java虛擬機(jī)就會(huì)將發(fā)生的錯(cuò)誤表示為一個(gè)異常。違反語(yǔ)義規(guī)則包括2種情況。一種是Java類庫(kù)內(nèi)置的語(yǔ)義檢查。例如數(shù)組下標(biāo)越界,會(huì)引發(fā)IndexOutOfBoundsException;訪問(wèn)null的對(duì)象時(shí)會(huì)引發(fā)NullPointerException.另一種情況就是Java允許程序員擴(kuò)展這種語(yǔ)義檢查,程序員可以創(chuàng)建自己的異常,并自由選擇在何時(shí)用throw關(guān)鍵字引發(fā)異常。所有的異常都是java.lang.Thowable的子類。
4.1 EJB包括(SessionBean,EntityBean)說(shuō)出他們的生命周期,及如何管理事務(wù)的?
SessionBean:Stateless Session Bean 的生命周期是由容器決定的,當(dāng)客戶機(jī)發(fā)出請(qǐng)求要建立一個(gè)Bean的實(shí)例時(shí),EJB容器不一定要?jiǎng)?chuàng)建一個(gè)新的Bean的實(shí)例供客戶機(jī)調(diào)用,而是隨便找一個(gè)現(xiàn)有的實(shí)例提供給客戶機(jī)。當(dāng)客戶機(jī)第一次調(diào)用一個(gè)Stateful Session Bean 時(shí),容器必須立即在服務(wù)器中創(chuàng)建一個(gè)新的Bean實(shí)例,并關(guān)聯(lián)到客戶機(jī)上,以后此客戶機(jī)調(diào)用Stateful Session Bean 的方法時(shí)容器會(huì)把調(diào)用分派到與此客戶機(jī)相關(guān)聯(lián)的Bean實(shí)例。
EntityBean:Entity Beans能存活相對(duì)較長(zhǎng)的時(shí)間,并且狀態(tài)是持續(xù)的。只要數(shù)據(jù)庫(kù)中的數(shù)據(jù)存在,Entity beans就一直存活。而不是按照應(yīng)用程序或者服務(wù)進(jìn)程來(lái)說(shuō)的。即使EJB容器崩潰了,Entity beans也是存活的。Entity Beans生命周期能夠被容器或者 Beans自己管理。
EJB通過(guò)以下技術(shù)管理事務(wù):對(duì)象管理組織(OMG)的對(duì)象事務(wù)服務(wù)(OTS),Sun Microsystems的Transaction Service(JTS)、Java Transaction API(JTA),開(kāi)發(fā)組(X/Open)的XA接口。
4.2 應(yīng)用服務(wù)器有那些?
BEA WebLogic Server,IBM WebSphere Application Server,Oracle9i Application Server,jBoss,Tomcat
4.3 給我一個(gè)你最常見(jiàn)到的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.
4.4 接口是否可繼承接口? 抽象類是否可實(shí)現(xiàn)(implements)接口? 抽象類是否可繼承實(shí)體類(concrete class)?
接口可以繼承接口。抽象類可以實(shí)現(xiàn)(implements)接口,抽象類是否可繼承實(shí)體類,但前提是實(shí)體類必須有明確的構(gòu)造函數(shù)。
4.5 List, Set, Map是否繼承自Collection接口?
List,Set是,Map不是
4.6 說(shuō)出數(shù)據(jù)連接池的工作機(jī)制是什么?
J2EE服務(wù)器啟動(dòng)時(shí)會(huì)建立一定數(shù)量的池連接,并一直維持不少于此數(shù)目的池連接。客戶端程序需要連接時(shí),池驅(qū)動(dòng)程序會(huì)返回一個(gè)未使用的池連接并將其表記為忙。如果當(dāng)前沒(méi)有空閑連接,池驅(qū)動(dòng)程序就新建一定數(shù)量的連接,新建連接的數(shù)量有配置參數(shù)決定。當(dāng)使用的池連接調(diào)用完成后,池驅(qū)動(dòng)程序?qū)⒋诉B接表記為空閑,其他調(diào)用就可以使用這個(gè)連接。
4.7 abstract的method是否可同時(shí)是static,是否可同時(shí)是native,是否可同時(shí)是synchronized?
都不能
4.8 數(shù)組有沒(méi)有l(wèi)ength()這個(gè)方法? String有沒(méi)有l(wèi)ength()這個(gè)方法?
數(shù)組沒(méi)有l(wèi)ength()這個(gè)方法,有l(wèi)ength的屬性。String有有l(wèi)ength()這個(gè)方法。
4.9 Set里的元素是不能重復(fù)的,那么用什么方法來(lái)區(qū)分重復(fù)與否呢? 是用==還是equals()? 它們有何區(qū)別?
Set里的元素是不能重復(fù)的,那么用iterator()方法來(lái)區(qū)分重復(fù)與否。equals()是判讀兩個(gè)Set是否相等。
equals()和==方法決定引用值是否指向同一對(duì)象equals()在類中被覆蓋,為的是當(dāng)兩個(gè)分離的對(duì)象的內(nèi)容和類型相配的話,返回真值。
4.10 構(gòu)造器Constructor是否可被override?
構(gòu)造器Constructor不能被繼承,因此不能重寫(xiě)Overriding,但可以被重載Overloading.
3.1 Static Nested Class 和 Inner Class的不同。
Static Nested Class是被聲明為靜態(tài)(static)的內(nèi)部類,它可以不依賴于外部類實(shí)例被實(shí)例化。而通常的內(nèi)部類需要在外部類實(shí)例化后才能實(shí)例化。
3.2 JSP中動(dòng)態(tài)INCLUDE與靜態(tài)INCLUDE的區(qū)別?
動(dòng)態(tài)INCLUDE用jsp:include動(dòng)作實(shí)現(xiàn) <jsp:include page="included.jsp" flush="true" />它總是會(huì)檢查所含文件中的變化,適合用于包含動(dòng)態(tài)頁(yè)面,并且可以帶參數(shù)。
靜態(tài)INCLUDE用include偽碼實(shí)現(xiàn),定不會(huì)檢查所含文件的變化,適用于包含靜態(tài)頁(yè)面<%@ include file="included.htm" %>
3.3 什么時(shí)候用assert.
assertion(斷言)在軟件開(kāi)發(fā)中是一種常用的調(diào)試方式,很多開(kāi)發(fā)語(yǔ)言中都支持這種機(jī)制。在實(shí)現(xiàn)中,assertion就是在程序中的一條語(yǔ)句,它對(duì)一個(gè)boolean表達(dá)式進(jìn)行檢查,一個(gè)正確程序必須保證這個(gè)boolean表達(dá)式的值為true;如果該值為false,說(shuō)明程序已經(jīng)處于不正確的狀態(tài)下,系統(tǒng)將給出警告或退出。一般來(lái)說(shuō),assertion用于保證程序最基本、關(guān)鍵的正確性。assertion檢查通常在開(kāi)發(fā)和測(cè)試時(shí)開(kāi)啟。為了提高性能,在軟件發(fā)布后,assertion檢查通常是關(guān)閉的。
3.4 GC是什么? 為什么要有GC?
GC是垃圾收集的意思(Gabage Collection),內(nèi)存處理是編程人員容易出現(xiàn)問(wèn)題的地方,忘記或者錯(cuò)誤的內(nèi)存回收會(huì)導(dǎo)致程序或系統(tǒng)的不穩(wěn)定甚至崩潰,Java提供的GC功能可以自動(dòng)監(jiān)測(cè)對(duì)象是否超過(guò)作用域從而達(dá)到自動(dòng)回收內(nèi)存的目的,Java語(yǔ)言沒(méi)有提供釋放已分配內(nèi)存的顯示操作方法。
3.5 short s1 = 1; s1 = s1 + 1;有什么錯(cuò)? short s1 = 1; s1 += 1;有什么錯(cuò)?
short s1 = 1; s1 = s1 + 1; (s1+1運(yùn)算結(jié)果是int型,需要強(qiáng)制轉(zhuǎn)換類型)
short s1 = 1; s1 += 1;(可以正確編譯)
3.6 Math.round(11.5)等於多少? Math.round(-11.5)等於多少?
Math.round(11.5)==12
Math.round(-11.5)==-11
round方法返回與參數(shù)最接近的長(zhǎng)整數(shù),參數(shù)加1/2后求其floor.
3.7 String s = new String("xyz");創(chuàng)建了幾個(gè)String Object?
兩個(gè)
3.8 設(shè)計(jì)4個(gè)線程,其中兩個(gè)線程每次對(duì)j增加1,另外兩個(gè)線程對(duì)j每次減少1.寫(xiě)出程序。
以下程序使用內(nèi)部類實(shí)現(xiàn)線程,對(duì)j增減的時(shí)候沒(méi)有考慮順序問(wèn)題。
package test;
public class ThreadTest1 {
private int j;
public static void main(String args[]) {
ThreadTest1 tt = new ThreadTest1();
Inc inc = tt.new Inc();
Dec dec = tt.new Dec();
for (int i = 0; i < 2; i++) {
Thread t = new Thread(inc);
t.start();
t = new Thread(dec);
t.start();
}
}
private synchronized void inc() {
j++;
System.out.println(Thread.currentThread().getName() + "-inc:" + j);
}
private synchronized void dec() {
j--;
System.out.println(Thread.currentThread().getName() + "-dec:" + j);
}
class Inc implements Runnable {
public void run() {
for (int i = 0; i < 3; i++) {
inc();
}
}
}
class Dec implements Runnable {
public void run() {
for (int i = 0; i < 3; i++) {
dec();
}
}
}
}
|
3.9 Java有沒(méi)有g(shù)oto?
Java中的保留字,現(xiàn)在沒(méi)有在Java中使用。
3.10 啟動(dòng)一個(gè)線程是用run()還是start()?
啟動(dòng)一個(gè)線程是調(diào)用start()方法,使線程所代表的虛擬處理機(jī)處于可運(yùn)行狀態(tài),這意味著它可以由JVM調(diào)度并執(zhí)行。這并不意味著線程就會(huì)立即運(yùn)行。run()方法可以產(chǎn)生必須退出的標(biāo)志來(lái)停止一個(gè)線程。
2.1 HashMap和Hashtable的區(qū)別。
HashMap是Hashtable的輕量級(jí)實(shí)現(xiàn)(非線程安全的實(shí)現(xiàn)),他們都完成了Map接口,主要區(qū)別在于HashMap允許空(null)鍵值(key),由于非線程安全,效率上可能高于Hashtable.
HashMap允許將null作為一個(gè)entry的key或者value,而Hashtable不允許。
HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey.因?yàn)閏ontains方法容易讓人引起誤解。
Hashtable繼承自Dictionary類,而HashMap是Java1.2引進(jìn)的Map interface的一個(gè)實(shí)現(xiàn)。
最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多個(gè)線程訪問(wèn)Hashtable時(shí),不需要自己為它的方法實(shí)現(xiàn)同步,而HashMap 就必須為之提供外同步。Hashtable和HashMap采用的hash/rehash算法都大概一樣,所以性能不會(huì)有很大的差異。
2.2 final, finally, finalize的區(qū)別。
final 用于聲明屬性,方法和類,分別表示屬性不可變,方法不可覆蓋,類不可繼承。
finally是異常處理語(yǔ)句結(jié)構(gòu)的一部分,表示總是執(zhí)行。
finalize是Object類的一個(gè)方法,在垃圾收集器執(zhí)行的時(shí)候會(huì)調(diào)用被回收對(duì)象的此方法,可以覆蓋此方法提供垃圾收集時(shí)的其他資源回收,例如關(guān)閉文件等。
2.3 sleep() 和 wait() 有什么區(qū)別?
sleep是線程類(Thread)的方法,導(dǎo)致此線程暫停執(zhí)行指定時(shí)間,給執(zhí)行機(jī)會(huì)給其他線程,但是監(jiān)控狀態(tài)依然保持,到時(shí)后會(huì)自動(dòng)恢復(fù)。調(diào)用sleep不會(huì)釋放對(duì)象鎖。
wait是Object類的方法,對(duì)此對(duì)象調(diào)用wait方法導(dǎo)致本線程放棄對(duì)象鎖,進(jìn)入等待此對(duì)象的等待鎖定池,只有針對(duì)此對(duì)象發(fā)出notify方法(或notifyAll)后本線程才進(jìn)入對(duì)象鎖定池準(zhǔn)備獲得對(duì)象鎖進(jìn)入運(yùn)行狀態(tài)。
2.4 Overload和Override的區(qū)別。Overloaded的方法是否可以改變返回值的類型?
方法的重寫(xiě)Overriding和重載Overloading是Java多態(tài)性的不同表現(xiàn)。重寫(xiě)Overriding是父類與子類之間多態(tài)性的一種表現(xiàn),重載Overloading是一個(gè)類中多態(tài)性的一種表現(xiàn)。如果在子類中定義某方法與其父類有相同的名稱和參數(shù),我們說(shuō)該方法被重寫(xiě) (Overriding)。子類的對(duì)象使用這個(gè)方法時(shí),將調(diào)用子類中的定義,對(duì)它而言,父類中的定義如同被"屏蔽"了。如果在一個(gè)類中定義了多個(gè)同名的方法,它們或有不同的參數(shù)個(gè)數(shù)或有不同的參數(shù)類型,則稱為方法的重載(Overloading)。Overloaded的方法是可以改變返回值的類型。
2.5 error和exception有什么區(qū)別?
error 表示恢復(fù)不是不可能但很困難的情況下的一種嚴(yán)重問(wèn)題。比如說(shuō)內(nèi)存溢出。不可能指望程序能處理這樣的情況。
exception 表示一種設(shè)計(jì)或?qū)崿F(xiàn)問(wèn)題。也就是說(shuō),它表示如果程序運(yùn)行正常,從不會(huì)發(fā)生的情況。
2.6 同步和異步有何異同,在什么情況下分別使用他們?舉例說(shuō)明。
如果數(shù)據(jù)將在線程間共享。例如正在寫(xiě)的數(shù)據(jù)以后可能被另一個(gè)線程讀到,或者正在讀的數(shù)據(jù)可能已經(jīng)被另一個(gè)線程寫(xiě)過(guò)了,那么這些數(shù)據(jù)就是共享數(shù)據(jù),必須進(jìn)行同步存取。
當(dāng)應(yīng)用程序在對(duì)象上調(diào)用了一個(gè)需要花費(fèi)很長(zhǎng)時(shí)間來(lái)執(zhí)行的方法,并且不希望讓程序等待方法的返回時(shí),就應(yīng)該使用異步編程,在很多情況下采用異步途徑往往更有效率。
2.7 abstract class和interface有什么區(qū)別?
聲明方法的存在而不去實(shí)現(xiàn)它的類被叫做抽象類(abstract class),它用于要?jiǎng)?chuàng)建一個(gè)體現(xiàn)某些基本行為的類,并為該類聲明方法,但不能在該類中實(shí)現(xiàn)該類的情況。不能創(chuàng)建abstract 類的實(shí)例。然而可以創(chuàng)建一個(gè)變量,其類型是一個(gè)抽象類,并讓它指向具體子類的一個(gè)實(shí)例。不能有抽象構(gòu)造函數(shù)或抽象靜態(tài)方法。Abstract 類的子類為它們父類中的所有抽象方法提供實(shí)現(xiàn),否則它們也是抽象類為。取而代之,在子類中實(shí)現(xiàn)該方法。知道其行為的其它類可以在類中實(shí)現(xiàn)這些方法。
接口(interface)是抽象類的變體。在接口中,所有方法都是抽象的。多繼承性可通過(guò)實(shí)現(xiàn)這樣的接口而獲得。接口中的所有方法都是抽象的,沒(méi)有一個(gè)有程序體。接口只可以定義static final成員變量。接口的實(shí)現(xiàn)與子類相似,除了該實(shí)現(xiàn)類不能從接口定義中繼承行為。當(dāng)類實(shí)現(xiàn)特殊接口時(shí),它定義(即將程序體給予)所有這種接口的方法。然后,它可以在實(shí)現(xiàn)了該接口的類的任何對(duì)象上調(diào)用接口的方法。由于有抽象類,它允許使用接口名作為引用變量的類型。通常的動(dòng)態(tài)聯(lián)編將生效。引用可以轉(zhuǎn)換到接口類型或從接口類型轉(zhuǎn)換,instanceof 運(yùn)算符可以用來(lái)決定某對(duì)象的類是否實(shí)現(xiàn)了接口。
2.8 heap和stack有什么區(qū)別。
棧是一種線形集合,其添加和刪除元素的操作應(yīng)在同一段完成。棧按照后進(jìn)先出的方式進(jìn)行處理。
堆是棧的一個(gè)組成元素。
2.9 forward 和redirect的區(qū)別
forward是服務(wù)器請(qǐng)求資源,服務(wù)器直接訪問(wèn)目標(biāo)地址的URL,把那個(gè)URL的響應(yīng)內(nèi)容讀取過(guò)來(lái),然后把這些內(nèi)容再發(fā)給瀏覽器,瀏覽器根本不知道服務(wù)器發(fā)送的內(nèi)容是從哪兒來(lái)的,所以它的地址欄中還是原來(lái)的地址。
redirect就是服務(wù)端根據(jù)邏輯,發(fā)送一個(gè)狀態(tài)碼,告訴瀏覽器重新去請(qǐng)求那個(gè)地址,一般來(lái)說(shuō)瀏覽器會(huì)用剛才請(qǐng)求的所有參數(shù)重新請(qǐng)求,所以session,request參數(shù)都可以獲取。
2.10 EJB與Java Bean的區(qū)別?
Java Bean 是可復(fù)用的組件,對(duì)Java Bean并沒(méi)有嚴(yán)格的規(guī)范,理論上講,任何一個(gè)Java類都可以是一個(gè)Bean.但通常情況下,由于Java Bean是被容器所創(chuàng)建(如Tomcat)的,所以Java Bean應(yīng)具有一個(gè)無(wú)參的構(gòu)造器,另外,通常Java Bean還要實(shí)現(xiàn)Serializable接口用于實(shí)現(xiàn)Bean的持久性。Java Bean實(shí)際上相當(dāng)于微軟COM模型中的本地進(jìn)程內(nèi)COM組件,它是不能被跨進(jìn)程訪問(wèn)的。Enterprise Java Bean 相當(dāng)于DCOM,即分布式組件。它是基于Java的遠(yuǎn)程方法調(diào)用(RMI)技術(shù)的,所以EJB可以被遠(yuǎn)程訪問(wèn)(跨進(jìn)程、跨計(jì)算機(jī))。但EJB必須被布署在諸如Webspere、WebLogic這樣的容器中,EJB客戶從不直接訪問(wèn)真正的EJB組件,而是通過(guò)其容器訪問(wèn)。EJB容器是EJB組件的代理,EJB組件由容器所創(chuàng)建和管理。客戶通過(guò)容器來(lái)訪問(wèn)真正的EJB組件。
一、面向?qū)ο蟮奶卣饔心男┓矫?
1.抽象:
抽象就是忽略一個(gè)主題中與當(dāng)前目標(biāo)無(wú)關(guān)的那些方面,以便更充分地注意與當(dāng)前目標(biāo)有關(guān)的方面。抽象并不打算了解全部問(wèn)題,而只是選擇其中的一部分,暫時(shí)不用部分細(xì)節(jié)。抽象包括兩個(gè)方面,一是過(guò)程抽象,二是數(shù)據(jù)抽象。
2.繼承:
繼承是一種聯(lián)結(jié)類的層次模型,并且允許和鼓勵(lì)類的重用,它提供了一種明確表述共性的方法。對(duì)象的一個(gè)新類可以從現(xiàn)有的類中派生,這個(gè)過(guò)程稱為類繼承。新類繼承了原始類的特性,新類稱為原始類的派生類(子類),而原始類稱為新類的基類(父類)。派生類可以從它的基類那里繼承方法和實(shí)例變量,并且類可以修改或增加新的方法使之更適合特殊的需要。
3.封裝:
封裝是把過(guò)程和數(shù)據(jù)包圍起來(lái),對(duì)數(shù)據(jù)的訪問(wèn)只能通過(guò)已定義的界面。面向?qū)ο笥?jì)算始于這個(gè)基本概念,即現(xiàn)實(shí)世界可以被描繪成一系列完全自治、封裝的對(duì)象,這些對(duì)象通過(guò)一個(gè)受保護(hù)的接口訪問(wèn)其他對(duì)象。
4.多態(tài)性:
多態(tài)性是指允許不同類的對(duì)象對(duì)同一消息作出響應(yīng)。多態(tài)性包括參數(shù)化多態(tài)性和包含多態(tài)性。多態(tài)性語(yǔ)言具有靈活、抽象、行為共享、代碼共享的優(yōu)勢(shì),很好的解決了應(yīng)用程序函數(shù)同名問(wèn)題。
二、String是最基本的數(shù)據(jù)類型嗎?
基本數(shù)據(jù)類型包括byte、int、char、long、float、double、boolean和short. java.lang.String類是final類型的,因此不可以繼承這個(gè)類、不能修改這個(gè)類。為了提高效率節(jié)省空間,我們應(yīng)該用StringBuffer類。
三、int 和 Integer 有什么區(qū)別?
Java 提供兩種不同的類型:引用類型和原始類型(或內(nèi)置類型)。int是Java的原始數(shù)據(jù)類型,Integer是Java為int提供的封裝類。Java為每個(gè)原始類型提供了封裝類。
原始類型封裝類
boolean Boolean
char Character
byte Byte
short Short
int Integer
long Long
float Float
double Double
引用類型和原始類型的行為完全不同,并且它們具有不同的語(yǔ)義。引用類型和原始類型具有不同的特征和用法,它們包括:大小和速度問(wèn)題,這種類型以哪種類型的數(shù)據(jù)結(jié)構(gòu)存儲(chǔ),當(dāng)引用類型和原始類型用作某個(gè)類的實(shí)例數(shù)據(jù)時(shí)所指定的缺省值。對(duì)象引用實(shí)例變量的缺省值為 null,而原始類型實(shí)例變量的缺省值與它們的類型有關(guān)。
四、String 和StringBuffer的區(qū)別?
Java平臺(tái)提供了兩個(gè)類:String和StringBuffer,它們可以儲(chǔ)存和操作字符串,即包含多個(gè)字符的字符數(shù)據(jù)。這個(gè)String類提供了數(shù)值不可改變的字符串。而這個(gè)StringBuffer類提供的字符串進(jìn)行修改。當(dāng)你知道字符數(shù)據(jù)要改變的時(shí)候你就可以使用StringBuffer.典型地,你可以使用StringBuffer來(lái)動(dòng)態(tài)構(gòu)造字符數(shù)據(jù)。
五、運(yùn)行時(shí)異常與一般異常有何異同?
異常表示程序運(yùn)行過(guò)程中可能出現(xiàn)的非正常狀態(tài),運(yùn)行時(shí)異常表示虛擬機(jī)的通常操作中可能遇到的異常,是一種常見(jiàn)運(yùn)行錯(cuò)誤。Java編譯器要求方法必須聲明拋出可能發(fā)生的非運(yùn)行時(shí)異常,但是并不要求必須聲明拋出未被捕獲的運(yùn)行時(shí)異常。
六、說(shuō)出Servlet的生命周期,并說(shuō)出Servlet和CGI的區(qū)別。
Servlet被服務(wù)器實(shí)例化后,容器運(yùn)行其init方法,請(qǐng)求到達(dá)時(shí)運(yùn)行其service方法,service方法自動(dòng)派遣運(yùn)行與請(qǐng)求對(duì)應(yīng)的doXXX方法(doGet,doPost)等,當(dāng)服務(wù)器決定將實(shí)例銷毀的時(shí)候調(diào)用其destroy方法。
與CGI的區(qū)別在于Servlet處于服務(wù)器進(jìn)程中,它通過(guò)多線程方式運(yùn)行其service方法,一個(gè)實(shí)例可以服務(wù)于多個(gè)請(qǐng)求,并且其實(shí)例一般不會(huì)銷毀,而CGI對(duì)每個(gè)請(qǐng)求都產(chǎn)生新的進(jìn)程,服務(wù)完成后就銷毀,所以效率上低于CGI.
七、說(shuō)出ArrayList,Vector, LinkedList的存儲(chǔ)性能和特性
ArrayList和Vector都是使用數(shù)組方式存儲(chǔ)數(shù)據(jù),此數(shù)組元素?cái)?shù)大于實(shí)際存儲(chǔ)的數(shù)據(jù)以便增加和插入元素,它們都允許直接按序號(hào)索引元素,但是插入元素要涉及數(shù)組元素移動(dòng)等內(nèi)存操作,所以索引數(shù)據(jù)快而插入數(shù)據(jù)慢,Vector由于使用了synchronized方法(線程安全),通常性能上較ArrayList差,而LinkedList使用雙向鏈表實(shí)現(xiàn)存儲(chǔ),按序號(hào)索引數(shù)據(jù)需要進(jìn)行前向或后向遍歷,但是插入數(shù)據(jù)時(shí)只需要記錄本項(xiàng)的前后項(xiàng)即可,所以插入速度較快。
八、EJB是基于哪些技術(shù)實(shí)現(xiàn)的?并說(shuō)出SessionBean和EntityBean的區(qū)別,StatefulBean和StatelessBean的區(qū)別。
EJB包括Session Bean、Entity Bean、Message Driven Bean,基于JNDI、RMI、JAT等技術(shù)實(shí)現(xiàn)。
SessionBean在J2EE應(yīng)用程序中被用來(lái)完成一些服務(wù)器端的業(yè)務(wù)操作,例如訪問(wèn)數(shù)據(jù)庫(kù)、調(diào)用其他EJB組件。EntityBean被用來(lái)代表應(yīng)用系統(tǒng)中用到的數(shù)據(jù)。
對(duì)于客戶機(jī),SessionBean是一種非持久性對(duì)象,它實(shí)現(xiàn)某些在服務(wù)器上運(yùn)行的業(yè)務(wù)邏輯。
對(duì)于客戶機(jī),EntityBean是一種持久性對(duì)象,它代表一個(gè)存儲(chǔ)在持久性存儲(chǔ)器中的實(shí)體的對(duì)象視圖,或是一個(gè)由現(xiàn)有企業(yè)應(yīng)用程序?qū)崿F(xiàn)的實(shí)體。
Session Bean 還可以再細(xì)分為 Stateful Session Bean 與 Stateless Session Bean ,這兩種的 Session Bean都可以將系統(tǒng)邏輯放在 method之中執(zhí)行,不同的是 Stateful Session Bean 可以記錄呼叫者的狀態(tài),因此通常來(lái)說(shuō),一個(gè)使用者會(huì)有一個(gè)相對(duì)應(yīng)的 Stateful Session Bean 的實(shí)體。Stateless Session Bean 雖然也是邏輯組件,但是他卻不負(fù)責(zé)記錄使用者狀態(tài),也就是說(shuō)當(dāng)使用者呼叫 Stateless Session Bean 的時(shí)候,EJB Container 并不會(huì)找尋特定的 Stateless Session Bean 的實(shí)體來(lái)執(zhí)行這個(gè) method.換言之,很可能數(shù)個(gè)使用者在執(zhí)行某個(gè) Stateless Session Bean 的 methods 時(shí),會(huì)是同一個(gè) Bean 的 Instance 在執(zhí)行。從內(nèi)存方面來(lái)看, Stateful Session Bean 與 Stateless Session Bean 比較, Stateful Session Bean 會(huì)消耗 J2EE Server 較多的內(nèi)存,然而 Stateful Session Bean 的優(yōu)勢(shì)卻在于他可以維持使用者的狀態(tài)。
九、Collection 和 Collections的區(qū)別。
Collection是集合類的上級(jí)接口,繼承與他的接口主要有Set 和List.
Collections是針對(duì)集合類的一個(gè)幫助類,他提供一系列靜態(tài)方法實(shí)現(xiàn)對(duì)各種集合的搜索、排序、線程安全化等操作。
十、&和&&的區(qū)別。
&是位運(yùn)算符,表示按位與運(yùn)算,&&是邏輯運(yùn)算符,表示邏輯與(and)。
JVM JRE JDK,這些東西到底是什么?
我們?cè)诎惭b好JDK后就可以想象成我們已經(jīng)買了一臺(tái)安裝好軟件的新的電腦。
JVM : Java Virtual Machine(Java虛擬機(jī)) 。所謂“虛擬機(jī)”顧名思義就是模擬出來(lái)的東西。就像是我們?cè)谟秒娔X看電視,但是電腦里并沒(méi)有像電視機(jī)里面一樣的硬件支持,但是我們?nèi)匀豢梢詮碾娔X里接受電視臺(tái)的節(jié)目。那是因?yàn)槲覀兙帉?xiě)了一個(gè)可以模擬電視機(jī)硬件工作的軟件運(yùn)行在電腦的平臺(tái)上面的原因。同樣JVM就是模擬了電腦的硬件,它同樣有著像CPU一樣可以執(zhí)行代碼的功能。它的實(shí)現(xiàn)具體有:指令集 寄存器組 類文件格式 棧 垃圾收集堆 內(nèi)存區(qū)。可以把它理解成是專門用來(lái)執(zhí)行Java程序的一臺(tái)機(jī)器。也就是說(shuō)JVM提供了Java執(zhí)行的硬件平臺(tái)。JVM上執(zhí)行的代碼都存放在 .CLASS 文件中。JVM只執(zhí)行字節(jié)碼文件。
JRE : Java Runtime Environment(Java運(yùn)行環(huán)境)。就是可以運(yùn)行Java程序的地方。就像是我們要在電腦上運(yùn)行一個(gè)視頻軟件的時(shí)候必須在Windos或者是Linux等操作系統(tǒng)上一樣。那我們就可以把它看做是一個(gè)操作系統(tǒng)。也就是說(shuō)JRE提供了Java執(zhí)行的軟件平臺(tái)。在運(yùn)行Java的過(guò)程中除了需要有JVM執(zhí)行Java代碼這個(gè)動(dòng)作外,還需要Java API(Application Programming Interface,應(yīng)用編程接口)說(shuō)簡(jiǎn)單的就是“類庫(kù)”。Java程序在運(yùn)行中沒(méi)有這些API是不行的,所以JRE包含JVM。
JDK : Java Development ToolKit(Java開(kāi)發(fā)工具包)。我們有了硬件和軟件兩個(gè)平臺(tái)后就可以做我們自己想做的事情了。JDK就是我們用來(lái)做事情的工具,它包括JRE還有其他工具。我們所說(shuō)版本的不同,也就是說(shuō)它里面的工具有差異。就像是你不同的工具箱里放著不同的工具一樣。舉個(gè)例子:最常用的一個(gè)就是javac,它是把.java的文件翻譯成.class文件的工具。然后讓JVM來(lái)執(zhí)行.class文件中的字節(jié)碼。(就像電腦的CPU只認(rèn)識(shí)0或1的道理)
如果一臺(tái)計(jì)算機(jī)的需求只是運(yùn)行Java程序,而不是去編寫(xiě)Java程序的時(shí)候,它只需要安裝JRE就可以了。現(xiàn)在大家知道JVM JRE JDK,這些東西到底是什么了吧。
摘要:本文從
Java異常最基本的概念、語(yǔ)法開(kāi)始講述了
Java異常處理的基本知識(shí),分析了Java異常體系結(jié)構(gòu),對(duì)比Spring的異常處理框架,闡述了異常處理的基本原則。并且作者提出了自己處理一個(gè)大型應(yīng)用系統(tǒng)異常的思想,并通過(guò)設(shè)計(jì)一個(gè)異常處理的框架來(lái)論述此思想。
一、 異常的概念和Java異常體系結(jié)構(gòu)
異常是程序運(yùn)行過(guò)程中出現(xiàn)的錯(cuò)誤。本文主要講授的是Java語(yǔ)言的異常處理。Java語(yǔ)言的異常處理框架,是Java語(yǔ)言健壯性的一個(gè)重要體現(xiàn)。
Java把異常當(dāng)作對(duì)象來(lái)處理,并定義一個(gè)基類java.lang.Throwable作為所有異常的超類。在Java API中已經(jīng)定義了許多異常類,這些異常類分為兩大類,錯(cuò)誤Error和異常Exception。Java異常體系結(jié)構(gòu)呈樹(shù)狀,其層次結(jié)構(gòu)圖如圖 1所示:

Thorwable類所有異常和錯(cuò)誤的超類,有兩個(gè)子類Error和Exception,分別表示錯(cuò)誤和異常。其中異常類Exception又分為運(yùn)行時(shí)異常(RuntimeException)和非運(yùn)行時(shí)異常,這兩種異常有很大的區(qū)別,也稱之為不檢查異常(Unchecked Exception)和檢查異常(Checked Exception)。下面將詳細(xì)講述這些異常之間的區(qū)別與聯(lián)系:
1、Error與Exception
Error是程序無(wú)法處理的錯(cuò)誤,比如OutOfMemoryError、ThreadDeath等。這些異常發(fā)生時(shí),Java虛擬機(jī)(JVM)一般會(huì)選擇線程終止。
Exception是程序本身可以處理的異常,這種異常分兩大類運(yùn)行時(shí)異常和非運(yùn)行時(shí)異常。程序中應(yīng)當(dāng)盡可能去處理這些異常。
2、運(yùn)行時(shí)異常和非運(yùn)行時(shí)異常
運(yùn)行時(shí)異常都是RuntimeException類及其子類異常,如NullPointerException、IndexOutOfBoundsException等,這些異常是不檢查異常,程序中可以選擇捕獲處理,也可以不處理。這些異常一般是由程序邏輯錯(cuò)誤引起的,程序應(yīng)該從邏輯角度盡可能避免這類異常的發(fā)生。
非運(yùn)行時(shí)異常是RuntimeException以外的異常,類型上都屬于Exception類及其子類。從程序語(yǔ)法角度講是必須進(jìn)行處理的異常,如果不處理,程序就不能編譯通過(guò)。如IOException、SQLException等以及用戶自定義的Exception異常,一般情況下不自定義檢查異常。
二、 異常的捕獲和處理
Java異常的捕獲和處理是一個(gè)不容易把握的事情,如果處理不當(dāng),不但會(huì)讓程序代碼的可讀性大大降低,而且導(dǎo)致系統(tǒng)性能低下,甚至引發(fā)一些難以發(fā)現(xiàn)的錯(cuò)誤。
Java異常處理涉及到五個(gè)關(guān)鍵字,分別是:try、catch、finally、throw、throws。下面將驟一介紹,通過(guò)認(rèn)識(shí)這五個(gè)關(guān)鍵字,掌握基本異常處理知識(shí)。
1、 異常處理的基本語(yǔ)法
在java中,異常處理的完整語(yǔ)法是:
try{
//(嘗試運(yùn)行的)程序代碼
}catch(異常類型 異常的變量名){
//異常處理代碼
}finally{
//異常發(fā)生,方法返回之前,總是要執(zhí)行的代碼
} |
以上語(yǔ)法有三個(gè)代碼塊:
try語(yǔ)句塊,表示要嘗試運(yùn)行代碼,try語(yǔ)句塊中代碼受異常監(jiān)控,其中代碼發(fā)生異常時(shí),會(huì)拋出異常對(duì)象。
catch語(yǔ)句塊會(huì)捕獲try代碼塊中發(fā)生的異常并在其代碼塊中做異常處理,catch語(yǔ)句帶一個(gè)Throwable類型的參數(shù),表示可捕獲異常類型。當(dāng)try中出現(xiàn)異常時(shí),catch會(huì)捕獲到發(fā)生的異常,并和自己的異常類型匹配,若匹配,則執(zhí)行catch塊中代碼,并將catch塊參數(shù)指向所拋的異常對(duì)象。catch語(yǔ)句可以有多個(gè),用來(lái)匹配多個(gè)中的一個(gè)異常,一旦匹配上后,就不再嘗試匹配別的catch塊了。通過(guò)異常對(duì)象可以獲取異常發(fā)生時(shí)完整的JVM堆棧信息,以及異常信息和異常發(fā)生的原因等。
finally語(yǔ)句塊是緊跟catch語(yǔ)句后的語(yǔ)句塊,這個(gè)語(yǔ)句塊總是會(huì)在方法返回前執(zhí)行,而不管是否try語(yǔ)句塊是否發(fā)生異常。并且這個(gè)語(yǔ)句塊總是在方法返回前執(zhí)行。目的是給程序一個(gè)補(bǔ)救的機(jī)會(huì)。這樣做也體現(xiàn)了Java語(yǔ)言的健壯性。
2、 try、catch、finally三個(gè)語(yǔ)句塊應(yīng)注意的問(wèn)題
第一、try、catch、finally三個(gè)語(yǔ)句塊均不能單獨(dú)使用,三者可以組成 try...catch...finally、try...catch、try...finally三種結(jié)構(gòu),catch語(yǔ)句可以有一個(gè)或多個(gè),finally語(yǔ)句最多一個(gè)。
第二、try、catch、finally三個(gè)代碼塊中變量的作用域?yàn)榇a塊內(nèi)部,分別獨(dú)立而不能相互訪問(wèn)。如果要在三個(gè)塊中都可以訪問(wèn),則需要將變量定義到這些塊的外面。
第三、多個(gè)catch塊時(shí)候,只會(huì)匹配其中一個(gè)異常類并執(zhí)行catch塊代碼,而不會(huì)再執(zhí)行別的catch塊,并且匹配catch語(yǔ)句的順序是由上到下。
3、throw、throws關(guān)鍵字
throw關(guān)鍵字是用于方法體內(nèi)部,用來(lái)拋出一個(gè)Throwable類型的異常。如果拋出了檢查異常,則還應(yīng)該在方法頭部聲明方法可能拋出的異常類型。該方法的調(diào)用者也必須檢查處理拋出的異常。如果所有方法都層層上拋獲取的異常,最終JVM會(huì)進(jìn)行處理,處理也很簡(jiǎn)單,就是打印異常消息和堆棧信息。如果拋出的是Error或RuntimeException,則該方法的調(diào)用者可選擇處理該異常。有關(guān)異常的轉(zhuǎn)譯會(huì)在下面說(shuō)明。
throws關(guān)鍵字用于方法體外部的方法聲明部分,用來(lái)聲明方法可能會(huì)拋出某些異常。僅當(dāng)拋出了檢查異常,該方法的調(diào)用者才必須處理或者重新拋出該異常。當(dāng)方法的調(diào)用者無(wú)力處理該異常的時(shí)候,應(yīng)該繼續(xù)拋出,而不是囫圇吞棗一般在catch塊中打印一下堆棧信息做個(gè)勉強(qiáng)處理。下面給出一個(gè)簡(jiǎn)單例子,看看如何使用這兩個(gè)關(guān)鍵字:
public static void test3() throws Exception{
//拋出一個(gè)檢查異常
throw new Exception("方法test3中的Exception");
}
3、 Throwable類中的常用方法
getCause():返回拋出異常的原因。如果 cause 不存在或未知,則返回 null。
getMessage():返回異常的消息信息。
printStackTrace():對(duì)象的堆棧跟蹤輸出至錯(cuò)誤輸出流,作為字段 System.err 的值。
|
三、 異常處理的一般原則
1、 能處理就早處理,拋出不去還不能處理的就想法消化掉或者轉(zhuǎn)換為RuntimeException處理。因?yàn)閷?duì)于一個(gè)應(yīng)用系統(tǒng)來(lái)說(shuō),拋出大量異常是有問(wèn)題的,應(yīng)該從程序開(kāi)發(fā)角度盡可能的控制異常發(fā)生的可能。
2、 對(duì)于檢查異常,如果不能行之有效的處理,還不如轉(zhuǎn)換為RuntimeException拋出。這樣也讓上層的代碼有選擇的余地――可處理也可不處理。
3、 對(duì)于一個(gè)應(yīng)用系統(tǒng)來(lái)說(shuō),應(yīng)該有自己的一套異常處理框架,這樣當(dāng)異常發(fā)生時(shí),也能得到統(tǒng)一的處理風(fēng)格,將優(yōu)雅的異常信息反饋給用戶。
四、 異常的轉(zhuǎn)譯與異常鏈
1、異常轉(zhuǎn)譯的原理
所謂的異常轉(zhuǎn)譯就是將一種異常轉(zhuǎn)換另一種新的異常,也許這種新的異常更能準(zhǔn)確表達(dá)程序發(fā)生異常。
在Java中有個(gè)概念就是異常原因,異常原因?qū)е庐?dāng)前拋出異常的那個(gè)異常對(duì)象,幾乎所有帶異常原因的異常構(gòu)造方法都使用Throwable類型做參數(shù),這也就為異常的轉(zhuǎn)譯提供了直接的支持,因?yàn)槿魏涡问降漠惓:湾e(cuò)誤都是Throwable的子類。比如將SQLException轉(zhuǎn)換為另外一個(gè)新的異常DAOException,可以這么寫(xiě):
先自定義一個(gè)異常DAOException:
public class DAOException extends RuntimeException {
//(省略了部分代碼)
public DAOException(String message, Throwable cause) {
super(message, cause);
}
} |
比如有一個(gè)SQLException類型的異常對(duì)象e,要轉(zhuǎn)換為DAOException,可以這么寫(xiě):
DAOException daoEx = new DAOException ( "SQL異常", e);
異常轉(zhuǎn)譯是針對(duì)所有繼承Throwable超類的類而言的,從編程的語(yǔ)法角度講,其子類之間都可以相互轉(zhuǎn)換。但是,從合理性和系統(tǒng)設(shè)計(jì)角度考慮,可將異常分為三類:Error、Exception、RuntimeException,筆者認(rèn)為,合理的轉(zhuǎn)譯關(guān)系圖應(yīng)該如圖 2:

圖 2 異常轉(zhuǎn)譯
為什么要這么做呢?筆者認(rèn)為,異常的處理存在著一套哲學(xué)思想:對(duì)于一個(gè)應(yīng)用系統(tǒng)來(lái)說(shuō),系統(tǒng)所發(fā)生的任何異常或者錯(cuò)誤對(duì)操作用戶來(lái)說(shuō)都是系統(tǒng)"運(yùn)行時(shí)"異常,都是這個(gè)應(yīng)用系統(tǒng)內(nèi)部的異常。這也是異常轉(zhuǎn)譯和應(yīng)用系統(tǒng)異常框架設(shè)計(jì)的指導(dǎo)原則。在系統(tǒng)中大量處理非檢查異常的負(fù)面影響很多,最重要的一個(gè)方面就是代碼可讀性降低,程序編寫(xiě)復(fù)雜,異常處理的代碼也很蒼白無(wú)力。因此,很有必要將這些檢查異常Exception和錯(cuò)誤Error轉(zhuǎn)換為RuntimeException異常,讓程序員根據(jù)情況來(lái)決定是否捕獲和處理所發(fā)生的異常。
圖中的三條線標(biāo)識(shí)轉(zhuǎn)換的方向,分三種情況:
①:Error到Exception:將錯(cuò)誤轉(zhuǎn)換為異常,并繼續(xù)拋出。例如Spring WEB框架中,將org.springframework.web.servlet.DispatcherServlet的doDispatch()方法中,將捕獲的錯(cuò)誤轉(zhuǎn)譯為一個(gè)NestedServletException異常。這樣做的目的是為了最大限度挽回因錯(cuò)誤發(fā)生帶來(lái)的負(fù)面影響。因?yàn)橐粋€(gè)Error常常是很嚴(yán)重的錯(cuò)誤,可能會(huì)引起系統(tǒng)掛起。
②:Exception到RuntimeException:將檢查異常轉(zhuǎn)換為RuntimeException可以讓程序代碼變得更優(yōu)雅,讓開(kāi)發(fā)人員集中經(jīng)理設(shè)計(jì)更合理的程序代碼,反過(guò)來(lái)也增加了系統(tǒng)發(fā)生異常的可能性。
③:Error到RuntimeException:目的還是一樣的。把所有的異常和錯(cuò)誤轉(zhuǎn)譯為不檢查異常,這樣可以讓代碼更為簡(jiǎn)潔,還有利于對(duì)錯(cuò)誤和異常信息的統(tǒng)一處理。
1、 異常鏈
異常鏈顧名思義就是將異常發(fā)生的原因一個(gè)傳一個(gè)串起來(lái),即把底層的異常信息傳給上層,這樣逐層拋出。Java API文檔中給出了一個(gè)簡(jiǎn)單的模型:
try {
lowLevelOp();
} catch (LowLevelException le) {
throw (HighLevelException)
new HighLevelException().initCause(le);
} |
當(dāng)程序捕獲到了一個(gè)底層異常le,在處理部分選擇了繼續(xù)拋出一個(gè)更高級(jí)別的新異常給此方法的調(diào)用者。這樣異常的原因就會(huì)逐層傳遞。這樣,位于高層的異常遞歸調(diào)用getCause()方法,就可以遍歷各層的異常原因。這就是Java異常鏈的原理。異常鏈的實(shí)際應(yīng)用很少,發(fā)生異常時(shí)候逐層上拋不是個(gè)好注意,上層拿到這些異常又能奈之何?而且異常逐層上拋會(huì)消耗大量資源,因?yàn)橐4嬉粋€(gè)完整的異常鏈信息
五、 設(shè)計(jì)一個(gè)高效合理的異常處理框架
對(duì)于一個(gè)應(yīng)用系統(tǒng)來(lái)說(shuō),發(fā)生所有異常在用戶看來(lái)都是應(yīng)用系統(tǒng)內(nèi)部的異常。因此應(yīng)該設(shè)計(jì)一套應(yīng)用系統(tǒng)的異常框架,以處理系統(tǒng)運(yùn)行過(guò)程中的所有異常。
基于這種觀點(diǎn),可以設(shè)計(jì)一個(gè)應(yīng)用系統(tǒng)的異常比如叫做AppException。并且對(duì)用戶來(lái)說(shuō),這些異常都是運(yùn)行應(yīng)用系統(tǒng)運(yùn)行時(shí)發(fā)生的,因此AppException應(yīng)該繼承RuntimeException,這樣系統(tǒng)中所有的其他異常都轉(zhuǎn)譯為AppException,當(dāng)異常發(fā)生的時(shí)候,前端接收到AppExcetpion并做統(tǒng)一的處理。畫(huà)出異常處理框架如圖 3 :

圖 3 一個(gè)應(yīng)用系統(tǒng)的異常處理框架
在這個(gè)設(shè)計(jì)圖中,AppRuntimeException是系統(tǒng)異常的基類,對(duì)外只拋出這個(gè)異常,這個(gè)異常可以由前端(客戶端)接收處理,當(dāng)異常發(fā)生時(shí),客戶端的相關(guān)組件捕獲并處理這些異常,將"友好"的信息展示給客戶。
在AppRuntimeException下層,有各種各樣的異常和錯(cuò)誤,最終都轉(zhuǎn)譯為AppRuntimeException,AppRuntimeException下面還可以設(shè)計(jì)一些別的子類異常,比如AppDAOException、OtherException等,這些都根據(jù)實(shí)際需要靈活處理。在往下就是如何將捕獲的原始異常比如SQLException、HibernateException轉(zhuǎn)換為更高級(jí)一點(diǎn)AppDAOException。
有關(guān)異常框架設(shè)計(jì)這方面公認(rèn)比較好的就是Spring,Spring中的所有異常都可以用org.springframework.core.NestedRuntimeException來(lái)表示,并且該基類繼承的是RuntimeException。Spring框架很龐大,因此設(shè)計(jì)了很多NestedRuntimeException的子類,還有異常轉(zhuǎn)換的工具,這些都是非常優(yōu)秀的設(shè)計(jì)思想。
六、 Java異常處理總結(jié)
回顧全文,總結(jié)一下Java異常處理的要點(diǎn):
1、 異常是程序運(yùn)行過(guò)程過(guò)程出現(xiàn)的錯(cuò)誤,在Java中用類來(lái)描述,用對(duì)象來(lái)表示具體的異常。Java將其區(qū)分為Error與Exception,Error是程序無(wú)力處理的錯(cuò)誤,Exception是程序可以處理的錯(cuò)誤。異常處理是為了程序的健壯性。
2、 Java異常類來(lái)自于Java API定義和用戶擴(kuò)展。通過(guò)繼承Java API異常類可以實(shí)現(xiàn)異常的轉(zhuǎn)譯。
3、 異常能處理就處理,不能處理就拋出,最終沒(méi)有處理的異常JVM會(huì)進(jìn)行處理。
4、 異常可以傳播,也可以相互轉(zhuǎn)譯,但應(yīng)該根據(jù)需要選擇合理的異常轉(zhuǎn)譯的方向。
5、 對(duì)于一個(gè)應(yīng)用系統(tǒng),設(shè)計(jì)一套良好的異常處理體系很重要。這一點(diǎn)在系統(tǒng)設(shè)計(jì)的時(shí)候就應(yīng)該考慮到。
轉(zhuǎn)自:http://forum.hibernate.org.cn
作者:robbin
Java Learning Path (一)、工具篇
一、 JDK (Java Development Kit)
JDK是整個(gè)Java的核心,包括了Java運(yùn)行環(huán)境(Java Runtime
Envirnment),一堆Java工具和Java基礎(chǔ)的類庫(kù)(rt.jar)。不論什么Java應(yīng)用服務(wù)器實(shí)質(zhì)都是內(nèi)置了某個(gè)版本的JDK。因此掌握
JDK是學(xué)好Java的第一步。最主流的JDK是Sun公司發(fā)布的JDK,除了Sun之外,還有很多公司和組織都開(kāi)發(fā)了自己的JDK,例如IBM公司開(kāi)發(fā)
的JDK,BEA公司的Jrocket,還有GNU組織開(kāi)發(fā)的JDK等等。其中IBM的JDK包含的JVM(Java Virtual
Machine)運(yùn)行效率要比Sun JDK包含的JVM高出許多。而專門運(yùn)行在x86平臺(tái)的Jrocket在服務(wù)端運(yùn)行效率也要比Sun
JDK好很多。但不管怎么說(shuō),我們還是需要先把Sun JDK掌握好。
1、 JDK的下載和安裝
JDK又叫做J2SE(Java2 SDK Standard
Edition),可以從Sun的Java網(wǎng)站上下載到,http://java.sun.com/j2se/downloads.html
,JDK當(dāng)前最新的版本是J2SDK1.4.2,建議下載該版本的JDK,下載頁(yè)面在這里:http://java.sun.com/j2se
/1.4.2/download.html。
下載好的JDK是一個(gè)可執(zhí)行安裝程序,默認(rèn)安裝完畢后會(huì)在C:\Program
Files\Java\目錄下安裝一套JRE(供瀏覽器來(lái)使用),在C:\j2sdk1.4.2下安裝一套JDK(也包括一套JRE)。然后我們需要在環(huán)
境變量PATH的最前面增加java的路徑C:\j2sdk1.4.2\bin。這樣JDK就安裝好了。
2、 JDK的命令工具
JDK的最重要命令行工具:
java: 啟動(dòng)JVM執(zhí)行class
javac: Java編譯器
jar: Java打包工具
javadoc: Java文檔生成器
這些命令行必須要非常非常熟悉,對(duì)于每個(gè)參數(shù)都要很精通才行。對(duì)于這些命令的學(xué)習(xí),JDK Documentation上有詳細(xì)的文檔。
二、 JDK Documentation
Documentation在JDK的下載頁(yè)面也有下載連接,建議同時(shí)下載Documentation。Documentation是最最重要的編程手
冊(cè),涵蓋了整個(gè)Java所有方面的內(nèi)容的描述。可以這樣說(shuō),學(xué)習(xí)Java編程,大部分時(shí)間都是花在看這個(gè)Documentation上面的。我是隨身攜帶
的,寫(xiě)Java代碼的時(shí)候,隨時(shí)查看,須臾不離手。
三、 應(yīng)用服務(wù)器(App Server)
App Server是運(yùn)行Java企業(yè)組件的平臺(tái),構(gòu)成了應(yīng)用軟件的主要運(yùn)行環(huán)境。當(dāng)前主流的App Server是BEA公司的Weblogic
Server和IBM公司的Websphere以及免費(fèi)的Jboss,選擇其中一個(gè)進(jìn)行學(xué)習(xí)就可以了,個(gè)人推薦Weblogic,因?yàn)樗捏w系結(jié)構(gòu)更加干
凈,開(kāi)發(fā)和部署更加方便,是Java企業(yè)軟件開(kāi)發(fā)人員首選的開(kāi)發(fā)平臺(tái)。下面簡(jiǎn)要介紹幾種常用的App Server:
1、 Tomcat
Tomcat嚴(yán)格意義上并不是一個(gè)真正的App
Server,它只是一個(gè)可以支持運(yùn)行Serlvet/JSP的Web容器,不過(guò)Tomcat也擴(kuò)展了一些App
Server的功能,如JNDI,數(shù)據(jù)庫(kù)連接池,用戶事務(wù)處理等等。Tomcat被非常廣泛的應(yīng)用在中小規(guī)模的Java
Web應(yīng)用中,因此本文做一點(diǎn)下載、安裝和配置Tomcat的介紹:
Tomcat是Apache組織下Jakarta項(xiàng)目下的一個(gè)子項(xiàng)目,它的主網(wǎng)站是:http://jakarta.apache.org/tomcat
/
,Tomcat最新版本是Tomcat4.1.27,軟件下載的連接是:http://www.apache.org/dist/jakarta
/tomcat-4/binaries/ 。
下載Tomcat既可以直接下載zip包,也可以下載exe安裝包(個(gè)人建議zip更干凈些),不管哪種情況,下載完畢安裝好以后(zip直接解壓縮就可以了)。需要設(shè)置兩個(gè)環(huán)境變量:
JAVA_HOME=C:\j2sdk1.4.2
CATALINA_HOME=D:\tomcat4 (你的Tomcat安裝目錄)
這樣就安裝好了,啟動(dòng)Tomcat運(yùn)行CATALINA_HOME\bin\startup.bat,關(guān)閉Tomcat運(yùn)行shutdown.bat腳
本。Tomcat啟動(dòng)以后,默認(rèn)使用8080端口,因此可以用瀏覽器訪問(wèn)http://localhost:8080來(lái)測(cè)試Tomcat是否正常啟動(dòng)。
Tomcat提供了兩個(gè)Web界面的管理工具,URL分別是:
http://localhost:8080/admin/index.jsp
http://localhost:8080/manager/html
在啟用這兩個(gè)管理工具之前,先需要手工配置一下管理員用戶和口令。用一個(gè)文本工具打開(kāi)CATALINA_HOME\conf\tomcat-users.xml這個(gè)文件,加入如下幾行:
<role rolename="manager"/>;
<role rolename="admin"/>;
<user username="robbin" password="12345678" roles="admin,manager,tomcat"/>;
這樣用戶“robbin”就具備了超級(jí)管理員權(quán)限。重新啟動(dòng)Tomcat以后,你就可以使用該用戶來(lái)登陸如上的兩個(gè)管理工具,通過(guò)Web方式進(jìn)行Tomcat的配置和管理了。
2、 BEA Weblogic
Weblogic可以到BEA的網(wǎng)站上免費(fèi)注冊(cè)之后下載到最新的Weblogic8.1企業(yè)版,License可以免費(fèi)使用1年時(shí)間,其實(shí)這已經(jīng)完全足夠
了。Weblogic的下載連接:http://commerce.bea.com/index.jsp,Weblogic的在線文檔:http:
//edocs.bea.com/ 。
3、 IBM Webshpere
Websphere同樣可以下載到免費(fèi)的試用版本,到IBM的developerWorks網(wǎng)站可以看到Websphere試用產(chǎn)品的下載和相關(guān)的
Websphere的資料,developerWorks中文網(wǎng)站的連接是:http://www-900.ibm.com
/developerWorks/cn/wsdd/
,Websphere的下載連接:http://www7b.software.ibm.com/wsdd/downloads
/WASsupport.html 。
4、 Jboss
Jboss是免費(fèi)開(kāi)源的App
Server,可以免費(fèi)的從Jboss網(wǎng)站下載:http://www.jboss.org/index.html,然而Jboss的文檔是不免費(fèi),需要
花錢購(gòu)買,所以為我們學(xué)習(xí)Jboss設(shè)置了一定的障礙。在Jdon上有幾篇不錯(cuò)的Jboss配置文檔,可以用來(lái)參
考:http://www.jdon.com/idea.html
四、 Java應(yīng)用的運(yùn)行環(huán)境
Java的應(yīng)用可以簡(jiǎn)單分為以下幾個(gè)方面:
1、 Java的桌面應(yīng)用
桌面應(yīng)用一般僅僅需要JRE的支持就足夠了。
2、 Java Web應(yīng)用
Java的Web應(yīng)用至少需要安裝JDK和一個(gè)web容器(例如Tomcat),以及一個(gè)多用戶數(shù)據(jù)庫(kù),Web應(yīng)用至少分為三層:
Browser層:瀏覽器顯示用戶頁(yè)面
Web層:運(yùn)行Servlet/JSP
DB層:后端數(shù)據(jù)庫(kù),向Java程序提供數(shù)據(jù)訪問(wèn)服務(wù)
3、 Java企業(yè)級(jí)應(yīng)用
企業(yè)級(jí)應(yīng)用比較復(fù)雜,可以擴(kuò)展到n層,最簡(jiǎn)單情況會(huì)分為4層:
Browser層:瀏覽器顯示用戶頁(yè)面
Client層:Java客戶端圖形程序(或者嵌入式設(shè)備的程序)直接和Web層或者EJB層交互
Web層:運(yùn)行Servlet/JSP
EJB層:運(yùn)行EJB,完成業(yè)務(wù)邏輯運(yùn)算
DB層:后端數(shù)據(jù)庫(kù),向Java程序提供數(shù)據(jù)訪問(wèn)服務(wù)
4、 Java嵌入式應(yīng)用
Java嵌入式應(yīng)用是一個(gè)方興未艾的領(lǐng)域,從事嵌入式開(kāi)發(fā),需要從Sun下載J2ME開(kāi)發(fā)包,J2ME包含了嵌入式設(shè)備專用虛擬機(jī)KVM,和普通的JDK中包含的JVM有所不同。另外還需要到特定的嵌入式廠商那里下載模擬器。
Java Learning Path(二)、書(shū)籍篇
學(xué)習(xí)一門新的知識(shí),不可能指望只看一本,或者兩本書(shū)就能夠完全掌握。需要有一個(gè)循序漸進(jìn)的閱讀過(guò)程。我推薦Oreilly出版的Java系列書(shū)籍。
在這里我只想補(bǔ)充一點(diǎn)看法,很多人學(xué)習(xí)Java是從《Thinking in
Java》這本書(shū)入手的,但是我認(rèn)為這本書(shū)是不適合初學(xué)者的。我認(rèn)為正確的使用這本書(shū)的方法應(yīng)該是作為輔助的讀物。《Thinking in
Java》并不是在完整的介紹Java的整個(gè)體系,而是一種跳躍式的寫(xiě)作方法,是一種類似tips的方法來(lái)對(duì)Java很多知識(shí)點(diǎn)進(jìn)行了深入的分析和解釋。
對(duì)于初學(xué)者來(lái)說(shuō),最好是找一本Java入門的書(shū)籍,但是比較完整的循序的介紹Java的語(yǔ)法,面向?qū)ο蟮奶匦裕诵念悗?kù)等等,在看這本書(shū)的同時(shí),
可以同步來(lái)看《Thinking in Java》,來(lái)加深對(duì)Java的理解和原理的運(yùn)用,同時(shí)又可以完整的了解Java的整個(gè)體系。
對(duì)于Java的入門書(shū)籍,蔡學(xué)鏞推薦的是Oreilly的《Exploring Java, 2nd Edition》 或者《Java in a
Nutshell,2nd Edition(針對(duì)C++背景)》,我并沒(méi)有看過(guò)這兩本書(shū)。其實(shí)我覺(jué)得電子工業(yè)出版社的《Java
2編程詳解》或者《Java 2從入門到精通》就很不錯(cuò)。
在所有的Java書(shū)籍當(dāng)中,其實(shí)最最有用的,并不是O'reilly的 Java
Serials,真正最最有用處是JDK的Documentation!幾乎你想獲得的所有的知識(shí)在Documentation里面全部都有,其中最主要
的部分當(dāng)然是Java基礎(chǔ)類庫(kù)的API文檔,是按照package來(lái)組織的,對(duì)于每一個(gè)class都有詳細(xì)的解釋,它的繼承關(guān)系,是否實(shí)現(xiàn)了某個(gè)接口,通
常用在哪些場(chǎng)合,還可以查到它所有的public的屬性和方法,每個(gè)屬性的解釋,意義,每個(gè)方法的用途,調(diào)用的參數(shù),參數(shù)的意義,返回值的類型,以及方法
可能拋出的異常等等。可以這樣來(lái)說(shuō),所有關(guān)于Java編程方面的書(shū)籍其實(shí)都不過(guò)是在用比較通俗易懂的語(yǔ)言,和良好的組織方式來(lái)介紹
Documentation里面的某個(gè)package里面包含的一些類的用法而已。所以萬(wàn)變不離其宗,如果你有足夠的能力來(lái)直接通過(guò)
Documentation來(lái)學(xué)習(xí)Java的類庫(kù),那么基本上就不需要看其他的書(shū)籍了。除此之外,Documentation也是編程必備的手冊(cè),我的桌
面上有三個(gè)Documentation的快捷方式,分別是J2SDK1.4.1的Documentation,Servlet2.3的
Documentation和J2SDKEE1.3.1的Documentation。有了這個(gè)三個(gè)Documentation,什么其他的書(shū)籍都不需要
了。
對(duì)于Java Web
編程來(lái)說(shuō),最核心的是要熟悉和掌握HTTP協(xié)議,這個(gè)就和Java無(wú)關(guān)了,在熟悉HTTP協(xié)議之后,就需要熟悉Java的實(shí)現(xiàn)HTTP協(xié)議的類庫(kù),也就是
Servlet API,所以最重要的東西就是Servlet API。當(dāng)然對(duì)于初學(xué)者而言,直接通過(guò)Servlet
API來(lái)學(xué)習(xí)Web編程有很大的難度,我推薦O'reilly的《Java Server Pages 》這本書(shū)來(lái)學(xué)習(xí)Web 編程。
EJB的書(shū)籍當(dāng)中,《Enterprise JavaBeans, 2nd Edition》是一本很不錯(cuò)的書(shū),
EJB的學(xué)習(xí)門檻是比較高,入門很難,但是這本書(shū)完全降低了學(xué)習(xí)的難度,特別重要的一點(diǎn)是,EJB的學(xué)習(xí)需要結(jié)合一種App
Server的具體實(shí)現(xiàn),所以在學(xué)習(xí)EJB的同時(shí),必須同步的學(xué)習(xí)某種App
Server,而這本書(shū)相關(guān)的出了三本書(shū),分別是Weblogic6.1,Websphere4.0和JBoss3.0上面部署書(shū)中例子的實(shí)做。真是既有
理論,又有實(shí)踐。在學(xué)習(xí)EJB的同時(shí),可以邊看邊做,EJB的學(xué)習(xí)會(huì)變得很輕松。
但是這本書(shū)也有一個(gè)問(wèn)題,就是版本比較舊,主要講EJB1.1規(guī)范和部分EJB2.0的規(guī)范。而Ed Roman寫(xiě)的《Mastering EJB
2.0》這本書(shū)完全是根據(jù)EJB2.0規(guī)范寫(xiě)的,深入淺出,覆蓋了EJB編程的各個(gè)方面,并且還有很多編程經(jīng)驗(yàn)tips,也是學(xué)習(xí)EJB非常推薦的書(shū)籍之
一。
如果是結(jié)合Weblogic來(lái)學(xué)習(xí)J2EE的話,《J2EE應(yīng)用與BEA Weblogic
Server》絕對(duì)是首選讀物,雖然是講述的Weblogic6.0,仍然值得購(gòu)買,這本書(shū)是BEA官方推薦的教材,作者也是BEA公司的工程師。現(xiàn)在中
文版已經(jīng)隨處可見(jiàn)了。這本書(shū)結(jié)合Weblogic介紹了J2EE各個(gè)方面的技術(shù)在Weblogic平臺(tái)上的開(kāi)發(fā)和部署,實(shí)踐指導(dǎo)意義非常強(qiáng)。
在掌握了Java平臺(tái)基礎(chǔ)知識(shí)和J2EE方面的知識(shí)以后,更進(jìn)一步的是學(xué)習(xí)如何運(yùn)用OO的方法進(jìn)行軟件的設(shè)計(jì),那么就一定要學(xué)習(xí)“設(shè)計(jì)模式”。Sun公司
出版了一本《J2EE核心模式》,是每個(gè)開(kāi)發(fā)Java企業(yè)平臺(tái)軟件的架構(gòu)師必備的書(shū)籍。這本書(shū)全面的介紹了J2EE體系架構(gòu)的各種設(shè)計(jì)模式,是設(shè)計(jì)師的必
讀書(shū)籍。
Java Learning Path(三)過(guò)程篇
每個(gè)人的學(xué)習(xí)方法是不同的,一個(gè)人的方法不見(jiàn)得適合另一個(gè)人,我只能是談自己的學(xué)習(xí)方法。因?yàn)槲覍W(xué)習(xí)Java是完全自學(xué)的,從來(lái)沒(méi)有問(wèn)過(guò)別人,所以學(xué)習(xí)的過(guò)程基本上完全是自己摸索出來(lái)的。我也不知道這種方法是否是比較好的方法,只能給大家提供一點(diǎn)參考了。
學(xué)習(xí)Java的第一步是安裝好JDK,寫(xiě)一個(gè)Hello World,?
其實(shí)JDK的學(xué)習(xí)沒(méi)有那么簡(jiǎn)單,關(guān)于JDK有兩個(gè)問(wèn)題是很容易一直困擾Java程序員的地方:一個(gè)是CLASSPATH的問(wèn)題,其實(shí)從原理上來(lái)說(shuō),是要搞
清楚JRE的ClassLoader是如何加載Class的;另一個(gè)問(wèn)題是package和import問(wèn)題,如何來(lái)尋找類的路徑問(wèn)題。把這兩個(gè)問(wèn)題摸索
清楚了,就掃除了學(xué)習(xí)Java和使用JDK的最大障礙。推薦看一下王森的《Java深度歷險(xiǎn)》,對(duì)這兩個(gè)問(wèn)題進(jìn)行了深入的探討。
第二步是學(xué)習(xí)Java的語(yǔ)法。Java的語(yǔ)法是類C++的,基本上主流的編程語(yǔ)言不是類C,就是類C++的,沒(méi)有什么新東西,所以語(yǔ)法的學(xué)習(xí),大概就是半
天的時(shí)間足夠了。唯一需要注意的是有幾個(gè)不容易搞清楚的關(guān)鍵字的用法,public,protected,private,static,什么時(shí)候用,為
什么要用,怎么用,這可能需要有人來(lái)指點(diǎn)一下,我當(dāng)初是完全自己琢磨出來(lái)的,花了很久的時(shí)間。不過(guò)后來(lái)我看到《Thinking in
Java》這本書(shū)上面是講了這些概念的。
第三步是學(xué)習(xí)Java的面向?qū)ο蟮木幊陶Z(yǔ)言的特性的地方。比如繼承,構(gòu)造器,抽象類,接口,方法的多態(tài),重載,覆蓋,Java的異常處理機(jī)制。對(duì)于一個(gè)沒(méi)
有面向?qū)ο笳Z(yǔ)言背景的人來(lái)說(shuō),我覺(jué)得這個(gè)過(guò)程需要花很長(zhǎng)很長(zhǎng)時(shí)間,因?yàn)閷W(xué)習(xí)Java之前沒(méi)有C++的經(jīng)驗(yàn),只有C的經(jīng)驗(yàn),我是大概花了一個(gè)月左右吧,才徹
底把這些概念都搞清楚,把書(shū)上面的例子反復(fù)的揣摩,修改,嘗試,把那幾章內(nèi)容反復(fù)的看過(guò)來(lái),看過(guò)去,看了不下5遍,才徹底領(lǐng)悟了。不過(guò)我想如果有C++經(jīng)
驗(yàn)的話,應(yīng)該一兩天時(shí)間足夠了。那么在這個(gè)過(guò)程中,可以多看看《Thinking in
Java》這本書(shū),對(duì)面向?qū)ο蟮闹v解非常透徹。可惜的是我學(xué)習(xí)的時(shí)候,并沒(méi)有看到這本書(shū),所以自己花了大量的時(shí)間,通過(guò)自己的嘗試和揣摩來(lái)學(xué)會(huì)的。
第四步就是開(kāi)始熟悉Java的類庫(kù)。Java的基礎(chǔ)類庫(kù)其實(shí)就是JDK安裝目錄下面jre\lib\rt.jar這個(gè)包。學(xué)習(xí)基礎(chǔ)類庫(kù)就是學(xué)習(xí)rt.jar。基礎(chǔ)類庫(kù)里面的類非常非常多。據(jù)說(shuō)有3000多個(gè),我沒(méi)有統(tǒng)計(jì)過(guò)。但是真正對(duì)于我們來(lái)說(shuō)最核心的只有4個(gè),分別是
java.lang.*;
java.io.*;
java.util.*;
java.sql.*;
這四個(gè)包的學(xué)習(xí),每個(gè)包的學(xué)習(xí)都可以寫(xiě)成一本厚厚的教材,而O'reilly也確實(shí)是這樣做的。我覺(jué)得如果時(shí)間比較緊,是不可能通過(guò)讀四本書(shū)來(lái)學(xué)習(xí)。我覺(jué)得比較好的學(xué)習(xí)方法是這樣的:
首先要通讀整個(gè)package的框架,了解整個(gè)package的class,interface,exception的構(gòu)成,最好是能夠找到介紹整個(gè)包框架的文章。這些專門介紹包的書(shū)籍的前幾章應(yīng)該就是這些總體的框架內(nèi)容介紹。
對(duì)包整體框架的把握并不是要熟悉每個(gè)類的用法,記住它有哪些屬性,方法。想記也記不住的。而是要知道包有哪些方面的類構(gòu)成的,這些類的用途是什么,最核心
的幾個(gè)類分別是完成什么功能的。我在給人培訓(xùn)的時(shí)候一般是一次課講一個(gè)包,所以不可能詳細(xì)的介紹每個(gè)類的用法,但是我反復(fù)強(qiáng)調(diào),我給你們講這些包的不是要
告訴你們類的方法是怎么調(diào)用的,也不要求你們記住類的方法調(diào)用,而是要你們了解,Java給我們提供了哪些類,每個(gè)類是用在什么場(chǎng)合,當(dāng)我遇到問(wèn)題的時(shí)
候,我知道哪個(gè)類,或者哪幾個(gè)類的組合可以解決我的問(wèn)題,That'all!,當(dāng)我們具體寫(xiě)程序的時(shí)候,只要你知道該用哪個(gè)類來(lái)完成你的工作就足夠了。編
碼的時(shí)候,具體的方法調(diào)用,是邊寫(xiě)代碼,邊查Documentation,所有的東西都在Documentation里面,不要求你一定記住,實(shí)際你也記
不住3000多個(gè)類的總共將近10萬(wàn)個(gè)方法調(diào)用。所以對(duì)每個(gè)包的總體框架的把握就變得極為重要。
第五步,通過(guò)上面的學(xué)習(xí),如果學(xué)的比較扎實(shí)的話,就打好了Java的基礎(chǔ)了,剩下要做的工作是掃清Documentation里面除了上面4個(gè)包之外的其
他一些比較有用處的類。相信進(jìn)展到這一步,Java的自學(xué)能力已經(jīng)被培養(yǎng)出來(lái)了,可以到了直接學(xué)習(xí)Documentation的水平了。除了要做GUI編
程之外,JDK里面其他會(huì)有用處的包是這些:
java.text.*;
java.net.*;
javax.naming.*;
這些包里面真正用的比較多的類其實(shí)很少,只有幾個(gè),所以不需要花很多時(shí)間。
第六步,Java Web 編程
Web編程的核心是HTTP協(xié)議,HTTP協(xié)議和Java無(wú)關(guān),如果不熟悉HTTP協(xié)議的話,雖然也可以學(xué)好Servlet/JSP編程,但是達(dá)不到舉一
反三,一通百通的境界。所以HTTP協(xié)議的學(xué)習(xí)是必備的。如果熟悉了HTTP協(xié)議的話,又有了Java編程的良好的基礎(chǔ),學(xué)習(xí)Servlet/JSP簡(jiǎn)直
易如反掌,我學(xué)習(xí)Servlet/JSP就用了不到一周的時(shí)間,然后就開(kāi)始用JSP來(lái)做項(xiàng)目了。
在Servlet/JSP的學(xué)習(xí)中,重頭仍然是Servlet Documentation。Servlet
API最常用的類很少,花比較少的時(shí)間就可以掌握了。把這些類都看一遍,多寫(xiě)幾個(gè)例子試試。Servlet/JSP編程本質(zhì)就是在反復(fù)調(diào)用這些類來(lái)通過(guò)
HTTP協(xié)議在Web Server和Brower之間交談。另外對(duì)JSP,還需要熟悉幾個(gè)常用JSP的標(biāo)記,具體的寫(xiě)法記不住的話,臨時(shí)查就是了。
此外Java Web編程學(xué)習(xí)的重點(diǎn)要放在Web
Application的設(shè)計(jì)模式上,如何進(jìn)行業(yè)務(wù)邏輯的分析,并且進(jìn)行合理的設(shè)計(jì),按照MVC設(shè)計(jì)模式的要求,運(yùn)用Servlet和JSP分別完成不同
的邏輯層,掌握如何在Servlet和JSP之間進(jìn)行流程的控制和數(shù)據(jù)的共享,以及Web Application應(yīng)該如何配置和部署。
第七步,J2EE編程
以上的學(xué)習(xí)過(guò)程如果是比較順利的話,進(jìn)行到這一步,難度又陡然提高。因?yàn)樯厦娴闹R(shí)內(nèi)容都是只涉及一個(gè)方面,而像EJB,JMS,JTA等核心的J2EE規(guī)范往往是幾種Java技術(shù)的綜合運(yùn)用的結(jié)晶,所以掌握起來(lái)難度比較大。
首先一定要學(xué)習(xí)好JNDI,JNDI是App
Server定位服務(wù)器資源(EJB組件,Datasouce,JMS)查找方法,如果對(duì)JNDI不熟悉的話,EJB,JMS這些東西幾乎學(xué)不下去。
JNDI其實(shí)就是javax.naming.*這個(gè)包,運(yùn)用起來(lái)很簡(jiǎn)單。難點(diǎn)在于服務(wù)器資源文件的配置。對(duì)于服務(wù)器資源文件的配置,就需要看看專門的文檔
規(guī)范了,比如web.xml的寫(xiě)法,ejb-jar.xml的寫(xiě)法等等。針對(duì)每種不同的App
Server,還有自己的服務(wù)資源配置文件,也是需要熟悉的。
然后可以學(xué)習(xí)JTA,主要是要理解JTA對(duì)于事務(wù)的控制的方法,以及該在什么場(chǎng)合使用JTA。這里可以簡(jiǎn)單的舉個(gè)例子,我們知道一般情況可以對(duì)于一個(gè)數(shù)據(jù)
庫(kù)連接進(jìn)行事務(wù)控制(conn.setAutoCommit(false),....,conn.commit()),做為一個(gè)原子操作,但是假設(shè)我的業(yè)
務(wù)需求是要把對(duì)兩個(gè)不同數(shù)據(jù)庫(kù)的操作做為一個(gè)原子操作,你能做的到嗎?這時(shí)候只能用JTA了。假設(shè)操作過(guò)程是先往A數(shù)據(jù)庫(kù)插一條記錄,然后刪除B數(shù)據(jù)庫(kù)另
一個(gè)記錄,我們自己寫(xiě)代碼是控制不了把整個(gè)操作做為一個(gè)原子操作的。用JTA的話,由App Server來(lái)完成控制。
在學(xué)習(xí)EJB之前要學(xué)習(xí)對(duì)象序列化和RMI,RMI是EJB的基礎(chǔ)。接著學(xué)習(xí)JMS和EJB,對(duì)于EJB來(lái)說(shuō),最關(guān)鍵是要理解EJB是如何通過(guò)RMI來(lái)實(shí)現(xiàn)對(duì)遠(yuǎn)端對(duì)象的調(diào)用的,以及在什么情況下要用到EJB。
在學(xué)習(xí)完EJB,JMS這些東西之后,你可能會(huì)意識(shí)到要急不可待學(xué)習(xí)兩個(gè)領(lǐng)域的知識(shí),一個(gè)是UML,另一個(gè)是Design
Pattern。Java企業(yè)軟件的設(shè)計(jì)非常重視框架(Framework)的設(shè)計(jì),一個(gè)好的軟件框架是軟件開(kāi)發(fā)成功的必要條件。在這個(gè)時(shí)候,應(yīng)該開(kāi)始把
學(xué)習(xí)的重點(diǎn)放在設(shè)計(jì)模式和框架的學(xué)習(xí)上,通過(guò)學(xué)習(xí)和實(shí)際的編程經(jīng)驗(yàn)來(lái)掌握EJB的設(shè)計(jì)模式和J2EE的核心模式。
J2EE規(guī)范里面,除了EJB,JMS,JTA,Servlet/JSP,JDBC之外還有很多很多的企業(yè)技術(shù),這里不一一進(jìn)行介紹了。
另外還有一個(gè)最新領(lǐng)域Web Services。Web
Services也完全沒(méi)有任何新東西,它像是一種黏合劑,可以把不同的服務(wù)統(tǒng)一起來(lái)提供一個(gè)統(tǒng)一的調(diào)用接口,作為使用者來(lái)說(shuō),我只要獲得服務(wù)提供者給我
的WSDL(對(duì)服務(wù)的描述),就夠了,我完全不知道服務(wù)器提供者提供的服務(wù)究竟是EJB組件,還是.Net組件,還是什么CORBA組件,還是其他的什么
實(shí)現(xiàn),我也不需要知道。Web
Services最偉大的地方就在于通過(guò)統(tǒng)一的服務(wù)提供方式和調(diào)用方式,實(shí)現(xiàn)了整個(gè)Internet服務(wù)的共享,是一個(gè)非常令人激動(dòng)的技術(shù)領(lǐng)域。Web
Services好像目前還沒(méi)有什么很好的書(shū)籍,但是可以通過(guò)在網(wǎng)絡(luò)上面查資料的方式來(lái)學(xué)習(xí)。
Java Learning Path(四) 方法篇
Java作為一門編程語(yǔ)言,最好的學(xué)習(xí)方法就是寫(xiě)代碼。當(dāng)你學(xué)習(xí)一個(gè)類以后,你就可以自己寫(xiě)個(gè)簡(jiǎn)單的例子程序來(lái)運(yùn)行一下,看看有什么結(jié)果,然后再多調(diào)用幾
個(gè)類的方法,看看運(yùn)行結(jié)果,這樣非常直觀的把類給學(xué)會(huì)了,而且記憶非常深刻。然后不應(yīng)該滿足把代碼調(diào)通,你應(yīng)該想想看如果我不這樣寫(xiě),換個(gè)方式,再試試行
不行。記得哪個(gè)高人說(shuō)過(guò)學(xué)習(xí)編程就是個(gè)破壞的過(guò)程,把書(shū)上的例子,自己學(xué)習(xí)Documentation編寫(xiě)的例子在運(yùn)行通過(guò)以后,不斷的嘗試著用不同的方
法實(shí)現(xiàn),不斷的嘗試破壞代碼的結(jié)構(gòu),看看它會(huì)有什么結(jié)果。通過(guò)這樣的方式,你會(huì)很徹底的很精通的掌握J(rèn)ava。
舉個(gè)例子,我們都編過(guò)Hello World
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World"

;
}
}
很多初學(xué)者不是很理解為什么main方法一定要這樣來(lái)定義public static void main(String[]
args),能不能不這樣寫(xiě)?包括我剛學(xué)習(xí)Java的時(shí)候也有這樣的疑問(wèn)。想知道答案嗎?很簡(jiǎn)單,你把main改個(gè)名字運(yùn)行一下,看看報(bào)什么錯(cuò)誤,然后根
據(jù)出錯(cuò)信息進(jìn)行分析;把main的public取掉,在試試看,報(bào)什么錯(cuò)誤;static去掉還能不能運(yùn)行;不知道m(xù)ain方法是否一定要傳一個(gè)
String[]數(shù)組的,把String[]改掉,改成int[],或者String試試看;不知道是否必須寫(xiě)args參數(shù)名稱的,也可以把a(bǔ)rgs改成
別的名字,看看運(yùn)行結(jié)果如何。
我當(dāng)初學(xué)習(xí)Java的時(shí)候就是這樣做的,把Hello World程序反復(fù)改了七八次,不斷運(yùn)行,分析運(yùn)行結(jié)果,最后就徹底明白為什么了main方法是這樣定義的了。
此外,我對(duì)于staic,public,private,Exception,try{ }catch
{}finally{}等等等等一開(kāi)始都不是很懂,都是把參考書(shū)上面的例子運(yùn)行成功,然后就開(kāi)始破壞它,不斷的根據(jù)自己心里面的疑問(wèn)來(lái)重新改寫(xiě)程序,看看
能不能運(yùn)行,運(yùn)行出來(lái)是個(gè)什么樣子,是否可以得到預(yù)期的結(jié)果。這樣雖然比較費(fèi)時(shí)間,不過(guò)一個(gè)例子程序這樣反復(fù)破壞幾次之后。我就對(duì)這個(gè)相關(guān)的知識(shí)徹底學(xué)通
了。有時(shí)候甚至故意寫(xiě)一些錯(cuò)誤的代碼來(lái)運(yùn)行,看看能否得到預(yù)期的運(yùn)行錯(cuò)誤。這樣對(duì)于編程的掌握是及其深刻的。
其中特別值得一提的是JDK有一個(gè)非常棒的調(diào)試功能,-verbose
java –verbose
javac –verbose 以及其它很多JDK工具都有這個(gè)選項(xiàng)
-verbose 可以顯示在命令執(zhí)行的過(guò)程中,JVM都依次加載哪里Class,通過(guò)這些寶貴的調(diào)試信息,可以幫助我們分析出JVM在執(zhí)行的過(guò)程中都干了些什么。
另外,自己在學(xué)習(xí)過(guò)程中,寫(xiě)的很多的這種破壞例程,應(yīng)該有意識(shí)的分門別類的保存下來(lái),在工作中積累的典型例程也應(yīng)該定期整理,日積月累,自己就有了一個(gè)代
碼庫(kù)了。遇到類似的問(wèn)題,到代碼庫(kù)里面 Copy & Paste ,Search &
Replace,就好了,極大提高了開(kāi)發(fā)速度。最理想的情況是把一些通用的例程自己再抽象一層,形成一個(gè)通用的類庫(kù),封裝好。那么可復(fù)用性就更強(qiáng)了。
所以我覺(jué)得其實(shí)不是特別需要例程的,自己寫(xiě)的破壞例程就是最好的例子,如果你實(shí)在對(duì)自己寫(xiě)的代碼不放心的話,我強(qiáng)烈推薦你看看JDK基礎(chǔ)類庫(kù)的Java源
代碼。在JDK安裝目錄下面會(huì)有一個(gè)src.zip,解開(kāi)來(lái)就可以完整的看到整個(gè)JDK基礎(chǔ)類庫(kù),也就是rt.jar的Java源代碼,你可以參考一下
Sun是怎么寫(xiě)Java程序的,規(guī)范是什么樣子的。我自己在學(xué)習(xí)Java的類庫(kù)的時(shí)候,當(dāng)有些地方理解的不是很清楚的時(shí)候,或者想更加清晰的理解運(yùn)作的細(xì)
節(jié)的時(shí)候,往往會(huì)打開(kāi)相應(yīng)的類的源代碼,通過(guò)看源代碼,所有的問(wèn)題都會(huì)一掃而空。
Java Learning Path(五)資源篇
1、 http://java.sun.com/ (英文)
Sun的Java網(wǎng)站,是一個(gè)應(yīng)該經(jīng)常去看的地方。不用多說(shuō)。
2、http://www-900.ibm.com/developerWorks/cn/
IBM的developerWorks網(wǎng)站,英語(yǔ)好的直接去英文主站點(diǎn)看。這里不但是一個(gè)極好的面向?qū)ο蟮姆治鲈O(shè)計(jì)網(wǎng)站,也是Web Services,Java,Linux極好的網(wǎng)站。強(qiáng)烈推薦!!!
3、http://www.javaworld.com/ (英文)
關(guān)于Java很多新技術(shù)的討論和新聞。想多了解Java的方方面面的應(yīng)用,這里比較好。
4、http://dev2dev.bea.com.cn/index.jsp
BEA的開(kāi)發(fā)者園地,BEA作為最重要的App Server廠商,有很多獨(dú)到的技術(shù),在Weblogic上做開(kāi)發(fā)的朋友不容錯(cuò)過(guò)。
5、http://www.huihoo.com/
灰狐動(dòng)力網(wǎng)站,一個(gè)專業(yè)的中間件網(wǎng)站,雖然不是專業(yè)的Java網(wǎng)站,但是在J2EE企業(yè)應(yīng)用技術(shù)方面有深厚的造詣。
6、http://www.theserverside.com/home/ (英文)
TheServerSide是一個(gè)著名的專門面向Java Server端應(yīng)用的網(wǎng)站。
7、http://www.javaresearch.org/
Java研究組織,有很多優(yōu)秀的Java方面的文章和教程,特別是在JDO方面的文章比較豐富。
8、http://www.cnjsp.org/
JSP技術(shù)網(wǎng)站,有相當(dāng)多的Java方面的文章和資源。
9、http://www.jdon.com/
Jdon論壇,是一個(gè)個(gè)人性質(zhì)的中文J2EE專業(yè)技術(shù)論壇,在眾多的Java的中文論壇中,Jdon一個(gè)是技術(shù)含量非常高,帖子質(zhì)量非常好的論壇。
10、http://sourceforge.net/
SourgeForge是一個(gè)開(kāi)放源代碼軟件的大本營(yíng),其中也有非常非常豐富的Java的開(kāi)放源代碼的著名的軟件。
_________________
Java編程技術(shù)中漢字問(wèn)題的分析及解決
在基于
Java 語(yǔ)言的編程中,我們經(jīng)常碰到
漢字的
處理及顯示的問(wèn)題。一大堆看不懂的亂碼肯定不是我們?cè)敢饪吹降娘@示效果,怎樣才能夠讓那些漢字正確顯示呢?Java語(yǔ)言默認(rèn)的編碼方式是UNICODE,
而我們中國(guó)人通常使用的文件和數(shù)據(jù)庫(kù)都是基于GB2312或者BIG5等方式編碼的,怎樣才能夠恰當(dāng)?shù)剡x擇漢字編碼方式并正確地處理漢字的編碼呢?本文將
從漢字編碼的常識(shí)入手,結(jié)合Java編程實(shí)例,分析以上兩個(gè)問(wèn)題并提出解決它們的方案。
現(xiàn)在 Java
編程語(yǔ)言已經(jīng)廣泛應(yīng)用于互聯(lián)網(wǎng)世界,早在 Sun 公司開(kāi)發(fā) Java 語(yǔ)言的時(shí)候,就已經(jīng)考慮到對(duì)非英文字符的支持了。Sun 公司公布的 Java
運(yùn)行環(huán)境(JRE)本身就分英文版和國(guó)際版,但只有國(guó)際版才支持非英文字符。不過(guò)在 Java 編程語(yǔ)言的應(yīng)用中,對(duì)中文字符的支持并非如同 Java
Soft
的標(biāo)準(zhǔn)規(guī)范中所宣稱的那樣完美,因?yàn)橹形淖址恢灰粋€(gè),而且不同的操作系統(tǒng)對(duì)中文字符的支持也不盡相同,所以會(huì)有許多和漢字編碼處理有關(guān)的問(wèn)題在我們進(jìn)
行應(yīng)用開(kāi)發(fā)中困擾著我們。有很多關(guān)于這些問(wèn)題的解答,但都比較瑣碎,并不能夠滿足大家迫切解決問(wèn)題的愿望,關(guān)于 Java
中文問(wèn)題的系統(tǒng)研究并不多,本文從漢字編碼常識(shí)出發(fā),分析 Java 中文問(wèn)題,希望對(duì)大家解決這個(gè)問(wèn)題有所幫助。
漢字編碼的常識(shí)
我們知道,英文字符一般是以一個(gè)字節(jié)來(lái)表示的,最常用的編碼方法是 ASCII
。但一個(gè)字節(jié)最多只能區(qū)分256個(gè)字符,而漢字成千上萬(wàn),所以現(xiàn)在都以雙字節(jié)來(lái)表示漢字,為了能夠與英文字符分開(kāi),每個(gè)字節(jié)的最高位一定為1,這樣雙字節(jié)
最多可以表示64K格字符。我們經(jīng)常碰到的編碼方式有 GB2312、BIG5、UNICODE
等。關(guān)于具體編碼方式的詳細(xì)資料,有興趣的讀者可以查閱相關(guān)資料。我膚淺談一下和我們關(guān)系密切的 GB2312 和 UNICODE。GB2312
碼,中華人民共和國(guó)國(guó)家標(biāo)準(zhǔn)漢字信息交換用編碼,是一個(gè)由中華人民共和國(guó)國(guó)家標(biāo)準(zhǔn)總局發(fā)布的關(guān)于簡(jiǎn)化漢字的編碼,通行于中國(guó)大陸地區(qū)及新加坡,簡(jiǎn)稱國(guó)標(biāo)
碼。兩個(gè)字節(jié)中,第一個(gè)字節(jié)(高字節(jié))的值為區(qū)號(hào)值加32(20H),第二個(gè)字節(jié)(低字節(jié))的值為位號(hào)值加32(20H),用這兩個(gè)值來(lái)表示一個(gè)漢字的編
碼。UNICODE 碼是微軟提出的解決多國(guó)字符問(wèn)題的多字節(jié)等長(zhǎng)編碼,它對(duì)英文字符采取前面加“0”字節(jié)的策略實(shí)現(xiàn)等長(zhǎng)兼容。如 “A” 的
ASCII 碼為0x41,UNICODE 就為0x00,0x41。利用特殊的工具各種編碼之間可以互相轉(zhuǎn)換。
Java 中文問(wèn)題的初步認(rèn)識(shí)
我們基于 Java 編程語(yǔ)言進(jìn)行應(yīng)用開(kāi)發(fā)時(shí),不可避免地要處理中文。Java 編程語(yǔ)言默認(rèn)的編碼方式是
UNICODE,而我們通常使用的數(shù)據(jù)庫(kù)及文件都是基于 GB2312 編碼的,我們經(jīng)常碰到這樣的情況:瀏覽基于 JSP
技術(shù)的網(wǎng)站看到的是亂碼,文件打開(kāi)后看到的也是亂碼,被 Java 修改過(guò)的數(shù)據(jù)庫(kù)的內(nèi)容在別的場(chǎng)合應(yīng)用時(shí)無(wú)法繼續(xù)正確地提供信息。
String sEnglish = “apple”;
String sChinese = “蘋(píng)果”;
String s = “蘋(píng)果 apple ”; |
sEnglish 的長(zhǎng)度是5,sChinese的長(zhǎng)度是4,而 s 默認(rèn)的長(zhǎng)度是14。對(duì)于 sEnglish來(lái)說(shuō), Java
中的各個(gè)類都支持得非常好,肯定能夠正確顯示。但對(duì)于 sChinese 和 s 來(lái)說(shuō),雖然 Java Soft 聲明 Java
的基本類已經(jīng)考慮到對(duì)多國(guó)字符的支持(默認(rèn) UNICODE 編碼),但是如果操作系統(tǒng)的默認(rèn)編碼不是 UNICODE ,而是國(guó)標(biāo)碼等。從 Java
源代碼到得到正確的結(jié)果,要經(jīng)過(guò) “Java 源代碼-> Java 字節(jié)碼->
;虛擬機(jī)->操作系統(tǒng)->顯示設(shè)備”的過(guò)程。在上述過(guò)程中的每一步驟,我們都必須正確地處理漢字的編碼,才能夠使最終的顯示結(jié)果正確。
“ Java 源代碼-> Java 字節(jié)碼”,標(biāo)準(zhǔn)的 Java 編譯器 javac 使用的字符集是系統(tǒng)默認(rèn)的字符集,比如在中文
Windows 操作系統(tǒng)上就是 GBK ,而在 Linux 操作系統(tǒng)上就是ISO-8859-1,所以大家會(huì)發(fā)現(xiàn)在 Linux
操作系統(tǒng)上編譯的類中源文件中的中文字符都出了問(wèn)題,解決的辦法就是在編譯的時(shí)候添加 encoding 參數(shù),這樣才能夠與平臺(tái)無(wú)關(guān)。用法是
javac ?Cencoding GBK。
“ Java 字節(jié)碼->虛擬機(jī)->操作系統(tǒng)”, Java 運(yùn)行環(huán)境 (JRE) 分英文版和國(guó)際版,但只有國(guó)際版才支持非英文字符。
Java 開(kāi)發(fā)工具包 (JDK) 肯定支持多國(guó)字符,但并非所有的計(jì)算機(jī)用戶都安裝了 JDK 。很多操作系統(tǒng)及應(yīng)用軟件為了能夠更好的支持
Java ,都內(nèi)嵌了 JRE 的國(guó)際版本,為自己支持多國(guó)字符提供了方便。
“操作系統(tǒng)->顯示設(shè)備”,對(duì)于漢字來(lái)說(shuō),操作系統(tǒng)必須支持并能夠顯示它。英文操作系統(tǒng)如果不搭配特殊的應(yīng)用軟件的話,是肯定不能夠顯示中文的。
還有一個(gè)問(wèn)題,就是在 Java 編程過(guò)程中,對(duì)中文字符進(jìn)行正確的編碼轉(zhuǎn)換。例如,向網(wǎng)頁(yè)輸出中文字符串的時(shí)候,不論你是用
還是用<%=string%>,都必須作 UNICODE 到 GBK 的轉(zhuǎn)換,或者手動(dòng),或者自動(dòng)。在 JSP 1.0中,可以定義輸出字符集,從而實(shí)現(xiàn)內(nèi)碼的自動(dòng)轉(zhuǎn)換。用法是
<%@page contentType=”text/html;charset=gb2312” %> |
但是在一些 JSP 版本中并沒(méi)有提供對(duì)輸出字符集的支持,(例如 JSP 0.92),這就需要手動(dòng)編碼輸出了,方法非常多。最常用的方法是
String s1 = request.getParameter(“keyword”);
String s2 = new String(s1.getBytes(“ISO-8859-1”),”GBK”); |
getBytes 方法用于將中文字符以“ISO-8859-1”編碼方式轉(zhuǎn)化成字節(jié)數(shù)組,而“GBK”
是目標(biāo)編碼方式。我們從以ISO-8859-1方式編碼的數(shù)據(jù)庫(kù)中讀出中文字符串 s1 ,經(jīng)過(guò)上述轉(zhuǎn)換過(guò)程,在支持 GBK
字符集的操作系統(tǒng)和應(yīng)用軟件中就能夠正確顯示中文字符串 s2 。
Java 中文問(wèn)題的表層分析及處理
背景
|
開(kāi)發(fā)環(huán)境 |
JDK1.15 |
Vcafe2.0 |
JPadPro |
服務(wù)器端 |
NT IIS |
Sybase System |
Jconnect(JDBC) |
客戶端 |
IE5.0 |
Pwin98 |
|
.CLASS 文件存放在服務(wù)器端,由客戶端的瀏覽器運(yùn)行 APPLET , APPLET 只起調(diào)入 FRAME 類等主程序的作用。界面包括 Textfield ,TextArea,List,Choice 等。
I.用 JDBC 執(zhí)行 SELECT 語(yǔ)句從服務(wù)器端讀取數(shù)據(jù)(中文)后,將數(shù)據(jù)用 APPEND 方法加到 TextArea(TA) ,不能正確顯示。但加到 List 中時(shí),大部分漢字卻可正確顯示。
將數(shù)據(jù)按“ISO-8859-1” 編碼方式轉(zhuǎn)化為字節(jié)數(shù)組,再按系統(tǒng)缺省編碼方式 (Default Character Encoding) 轉(zhuǎn)化為 STRING ,即可在 TA 和 List 中正確顯示。
程序段如下:
dbstr2 = results.getString(1);
//After reading the result from DB server,converting it to string.
dbbyte1 = dbstr2.getBytes(“iso-8859-1”);
dbstr1 = new String(dbbyte1); |
在轉(zhuǎn)換字符串時(shí)不采用系統(tǒng)默認(rèn)編碼方式,而直接采用“ GBK” 或者 “GB2312” ,在 A 和 B 兩種情況下,從數(shù)據(jù)庫(kù)取數(shù)據(jù)都沒(méi)有問(wèn)題。
II.處理方式與“取中文”相逆,先將 SQL 語(yǔ)句按系統(tǒng)缺省編碼方式轉(zhuǎn)化為字節(jié)數(shù)組,再按“ISO-8859-1”編碼方式轉(zhuǎn)化為 STRING ,最后送去執(zhí)行,則中文信息可正確寫(xiě)入數(shù)據(jù)庫(kù)。
程序段如下:
sqlstmt = tf_input.getText();
//Before sending statement to DB server,converting it to sql statement.
dbbyte1 = sqlstmt.getBytes();
sqlstmt = newString(dbbyte1,”iso-8859-1”);
_stmt = _con.createStatement();
_stmt.executeUpdate(sqlstmt);
…… |
問(wèn)題:如果客戶機(jī)上存在 CLASSPATH 指向 JDK 的 CLASSES.ZIP 時(shí)(稱為 A 情況),上述程序代碼可正確執(zhí)行。但是如果客戶機(jī)只有瀏覽器,而沒(méi)有 JDK 和 CLASSPATH 時(shí)(稱為 B 情況),則漢字無(wú)法正確轉(zhuǎn)換。
我們的分析:
1.經(jīng)過(guò)測(cè)試,在 A 情況下,程序運(yùn)行時(shí)系統(tǒng)的缺省編碼方式為 GBK 或者 GB2312 。在 B 情況下,程序啟動(dòng)時(shí)瀏覽器的 JAVA 控制臺(tái)中出現(xiàn)如下錯(cuò)誤信息:
Can't find resource for sun.awt.windows.awtLocalization_zh_CN
然后系統(tǒng)的缺省編碼方式為“8859-1”。
2.如果在轉(zhuǎn)換字符串時(shí)不采用系統(tǒng)缺省編碼方式,而是直接采用 “GBK” 或“GB2312”,則在 A 情況下程序仍然可正常運(yùn)行,在 B 情況下,系統(tǒng)出現(xiàn)錯(cuò)誤:
UnsupportedEncodingException。
3.在客戶機(jī)上,把 JDK 的 CLASSES.ZIP 解壓后,放在另一個(gè)目錄中, CLASSPATH
只包含該目錄。然后一邊逐步刪除該目錄中的 .CLASS 文件,另一邊運(yùn)行測(cè)試程序,最后發(fā)現(xiàn)在一千多個(gè) CLASS
文件中,只有一個(gè)是必不可少的,該文件是:
sun.io.CharToByteDoubleByte.class。
將該文件拷到服務(wù)器端和其它的類放在一起,并在程序的開(kāi)頭 IMPORT 它,在 B 情況下程序仍然無(wú)法正常運(yùn)行。
4.在 A 情況下,如果在 CLASSPTH 中去掉 sun.io.CharToByteDoubleByte.class ,則程序運(yùn)行時(shí)測(cè)得默認(rèn)編碼方式為“8859-1”,否則為 “GBK” 或 “GB2312” 。
如果 JDK 的版本為1.2以上的話,在 B 情況下遇到的問(wèn)題得到了很好的解決,測(cè)試的步驟同上,有興趣的讀者可以嘗試一下。
Java 中文問(wèn)題的根源分析及解決
在簡(jiǎn)體中文 MS Windows 98 + JDK 1.3 下,可以用 System.getProperties() 得到 Java 運(yùn)行環(huán)境的一些基本屬性,類 PoorChinese 可以幫助我們得到這些屬性。
類 PoorChinese 的源代碼:
public class PoorChinese {}
執(zhí)行 java PoorChinese 后,我們會(huì)得到:
系統(tǒng)變量 file.encoding 的值為 GBK ,user.language 的值為 zh , user.region 的值為 CN ,這些系統(tǒng)變量的值決定了系統(tǒng)默認(rèn)的編碼方式是 GBK 。
在上述系統(tǒng)中,下面的代碼將 GB2312 文件轉(zhuǎn)換成 Big5 文件,它們能夠幫助我們理解 Java 中漢字編碼的轉(zhuǎn)化:
import java.io.*;
import java.util.*;
public class gb2big5
{
static int iCharNum=0;
public static void main(String[] args) {
System.out.println("Input GB2312 file, output Big5 file.");
if (args.length!=2)
{
System.err.println("Usage: jview gb2big5 gbfile big5file");
System.exit(1);
String inputString = readInput(args[0]);
writeOutput(inputString,args[1]);
System.out.println("Number of Characters in file: "+iCharNum+".");
}
static void writeOutput(String str, String strOutFile)
{
try
{
FileOutputStream fos = new FileOutputStream(strOutFile);
Writer out = new OutputStreamWriter(fos, "Big5");
out.write(str);
out.close();
}
catch (IOException e)
{
e.printStackTrace();
e.printStackTrace();
}
}
static String readInput(String strInFile)
{
StringBuffer buffer = new StringBuffer();
try
{
FileInputStream fis = new FileInputStream(strInFile);
InputStreamReader isr = new InputStreamReader(fis, "GB2312");
Reader in = new BufferedReader(isr);
int ch;
while ((ch = in.read()) > -1)
{
iCharNum += 1;buffer.append((char)ch);
}
in.close();
return buffer.toString();
}
catch (IOException e)
{
e.printStackTrace();
return null;
}
}
} |
編碼轉(zhuǎn)化的過(guò)程如下:
GB2312------------------>Unicode------------->Big5
執(zhí)行 java gb2big5 gb.txt big5.txt ,如果 gb.txt 的內(nèi)容是“今天星期三”,則得到的文件 big5.txt
中的字符能夠正確顯示;而如果 gb.txt 的內(nèi)容是“情人節(jié)快樂(lè)”,則得到的文件 big5.txt
中對(duì)應(yīng)于“節(jié)”和“樂(lè)”的字符都是符號(hào)“?”(0x3F),可見(jiàn) sun.io.ByteToCharGB2312 和
sun.io.CharToByteBig5 這兩個(gè)基本類并沒(méi)有編好。
正如上例一樣, Java
的基本類也可能存在問(wèn)題。由于國(guó)際化的工作并不是在國(guó)內(nèi)完成的,所以在這些基本類發(fā)布之前,沒(méi)有經(jīng)過(guò)嚴(yán)格的測(cè)試,所以對(duì)中文字符的支持并不像 Java
Soft 所聲稱的那樣完美。前不久,我的一位技術(shù)上的朋友發(fā)信給我說(shuō),他終于找到了 Java Servlet 中文問(wèn)題的根源。兩周以來(lái),他一直為
Java Servlet
的中文問(wèn)題所困擾,因?yàn)槊棵鎸?duì)一個(gè)含有中文字符的字符串都必須進(jìn)行強(qiáng)制轉(zhuǎn)換才能夠得到正確的結(jié)果(這好象是大家公認(rèn)的唯一的解決辦法)。
后來(lái),他確實(shí)不想如此繼續(xù)安分下去了,因?yàn)檫@樣的事情確實(shí)不應(yīng)該是高級(jí)程序員所要做的工作,他就找出 Servlet
解碼的源代碼進(jìn)行分析,因?yàn)樗麘岩蓡?wèn)題就出在解碼這部分。經(jīng)過(guò)四個(gè)小時(shí)的奮斗,他終于找到了問(wèn)題的根源所在。原來(lái)他的懷疑是正確的, Servlet
的解碼部分完全沒(méi)有考慮雙字節(jié),直接把 %XX 當(dāng)作一個(gè)字符。(原來(lái) Java Soft 也會(huì)犯這幺低級(jí)的錯(cuò)誤!)
如果你對(duì)這個(gè)問(wèn)題有興趣或者遇到了同樣的煩惱的話,你可以按照他的步驟 對(duì)Servlet.jar 進(jìn)行修改:
找到源代碼 HttpUtils 中的 static private String parseName ,在返回前將
sb(StringBuffer) 復(fù)制成 byte bs[] ,然后 return new
String(bs,”GB2312”)。作上述修改后就需要自己解碼了:
HashTable form=HttpUtils .parseQueryString(request.getQueryString())或者
form=HttpUtils.parsePostData(……) |
千萬(wàn)別忘了編譯后放到 Servlet.jar 里面。
關(guān)于 Java 中文問(wèn)題的總結(jié)
Java 編程語(yǔ)言成長(zhǎng)于網(wǎng)絡(luò)世界,這就要求 Java 對(duì)多國(guó)字符有很好的支持。 Java
編程語(yǔ)言適應(yīng)了計(jì)算的網(wǎng)絡(luò)化的需求,為它能夠在網(wǎng)絡(luò)世界迅速成長(zhǎng)奠定了堅(jiān)實(shí)的基礎(chǔ)。 Java 的締造者 (Java Soft) 已經(jīng)考慮到
Java
編程語(yǔ)言對(duì)多國(guó)字符的支持,只是現(xiàn)在的解決方案有很多缺陷在里面,需要我們付諸一些補(bǔ)償性的措施。而世界標(biāo)準(zhǔn)化組織也在努力把人類所有的文字統(tǒng)一在一種編
碼之中,其中一種方案是 ISO10646 ,它用四個(gè)字節(jié)來(lái)表示一個(gè)字符。當(dāng)然,在這種方案未被采用之前,還是希望 Java Soft
能夠嚴(yán)格地測(cè)試它的產(chǎn)品,為用戶帶來(lái)更多的方便。
附一個(gè)用于從數(shù)據(jù)庫(kù)和網(wǎng)絡(luò)中取出 中文亂碼的處理函數(shù),入?yún)⑹怯袉?wèn)題的字符串,出參是問(wèn)題已經(jīng)解決了的字符串。