posts - 73,  comments - 55,  trackbacks - 0
            serialVersionUID 用來表明類的不同版本間的兼容性.如果你修改了此類, 要修改此值. 否則以前用老版本的類序列化的類恢復(fù)時會出錯.
            可以利用JDK的bin目錄下的serialver.exe工具產(chǎn)生這個serialVersionUID
            對于Test.class,執(zhí)行命令: serialver Test
            為了在反序列化時,確保類版本的兼容性,最好在每個要序列化的類中加入private static final long serialVersionUID這個屬性,具體數(shù)值自己定義。這樣,即使某個類在與之對應(yīng)的對象已經(jīng)序列化出去后做了修改,該對象依然可以被正確反序列化。否則,如果不顯示定義該屬性,這個屬性值將由JVM根據(jù)類的相關(guān)信息計算,而修改后的類的計算結(jié)果與修改前的類的計算結(jié)果往往不同,從而造成對象的反序列化因為類版本不兼容而失敗。
            不顯示定義這個屬性值的另一個壞處是,不利于程序在不同的JVM之間的移植。因為不同的編譯器實現(xiàn)的該屬性值的計算策略可能不同,從而造成雖然類沒有改變,但是因為JVM不同,依然會有因類版本不兼容而無法正確反序列化的現(xiàn)象出現(xiàn)。
            因為我做的系統(tǒng)不太會經(jīng)常需要序列化類,所以為了去掉這些警告,做如下設(shè)置:
          Window-Preferences-Java,如圖所示,將serializable class without serialVersionUID的設(shè)置由warning改為Ignore。然后Eclipse會重新編譯程序,那些警告信息也就會消失了。
            小結(jié):如果我們開發(fā)大量需要序列化的類的時候,我們最好還是還原為原來的設(shè)置。這樣可以保證系統(tǒng)的性能和健壯。
          -------------------------------------------------------------------
          java對象序列化問題研究(轉(zhuǎn)自http://www.54bk.com/user1/2064/archives/2005/1864.html
          ??? 序列化的過程就是對象寫入字節(jié)流和從字節(jié)流中讀取對象。將對象狀態(tài)轉(zhuǎn)換成字節(jié)流之后,可以用java.io包中的各種字節(jié)流類將其保存到文件中,管道到另一線程中或通過網(wǎng)絡(luò)連接將對象數(shù)據(jù)發(fā)送到另一主機。對象序列化功能非常簡單、強大,在RMI、Socket、JMS、EJB都有應(yīng)用。對象序列化問題在網(wǎng)絡(luò)編程中并不是最激動人心的課題,但卻相當(dāng)重要,具有許多實用意義。
          一:對象序列化可以實現(xiàn)分布式對象。主要應(yīng)用例如:RMI要利用對象序列化運行遠程主機上的服務(wù),就像在本地機上運行對象時一樣。
          二:java對象序列化不僅保留一個對象的數(shù)據(jù),而且遞歸保存對象引用的每個對象的數(shù)據(jù)。可以將整個對象層次寫入字節(jié)流中,可以保存在文件中或在網(wǎng)絡(luò)連接上傳遞。利用對象序列化可以進行對象的“深復(fù)制”,即復(fù)制對象本身及引用的對象本身。序列化一個對象可能得到整個對象序列。
          ? 從上面的敘述中,我們知道了對象序列化是java編程中的必備武器,那么讓我們從基礎(chǔ)開始,好好學(xué)習(xí)一下它的機制和用法。
          ??? java序列化比較簡單,通常不需要編寫保存和恢復(fù)對象狀態(tài)的定制代碼。實現(xiàn)java.io.Serializable接口的類對象可以轉(zhuǎn)換成字節(jié)流或從字節(jié)流恢復(fù),不需要在類中增加任何代碼。只有極少數(shù)情況下才需要定制代碼保存或恢復(fù)對象狀態(tài)。這里要注意:不是每個類都可序列化,有些類是不能序列化的,例如涉及線程的類與特定JVM有非常復(fù)雜的關(guān)系。
          序列化機制:
          序列化分為兩大部分:序列化和反序列化。序列化是這個過程的第一部分,將數(shù)據(jù)分解成字節(jié)流,以便存儲在文件中或在網(wǎng)絡(luò)上傳輸。反序列化就是打開字節(jié)流并重構(gòu)對象。對象序列化不僅要將基本數(shù)據(jù)類型轉(zhuǎn)換成字節(jié)表示,有時還要恢復(fù)數(shù)據(jù)。恢復(fù)數(shù)據(jù)要求有恢復(fù)數(shù)據(jù)的對象實例。ObjectOutputStream中的序列化過程與字節(jié)流連接,包括對象類型和版本信息。反序列化時,JVM用頭信息生成對象實例,然后將對象字節(jié)流中的數(shù)據(jù)復(fù)制到對象數(shù)據(jù)成員中。下面我們分兩大部分來闡述:

          處理對象流:
          (序列化過程和反序列化過程)
          ? java.io包有兩個序列化對象的類。ObjectOutputStream負責(zé)將對象寫入字節(jié)流,ObjectInputStream從字節(jié)流重構(gòu)對象。
          ??? 我們先了解ObjectOutputStream類吧。ObjectOutputStream類擴展DataOutput接口。
          writeObject()方法是最重要的方法,用于對象序列化。如果對象包含其他對象的引用,則writeObject()方法遞歸序列化這些對象。每個ObjectOutputStream維護序列化的對象引用表,防止發(fā)送同一對象的多個拷貝。(這點很重要)由于writeObject()可以序列化整組交叉引用的對象,因此同一ObjectOutputStream實例可能不小心被請求序列化同一對象。這時,進行反引用序列化,而不是再次寫入對象字節(jié)流。
          下面,讓我們從例子中來了解ObjectOutputStream這個類吧。
          // 序列化 today's date 到一個文件中.
          ??? FileOutputStream f = new FileOutputStream("tmp");
          ??? ObjectOutputStream s = new ObjectOutputStream(f);
          ??? s.writeObject("Today");
          ??? s.writeObject(new Date());
          ??? s.flush();
          ?? 現(xiàn)在,讓我們來了解ObjectInputStream這個類。它與ObjectOutputStream相似。它擴展DataInput接口。ObjectInputStream中的方法鏡像DataInputStream中讀取Java基本數(shù)據(jù)類型的公開方法。readObject()方法從字節(jié)流中反序列化對象。每次調(diào)用readObject()方法都返回流中下一個Object。對象字節(jié)流并不傳輸類的字節(jié)碼,而是包括類名及其簽名。readObject()收到對象時,JVM裝入頭中指定的類。如果找不到這個類,則readObject()拋出ClassNotFoundException,如果需要傳輸對象數(shù)據(jù)和字節(jié)碼,則可以用RMI框架。ObjectInputStream的其余方法用于定制反序列化過程。
          例子如下:
          //從文件中反序列化 string 對象和 date 對象
          ??? FileInputStream in = new FileInputStream("tmp");
          ??? ObjectInputStream s = new ObjectInputStream(in);
          ??? String today = (String)s.readObject();
          ??? Date date = (Date)s.readObject();

          定制序列化過程:

          序列化通常可以自動完成,但有時可能要對這個過程進行控制。java可以將類聲明為serializable,但仍可手工控制聲明為static或transient的數(shù)據(jù)成員。
          例子:一個非常簡單的序列化類。
          public class simpleSerializableClass implements Serializable{
          ??? String sToday="Today:";
          ??? transient Date dtToday=new Date();
          }
          序列化時,類的所有數(shù)據(jù)成員應(yīng)可序列化除了聲明為transient或static的成員。將變量聲明為transient告訴JVM我們會負責(zé)將變元序列化。將數(shù)據(jù)成員聲明為transient后,序列化過程就無法將其加進對象字節(jié)流中,沒有從transient數(shù)據(jù)成員發(fā)送的數(shù)據(jù)。后面數(shù)據(jù)反序列化時,要重建數(shù)據(jù)成員(因為它是類定義的一部分),但不包含任何數(shù)據(jù),因為這個數(shù)據(jù)成員不向流中寫入任何數(shù)據(jù)。記住,對象流不序列化static或transient。我們的類要用writeObject()與readObject()方法以處理這些數(shù)據(jù)成員。使用writeObject()與readObject()方法時,還要注意按寫入的順序讀取這些數(shù)據(jù)成員。
          關(guān)于如何使用定制序列化的部分代碼如下:
          //重寫writeObject()方法以便處理transient的成員。
          public void writeObject(ObjectOutputStream outputStream) throws IOException{
          ??? outputStream.defaultWriteObject();//使定制的writeObject()方法可以
          ??????????????????????? 利用自動序列化中內(nèi)置的邏輯。
          ??? outputStream.writeObject(oSocket.getInetAddress());
          ??? outputStream.writeInt(oSocket.getPort());
          }
          //重寫readObject()方法以便接收transient的成員。
          private void readObject(ObjectInputStream inputStream) throws IOException,ClassNotFoundException{
          ??? inputStream.defaultReadObject();//defaultReadObject()補充自動序列化
          ??? InetAddress oAddress=(InetAddress)inputStream.readObject();
          ??? int iPort =inputStream.readInt();
          ??? oSocket = new Socket(oAddress,iPort);
          ??? iID=getID();
          ??? dtToday =new Date();
          }

          完全定制序列化過程:
          如果一個類要完全負責(zé)自己的序列化,則實現(xiàn)Externalizable接口而不是Serializable接口。Externalizable接口定義包括兩個方法writeExternal()與readExternal()。利用這些方法可以控制對象數(shù)據(jù)成員如何寫入字節(jié)流.類實現(xiàn)Externalizable時,頭寫入對象流中,然后類完全負責(zé)序列化和恢復(fù)數(shù)據(jù)成員,除了頭以外,根本沒有自動序列化。這里要注意了。聲明類實現(xiàn)Externalizable接口會有重大的安全風(fēng)險。writeExternal()與readExternal()方法聲明為public,惡意類可以用這些方法讀取和寫入對象數(shù)據(jù)。如果對象包含敏感信息,則要格外小心。這包括使用安全套接或加密整個字節(jié)流。到此為至,我們學(xué)習(xí)了序列化的基礎(chǔ)部分知識。關(guān)于序
          列化的高級教程,以后再述。

          ?
          posted @ 2006-12-12 11:12 保爾任 閱讀(358) | 評論 (0)編輯 收藏

          Java FAQ
          ?
          目錄:
          Q1.1 什么是Java、Java2、JDK?JDK后面的1.3、1.4版本號又是怎么回事?
          Q1.2 什么是JRE/J2RE?
          Q1.3 學(xué)習(xí)Java用什么工具比較好?
          Q1.4? 學(xué)習(xí)Java有哪些好的參考書?
          Q1.5? Java和C++哪個更好?
          Q1.6? 什么是J2SE/J2EE/J2ME?
          Q2.1? 我寫了第一個Java程序,應(yīng)該如何編譯/運行?
          Q2.2? 我照你說的做了,但是出現(xiàn)什么“'javac' 不是內(nèi)部或外部命令,也不是可運行的
          程序或批處理文件。”。
          Q2.3? 環(huán)境變量怎么設(shè)置?
          Q2.4? 我在javac xxx.java的時候顯示什么“unreported exception java.io.IOExcepti
          on;”。
          Q2.5? javac xxx.java順利通過了,但是java xxx的時候顯示什么“NoClassDefFoundErr
          or”。
          Q2.6? 我在java xxx的時候顯示“Exception in thread "main" java.lang.NoSuchMetho
          dError: main”。
          Q2.7? 在java xxx的時候顯示“Exception in thread "main" java.lang.NullPointerEx
          ception”。
          Q2.8 package是什么意思?怎么用?
          Q2.9 我沒有聲明任何package會怎么樣?
          Q2.10 在一個類中怎么使用其他類?
          Q2.11 我用了package的時候顯示"NoClassDefFoundError",但是我把所有package去掉的
          時候能正常運行。
          Q2.12 我想把java編譯成exe文件,該怎么做?
          Q2.13 我在編譯的時候遇到什么"deprecated API",是什么意思?
          Q3.1 我怎么給java程序加啟動參數(shù),就像dir /p/w那樣?
          Q3.2 我怎么從鍵盤輸入一個int/double/字符串?
          Q3.3 我怎么輸出一個int/double/字符串?
          Q3.4 我發(fā)現(xiàn)有些書上直接用System.in輸入,比你要簡單得多。
          Q3.5 我怎么從文件輸入一個int/double/字符串?
          Q3.6 我想讀寫文件的指定位置,該怎么辦?
          Q3.7 怎么判斷要讀的文件已經(jīng)到了盡頭?
          Q4.1? java里面怎么定義宏?
          Q4.2? java里面沒法用const。
          Q4.3? java里面也不能用goto。
          Q4.4? java里面能不能重載操作符?
          Q4.5? 我new了一個對象,但是沒法delete掉它。
          Q4.6? 我想知道為什么main方法必須被聲明為public static?為什么在main方法中不能調(diào)
          用非static成員?
          Q4.7? throw和throws有什么不同?
          Q4.8? 什么是異常?
          Q4.9? final和finally有什么不同?
          Q5.1? extends和implements有什么不同?
          Q5.2? java怎么實現(xiàn)多繼承?
          Q5.3 abstract是什么?
          Q5.4 public,protected,private有什么不同?
          Q5.5 Override和Overload有什么不同?
          Q5.6 我繼承了一個方法,但現(xiàn)在我想調(diào)用在父類中定義的方法。
          Q5.7 我想在子類的構(gòu)造方法中調(diào)用父類的構(gòu)造方法,該怎么辦?
          Q5.8 我在同一個類中定義了好幾個構(gòu)造方法并且想在一個構(gòu)造方法中調(diào)用另一個。
          Q5.9 我沒有定義構(gòu)造方法會怎么樣?
          Q5.10 我調(diào)用無參數(shù)的構(gòu)造方法失敗了。
          Q5.11 我該怎么定義類似于C++中的析構(gòu)方法(destructor)?
          Q5.12 我想將一個父類對象轉(zhuǎn)換成一個子類對象該怎么做?
          Q5.13 其實我不確定a是不是B的實例,能不能分情況處理?
          Q5.14 我在方法里修改了一個對象的值,但是退出方法后我發(fā)現(xiàn)這個對象的值沒變!
          Q6.1 java能動態(tài)分配數(shù)組嗎?
          Q6.2 我怎么知道數(shù)組的長度?
          Q6.3 我還想讓數(shù)組的長度能自動改變,能夠增加/刪除元素。
          Q???? 什么是鏈表?為什么要有ArrayList和LinkedList兩種List?
          Q6.5 我想用隊列/棧。
          Q6.6 我希望不要有重復(fù)的元素。
          Q6.7 我想遍歷集合/Map。
          Q6.8 我還要能夠排序。
          Q6.9 但是我想給數(shù)組排序。
          Q6.10 我想按不同方式排序。
          Q6.11 Map有什么用?
          Q6.12 set方法沒問題,但是get方法返回的是Object。
          Q6.13 ArrayList和Vector有什么不同?HashMap和Hashtable有什么不同?
          Q6.14 我要獲得一個隨機數(shù)。
          Q6.15 我比較兩個String總是false,但是它們明明都是"abc" !
          Q6.16 我想修改一個String但是在String類中沒找到編輯方法。
          Q6.17 我想處理日期/時間。

          一、準(zhǔn)備篇

          Q1.1 什么是Java、Java2、JDK?JDK后面的1.3、1.4版本號又是怎么回事?
          答:Java是一種通用的,并發(fā)的,強類型的,面向?qū)ο蟮木幊陶Z言(摘自Java規(guī)范第二版
          )。
          JDK是Sun公司分發(fā)的免費Java開發(fā)工具包,正式名稱為J2SDK(Java2 Software Develop K
          it)。
          包括基本的java工具包和標(biāo)準(zhǔn)類庫。
          到目前(2003年7月)為止,Java有3個主要版本,即1.0,1.1,2.0;
          JDK有1.0,1.1,1.2,1.3,1.4五個版本。
          從JDK1.2起,Sun公司覺得Java改變足夠大而將java語言版本號提升為2.0。
          不同的JDK主要在于提供的類庫不同。作為學(xué)習(xí)你可以下載最新的JDK1.4.2。
          真正開發(fā)時則應(yīng)考慮向前兼容,比如1.3。下載請去http://java.sun.com
          JDK1.5預(yù)計將在2004年推出,屆時其中將包含若干嶄新的特性。

          Q1.2 什么是JRE/J2RE?
          答:J2RE是Java2 Runtime Environment,即Java運行環(huán)境,有時簡稱JRE。
          如果你只需要運行Java程序或Applet,下載并安裝它即可。
          如果你要自行開發(fā)Java軟件,請下載JDK。在JDK中附帶有JRE。
          注意由于Microsoft對Java的支持不完全,請不要使用IE自帶的虛擬機來運行Applet,務(wù)必
          安裝一個JRE或JDK。

          Q1.3 學(xué)習(xí)Java用什么工具比較好?
          答:作者建議首先使用JDK+文本編輯器,這有助你理解下列幾個基礎(chǔ)概念:path,classp
          ath,package
          并熟悉基本命令:javac和java。并且下載和你的JDK版本一致的API幫助。
          如果你不確定類或函數(shù)的用法,請先查閱API而不是發(fā)貼求助。
          當(dāng)你熟悉Java之后,你可以考慮開始使用一個IDE。
          作者推薦eclipse,下載網(wǎng)址http://www.eclipse.org。因為eclispe是免費的,插件化的

          eclispe的主要缺點是缺乏一個可視化的桌面程序開發(fā)工具,
          幸運的是IBM在2003年11月已經(jīng)將部分代碼捐給eclipse組織,可以預(yù)計這個缺點很快就會
          得到彌補。
          無論如何,請不要使用Microsoft的VJ++!眾所周知Microsoft從來就沒有認(rèn)真支持過Java

          最后但并非最不重要,要有一本好的參考書,并且英文要過關(guān)。

          Q1.4? 學(xué)習(xí)Java有哪些好的參考書?
          答:作者首先推薦Thinking in Java,中文名《Java編程思想》,有中文版。
          目前的最新版本是第三版。
          http://64.78.49.204可以免費下載英文版。
          該書第一章介紹了很多面向?qū)ο蟮木幊趟枷耄鳛樾率謶?yīng)當(dāng)認(rèn)真閱讀。
          除此以外,O'relly出版社和Wrox出版社的書也不錯。作者本人不喜歡大陸作者的書。
          也許你覺得英文太難,但是網(wǎng)上大多數(shù)資料都是英文的。另外,你需要經(jīng)常查閱API,而那
          也是英文的。

          Q1.5? Java和C++哪個更好?
          答:這個問題是一個很不恰當(dāng)?shù)膯栴}。你應(yīng)該問:Java和C++哪個更適用于我的項目?
          Java的優(yōu)點和缺點一樣明顯。
          跨平臺是Java的主要優(yōu)點,但代價是運行速度的下降。
          VC和Windows平臺有良好的集成和足夠快的速度,但是也只能局限在Windows平臺上。
          和C++相比,Java學(xué)起來更快,開發(fā)人員不會碰到很多容易出錯的特性。
          但是VB程序員甚至只需要拼裝模塊就可以了。

          Q1.6? 什么是J2SE/J2EE/J2ME?
          答:J2SE就是一般的Java。
          J2ME是針對嵌入式設(shè)備的,比如支持Java的手機,它有自己的JRE和SDK。
          J2EE是一組用于企業(yè)級程序開發(fā)的規(guī)范和類庫,它使用J2SE的JRE。

          二、命令篇

          Q2.1? 我寫了第一個Java程序,應(yīng)該如何編譯/運行?
          答:首先請將程序保存為xxx.java文件,注意你可能需要修改文件后綴名。
          然后在dos窗口下使用javac xxx.java命令,你會發(fā)現(xiàn)該目錄下多了一個xxx.class文件,

          再使用java xxx命令,你的java程序就開始運行了。

          Q2.2? 我照你說的做了,但是出現(xiàn)什么“'javac' 不是內(nèi)部或外部命令,也不是可運行的
          程序或批處理文件。”。
          答:你遇到了path問題。操作系統(tǒng)在一定的范圍(path)內(nèi)搜索javac.exe,但是沒能找到。

          請編輯你的操作系統(tǒng)環(huán)境變量,新增一個JAVA_HOME變量,設(shè)為你JDK的安裝目錄,
          再編輯Path變量,加上一項 %JAVA_HOME%\bin。
          然后保存并新開一個dos窗口,你就可以使用javac和java命令了。

          Q2.3? 環(huán)境變量怎么設(shè)置?
          答:請向身邊會設(shè)的人咨詢。

          Q2.4? 我在javac xxx.java的時候顯示什么“unreported exception java.io.IOExcepti
          on;”。
          答:參見Q4.8以了解java中的異常機制。

          Q2.5? javac xxx.java順利通過了,但是java xxx的時候顯示什么“NoClassDefFoundErr
          or”。
          答:1. 你遇到了classpath問題。java命令在一定的范圍(classpath)內(nèi)搜索你直接或間接
          使用的class文件,但是未能找到。
          首先請確認(rèn)你沒有錯敲成java xxx.class,
          其次,檢查你的CLASSPATH環(huán)境變量,其實你并不需要設(shè)置該變量,
          但如果你設(shè)置了該變量又沒有包含.(代表當(dāng)前目錄)的項,
          你就會遇到這個問題。請在你的CLASSPATH環(huán)境變量中加入一項. 或干脆刪掉這個變量。

          2. 如果你使用了并非JDK自帶的標(biāo)準(zhǔn)包,比如javax.servlet.*包,也會遇到這個問題,請
          將相應(yīng)的jar文件加入classpath。
          3. 如果你在java源文件中定義了package,請參見Q2.11。


          Q2.6? 我在java xxx的時候顯示“Exception in thread "main" java.lang.NoSuchMetho
          dError: main”。
          答:首先,在你的程序中每個java文件有且只能有一個public類,
          這個類的類名必須和文件名的大小寫完全一樣。
          其次,在你要運行的類中有且只能有一個public static void main(String[] args)方法

          這個方法就是你的主程序。


          Q2.7? 在java xxx的時候顯示“Exception in thread "main" java.lang.NullPointerEx
          ception”。
          答:在程序中你試圖在值為null的對象變量上調(diào)用方法,請檢查你的程序確保你的對象被恰當(dāng)?shù)某跏蓟?br />參見Q4.8以了解java中的異常機制。


          Q2.8 package是什么意思?怎么用?
          答:為了唯一標(biāo)識每個類并分組,java使用了package的概念。
          每個類都有一個全名,例如String的全名是java.lang.String,其中java.lang是包名,S
          tring是短名。按照java命名慣例,包名是全部小寫的,而類名的第一個字母是大寫的。
          這樣,如果你自行定義了同樣名字的類String,你可以把它放在mypackage中,
          通過使用全名mypackage.String和java.lang.String來區(qū)分這兩個類。
          同時,將邏輯上相關(guān)的類放在同一個包中,可以使程序結(jié)構(gòu)更為清楚。
          為了定義包,你要做的就是在java文件開頭加一行“package mypackage;”。
          注意包沒有嵌套或包含關(guān)系,mypackage包和mypackage.mysubpackage包對JRE來說是并列的兩個包(雖然開發(fā)者可
          能暗示包含關(guān)系)。

          Q2.9 我沒有聲明任何package會怎么樣?
          答:你的類被認(rèn)為放在默認(rèn)包中。這時全名和短名是一致的。

          Q2.10 在一個類中怎么使用其他類?
          答:如果你使用java.lang包或者默認(rèn)包中的類,不用做任何事。
          如果你的類位于mypackage包中,并且要調(diào)用同一包中的其他類,也不用做任何事。
          如果你使用其他包中的類,在package聲明之后,類聲明之前使用import otherpackage1.Class
          1; 或 import otherpackage2.*;?
          這里.*表示引入這個包中的所有類。然后在程序中你可以使用其他類的短名。
          如果短名間有重名沖突,必須使用全名來區(qū)分。
          注意在使用其他包中的類時,你只能使用public的類和接口,參見Q5.4。

          Q2.11 我用了package的時候顯示"NoClassDefFoundError",但是我把所有package去掉的
          時候能正常運行。
          答:將你的java文件按包名組織存放。
          比如你的工作目錄是/work,你的類是package1.Class1,那么將它存放為/work/package1
          /Class1.java。
          如果沒有聲明包,那么直接放在/work下。
          在/work下執(zhí)行javac package1/class1.java,再執(zhí)行java package1.class1,你會發(fā)現(xiàn)一
          切正常。
          另外,如果你的類的個數(shù)已經(jīng)多到了你需要使用包來組織的話,你可以考慮開始使用IDE。

          Q2.12 我想把java編譯成exe文件,該怎么做?
          答:JDK只能將java源文件編譯為class文件。
          class文件是一種跨平臺的字節(jié)碼,必須依賴平臺相關(guān)的JRE來運行。Java以此來實現(xiàn)跨平
          臺性。
          有些開發(fā)工具可以將java文件編譯為exe文件。作者反對這種做法,因為這樣就取消了跨平
          臺性。
          如果你確信你的軟件只在Windows平臺上運行,你可以考慮使用C++/C#來編程。

          Q2.13 我在編譯的時候遇到什么"deprecated API",是什么意思?
          答:所謂deprecated是指已經(jīng)過時,但是為了向前兼容起見仍然保留的方法。
          這些方法可能會在以后取消支持。你應(yīng)當(dāng)改用較新的方法。
          在API里面會說明你應(yīng)當(dāng)用什么方法來代替之。

          三、I/O篇

          Q3.1 我怎么給java程序加啟動參數(shù),就像dir /p/w那樣?
          答:還記得public static void main(String[] args)嗎?這里的args就是你的啟動參數(shù)

          在運行時你輸入java package1.class1 arg1 arg2,args中就會有兩個String,第一個是
          arg1,第二個是arg2。

          Q3.2 我怎么從鍵盤輸入一個int/double/字符串?
          答:java的I/O操作比C++要復(fù)雜一點。如果要從鍵盤輸入,樣例代碼如下:
          BufferedReader cin = new BufferedReader( new InputStreamReader( System.in ) );

          String s = cin.readLine();
          這樣你就獲得了一個字符串,如果你需要數(shù)字的話再使用:
          int n = Integer.parseInt( s ); 或者 double d = Double.parseDouble( s );
          來將字符串"534"轉(zhuǎn)換成int或double。

          Q3.3 我怎么輸出一個int/double/字符串?
          答:使用System.out.println(n)或者System.out.println("Hello")等等。

          Q3.4 我發(fā)現(xiàn)有些書上直接用System.in輸入,比你要簡單得多。
          答:java使用unicode,是雙字節(jié)。而System.in是單字節(jié)的stream。
          如果你要輸入雙字節(jié)文字比如中文,請使用作者的做法。

          Q3.5 我怎么從文件輸入/輸出一個int/double/字符串?
          答:類似于從鍵盤輸入,只不過換成
          BufferedReader fin = new BufferedReader( new FileReader(" myFileName " ) );
          PrintWriter fout = new PrintWriter( new FileWriter(" myFileName " ) );
          另外如果你還沒下載API,請開始下載并閱讀java.io包中的內(nèi)容。

          Q3.6 我想讀寫文件的指定位置,該怎么辦?
          答:java.io.RandomAccessFile可以滿足你的需要。

          Q3.7 怎么判斷要讀的文件已經(jīng)到了盡頭?
          答:在Reader的read方法中明確說明返回-1表示流的結(jié)尾。

          四、 關(guān)鍵字篇

          Q4.1? java里面怎么定義宏?
          答:java不支持宏,因為宏代換不能保證類型安全。
          如果你需要定義常量,可以將它定義為某個類的static final成員。參見Q4.2和Q4.6。


          Q4.2? java里面沒法用const。
          答:你可以用final關(guān)鍵字。例如 final int m = 9。被聲明為final的變量不能被再次賦
          值。唯一的例外是所謂blank final,如下例所示:
          public class MyClass1 {
          ??? private final int a = 3;
          ??? private final int b; // blank final

          ??? public MyClass1() {
          ??????? a = 5; // 不合法,final變量不能被再次賦值。
          ??????? b = 4; // 合法,這是b第一次被賦值。
          ??????? b = 6; // 不合法,b不能被再次賦值。
          ??? }
          }
          final也可以用于聲明方法或類,被聲明為final的方法或類不能被繼承。
          注意const是java的保留字以備擴充。

          Q4.3? java里面也不能用goto。
          答:甚至在面向過程的語言中你也可以完全不用goto。請檢查你的程序流程是否合理。

          如果你需要從多層循環(huán)中迅速跳出,java增強了(和C++相比)break和continue的功能,
          支持label。
          例如:
          outer :
          while( ... )
          {
          inner :
          for( ... )
          {
          ?????????? ...?? break inner; ...
          ?????????? ... continue outer; ...
          }
          }
          和const一樣,goto也是java的保留字以備擴充。

          Q4.4? java里面能不能重載操作符?
          答:不能。String的+號是唯一一個內(nèi)置的重載操作符。你可以通過定義接口和方法來實現(xiàn)
          類似功能。

          Q4.5? 我new了一個對象,但是沒法delete掉它。
          答:java有自動內(nèi)存回收機制,即所謂Garbarge Collection。你不需要刪除對象。你再也
          不用擔(dān)心指針錯誤,內(nèi)存溢出了。

          Q4.6? 我想知道為什么main方法必須被聲明為public static?為什么在main方法中不能調(diào)
          用非static成員?
          答:聲明為public是為了這個方法可以被外部調(diào)用,詳情見Q5.4。
          static是為了將某個成員變量/方法關(guān)聯(lián)到類(class)而非實例(instance)。
          你不需要創(chuàng)建一個對象就可以直接使用這個類的static成員,因而在static成員中不能調(diào)
          用非static成員,因為后者是關(guān)聯(lián)到對象實例(instance)的。
          在A類中調(diào)用B類的static成員可以使用B.staticMember的寫法。
          注意一個類的static成員變量是唯一的,被所有該類對象所共享的,在多線程程序設(shè)計中尤其要謹(jǐn)慎小心。
          類的static成員是在類第一次被JRE裝載的時候初始化的。
          你可以使用如下方法來使用非static成員:
          public class A
          {
          ??? private void someMethod() //非static成員
          ??? {}
          ??? public static void main(String args)
          ??? {
          ???????? A a = new A();? //創(chuàng)建一個對象實例
          ???????? a.someMethod();? //現(xiàn)在你可以使用非static方法了
          ??? }
          }


          Q4.7? throw和throws有什么不同?
          答:throws用于方法聲明中,聲明一個方法會拋出哪些異常。而throw是在方法體中實際執(zhí)行拋出異常的
          動作。
          如果你在方法中throw一個異常,卻沒有在方法聲明中聲明之,編譯器會報錯。
          注意Error和RuntimeException的子類是例外,無需特別聲明。

          Q4.8? 什么是異常?
          答:異常最早在Ada語言中引入,用于在程序中動態(tài)處理錯誤并恢復(fù)。
          你可以在方法中攔截底層異常并處理之,也可以拋給更高層的模塊去處理。
          你也可以拋出自己的異常指示發(fā)生了某些不正常情況。常見的攔截處理代碼如下:
          try
          {
          ......//以下是可能發(fā)生異常的代碼
          ??????? ...... //異常被你或低層API拋出,執(zhí)行流程中斷并轉(zhuǎn)向攔截代碼。
          ??????? ......
          }
          catch(Exception1 e) //如果Exception1是Exception2的子類并要做特別處理,應(yīng)排在前

          {
          ? //發(fā)生Exception1時被該段攔截
          }
          catch(Exception2 e)
          {
          ? //發(fā)生Exception2時被該段攔截
          }
          finally //這是可選的
          {
          ?? //無論異常是否發(fā)生,均執(zhí)行此段代碼
          ?? //即使在catch段中又向外拋出了新的exception,finally段也會得到執(zhí)行。
          }

          Q4.9? final和finally有什么不同?
          答:final請見Q4.2。finally用于異常機制,參見Q4.8。

          五、 面向?qū)ο笃?/p>

          Q5.1? extends和implements有什么不同?
          答:對于class而言,extends用于(單)繼承一個類(class),而implements用于實現(xiàn)一個接口(interf
          ace)。
          interface的引入是為了部分地提供多繼承的功能。
          在interface中只需聲明方法頭,而將方法體留給實現(xiàn)的class來做。
          這些實現(xiàn)的class的實例完全可以當(dāng)作interface的實例來對待。
          在interface之間也可以聲明為extends(多繼承)的關(guān)系。
          注意一個interface可以extends多個其他interface。

          Q5.2? java怎么實現(xiàn)多繼承?
          答:java不支持顯式的多繼承。
          因為在顯式多繼承的語言例如c++中,會出現(xiàn)子類被迫聲明祖先虛基類構(gòu)造函數(shù)的問題,

          而這是違反面向?qū)ο蟮姆庋b性原則的。
          java提供了interface和implements關(guān)鍵字來部分地實現(xiàn)多繼承。參見Q5.1。

          Q5.3 abstract是什么?
          答:被聲明為abstract的方法無需給出方法體,留給子類來實現(xiàn)。
          而如果一個類中有abstract方法,那么這個類也必須聲明為abstract。
          被聲明為abstract的類無法實例化,盡管它可以定義構(gòu)造方法供子類使用。

          Q5.4 public,protected,private有什么不同?
          答:這些關(guān)鍵字用于聲明類和成員的可見性。
          public成員可以被任何類訪問,
          protected成員限于自己和子類訪問,
          private成員限于自己訪問。
          Java還提供了第四種的默認(rèn)可見性,一般稱為package private,當(dāng)沒有任何public,protected,private修飾符時,成員
          是同一包內(nèi)可見。
          類可以用public或默認(rèn)來修飾。

          Q5.5 Override和Overload有什么不同?
          答:Override是指父類和子類之間方法的繼承關(guān)系,這些方法有著相同的名稱和參數(shù)類型

          Overload是指同一個類中不同方法(可以在子類也可以在父類中定義)間的關(guān)系,
          這些方法有著相同的名稱和不同的參數(shù)類型。


          Q5.6 我繼承了一個方法,但現(xiàn)在我想調(diào)用在父類中定義的方法。
          答:用super.xxx()可以在子類中調(diào)用父類方法。

          Q5.7 我想在子類的構(gòu)造方法中調(diào)用父類的構(gòu)造方法,該怎么辦?
          答:在子類構(gòu)造方法的第一行調(diào)用super(...)即可。

          Q5.8 我在同一個類中定義了好幾個構(gòu)造方法并且想在一個構(gòu)造方法中調(diào)用另一個。
          答:在構(gòu)造方法第一行調(diào)用this(...)。

          Q5.9 我沒有定義構(gòu)造方法會怎么樣?
          答:自動獲得一個無參數(shù)的構(gòu)造方法。

          Q5.10 我調(diào)用無參數(shù)的構(gòu)造方法失敗了。
          答:如果你至少定義了一個構(gòu)造方法,就不再有自動提供的無參數(shù)的構(gòu)造方法了。
          你需要另外顯式定義一個無參數(shù)的構(gòu)造方法。
          另外一種可能是你的構(gòu)造方法或者類不是public的,參見Q5.4了解java中的可見性。

          Q5.11 我該怎么定義類似于C++中的析構(gòu)方法(destructor)?
          答:提供一個void finalize()方法。在Garbarge Collector回收該對象時會調(diào)用該方法。

          注意實際上你很難判斷一個對象會在什么時候被回收。作者從未感到需要用到該方法。


          Q5.12 我想將一個父類對象轉(zhuǎn)換成一個子類對象該怎么做?
          答:強制類型轉(zhuǎn)換。如
          public void meth(A a)
          {
          B b = (B)a;
          }
          如果a實際上并不是B的實例,會拋出ClassCastException。所以請確保a確實是B的實例。


          Q5.13 其實我不確定a是不是B的實例,能不能分情況處理?
          答:可以使用instanceof操作符。例如
          if( a instanceof B )
          {
          B b = (B)a;
          }
          else
          {
          ...
          }

          Q5.14 我在方法里修改了一個對象的值,但是退出方法后我發(fā)現(xiàn)這個對象的值沒變!
          答:很可能你把傳入?yún)?shù)重賦了一個新對象,例如下列代碼就會造成這種錯誤:
          public void fun1(A a) //a是局部參數(shù),指向了一個外在對象。
          {
          a = new A(); //a指向了一個新對象,和外在對象脫鉤了。如果你要讓a作為傳出變量,
          不要寫這一句。
          ??????? a.setAttr(attr);//修改了新對象的值,外在對象沒有被修改。
          }
          基本類型也會出現(xiàn)這種情況。例如:
          public void fun2(int a)
          {
          a = 10;//只作用于本方法,外面的變量不會變化。
          }

          六、java.util篇

          Q6.1 java能動態(tài)分配數(shù)組嗎?
          答:可以。例如int n = 3; Language[] myLanguages = new Language[n];

          Q6.2 我怎么知道數(shù)組的長度?
          答:用length屬性。如上例中的? myLanguages.length 就為 3。

          Q6.3 我還想讓數(shù)組的長度能自動改變,能夠增加/刪除元素。
          答:用順序表--java.util.List接口。
          你可以選擇用ArrayList或是LinkedList,前者是數(shù)組實現(xiàn),后者是鏈表實現(xiàn)。
          例如:? List list = new ArrayList(); 或是 List list = new LinkedList();? 。

          Q??? 什么是鏈表?為什么要有ArrayList和LinkedList兩種List?
          答:請補習(xí)數(shù)據(jù)結(jié)構(gòu)。

          Q6.5 我想用隊列/棧。
          答:用java.util.LinkedList。

          Q6.6 我希望不要有重復(fù)的元素。
          答:用集合--java.util.Set接口。例如:Set set = new HashSet()。

          Q6.7 我想遍歷集合/Map。
          答:用java.util.Iterator。參見API。

          Q6.8 我還要能夠排序。
          答:用java.util.TreeSet。例如:Set set = new TreeSet()。放進去的元素會自動排序

          你需要為元素實現(xiàn)Comparable接口,還可能需要提供equals()方法,compareTo()方法,h
          ashCode()方法。

          Q6.9 但是我想給數(shù)組排序。
          答:java.util.Arrays類包含了sort等實用方法。

          Q6.10 我想按不同方式排序。
          答:為每種方式定義一個實現(xiàn)了接口Comparator的排序類并和Arrays或TreeSet綜合運用。


          Q6.11 Map有什么用?
          答:存儲key-value的關(guān)鍵字-值對,你可以通過關(guān)鍵字來快速存取相應(yīng)的值。

          Q6.12 set方法沒問題,但是get方法返回的是Object。
          答:強制類型轉(zhuǎn)換成你需要的類型。參見Q5.12。

          Q6.13 ArrayList和Vector有什么不同?HashMap和Hashtable有什么不同?
          答:ArrayList和HashMap是多線程不安全的,在多個線程中訪問同一個ArrayList對象可能
          會引起沖突并導(dǎo)致錯誤。
          而Vector和Hashtable是多線程安全的,即使在多個線程中同時訪問同一個Vector對象也不
          會引起差錯。
          看起來我們更應(yīng)該使用Vector和Hashtable,但是實際上Vector和Hashtable的性能太差,
          所以如果你不在多線程中使用的話,還是應(yīng)該用ArrayList和HashMap。

          Q6.14 我要獲得一個隨機數(shù)。
          答:使用java.util.Random類。

          Q6.15 我比較兩個String總是false,但是它們明明都是"abc" !
          答:比較String一定要使用equals或equalsIgnoreCase方法,不要使用 == !
          ==比較的是兩個引用(變量)是否指向了同一個對象,而不是比較其內(nèi)容。

          Q6.16 我想修改一個String但是在String類中沒找到編輯方法。
          答:使用StringBuffer類。
          String str = "......."; //待處理的字符串
          StringBuffer buffer = new StringBuffer(str); //使用該字符串初始化一個StringBuf
          fer
          buffer.append("..."); //調(diào)用StringBuffer的相關(guān)API來編輯字符串
          String str2 = buffer.toString(); //獲得編輯后的字符串。
          另外,如果你需要將多個字符串連接起來,請盡量避免使用+號直接連接,而是使用Strin
          gBuffer.append()方法。

          Q6.17 我想處理日期/時間。
          答:使用java.util.Date類。你可以使用java.text.SimpleDateFormat類來在String和Da
          te間互相轉(zhuǎn)換。
          SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //規(guī)
          定日期格式
          Date date = formatter.parse("2003-07-26 18:30:35"); //將符合格式的String轉(zhuǎn)換為
          Date
          String s = formatter.format(date); //將Date轉(zhuǎn)換為符合格式的String
          關(guān)于定義日期格式的詳細信息請參見API。
          ?

          J2EE FAQ
          ?
          目錄:

          一、準(zhǔn)備篇
          Q1.1?? 什么是J2EE?它和普通的Java有什么不同?
          Q1.2?? J2EE好學(xué)嗎?
          Q1.3?? J2EE有什么用?
          Q1.4?? 學(xué)J2EE有前途嗎?
          Q1.5?? 據(jù)說J2EE的性能不如.NET好,是真的嗎?
          Q1.6?? 聽你說了這么多,我想學(xué)著玩玩。
          Q1.7?? 學(xué)習(xí)J2EE該怎么開始?
          Q1.8?? 我下了一個J2EE服務(wù)器但是不會配置。
          Q1.9?? 我發(fā)現(xiàn)你沒有提到Tomcat。

          二、 Servlet/JSP篇
          Q2.1?? 什么是Servlet?
          Q2.2?? 我怎么獲得Http請求里的參數(shù)?
          Q2.3?? 我怎么返回結(jié)果?
          Q2.4?? sendRedirect()和forward()有什么不同?
          Q2.5?? 我寫了一個Servlet程序,怎么運行它?
          Q2.6?? EAR和WAR有什么不同?
          Q2.7?? EAR格式是怎樣的?
          Q2.8?? WAR格式是怎樣的?
          Q2.9?? 我的普通HTML文件/JSP文件應(yīng)當(dāng)放在哪里?
          Q2.10? 我訪問不到servlet,甚至連HTML文件都訪問不到!
          Q2.11? 我能訪問HTML但是訪問不到servlet。
          Q2.12? 什么是JSP?它和Servlet有什么區(qū)別?
          Q2.13? 我的JSP顯示的漢字是亂碼。
          Q2.14? 為什么使用gb18030而不是gb2312?
          Q2.15? 在JSP里面怎么引用Java Bean。
          Q2.16? 我想在servlet間傳遞數(shù)據(jù)。
          Q2.17? 怎么調(diào)用cookie?
          Q2.18? 怎么在JSP里面實現(xiàn)文件下載?
          Q2.19? 怎么實現(xiàn)文件上傳?
          Q2.20? 我想讓頁面自動刷新,比如聊天室。
          Q2.21? 我想讓用戶登錄以后才能訪問頁面。
          Q2.22? 我想要能注冊用戶。
          Q2.23? 怎么在JSP中訪問數(shù)據(jù)庫?
          Q2.24? 什么是JSTL?

          一、準(zhǔn)備篇

          Q1.1? 什么是J2EE?它和普通的Java有什么不同?
          答:J2EE全稱為Java2 Platform, Enterprise Edition。
          “J2EE平臺本質(zhì)上是一個分布式的服務(wù)器應(yīng)用程序設(shè)計環(huán)境——一個Java環(huán)境,它提供了

          ·宿主應(yīng)用的一個運行基礎(chǔ)框架環(huán)境。
          ·一套用來創(chuàng)建應(yīng)用的Java擴展API。”

          Q1.2? J2EE好學(xué)嗎?
          答:J2EE是很多技術(shù)的集合體,并且還在成長中。
          你會遇到很多專有名詞:比如(X)HTML,Servlet/JSP,JDBC,JMS,JNDI,EJB,XML,Web
          Service……。
          尤其是XML和Web Service正在快速成長。幸運的是,你不需要等到學(xué)會所有技術(shù)后再開始
          編程。
          大體上J2EE可以分成3個主要應(yīng)用方式:Servlet/JSP,EJB,Web Service 和一些支撐技術(shù)
          例如JDBC和JNDI。
          你可以一個一個的學(xué)。

          Q1.3 J2EE有什么用?
          答:用來建設(shè)大型的分布式企業(yè)級應(yīng)用程序。或者用更時髦的名詞說就是“電子商務(wù)”應(yīng)
          用程序。
          這些企業(yè)可能大到擁有中心數(shù)據(jù)庫服務(wù)器,Web服務(wù)器集群和遍布全國的辦公終端,也可能
          小到只不過想做一個網(wǎng)站。但是你肯定聽過“殺雞焉用牛刀”的古訓(xùn)。

          Q1.4 學(xué)J2EE有前途嗎?
          答:在這一市場目前只有一種技術(shù)可以和J2EE競爭,那就是Microsoft的.NET。
          相對來說.NET要“新”一些而J2EE要“老”一些。這也意味著.NET更易用一點而J2EE更成
          熟一點。
          但是.NET只能用于Windows平臺(Microsoft聲稱要開發(fā)C#在Linux上的虛擬機但是尚未兌現(xiàn)
          該諾言)。
          在過去幾年,.NET的市場份額并不理想。不過Microsoft還有Longhorn這一殺手锏,鹿死誰
          手還很難說。

          Q1.5 據(jù)說J2EE的性能不如.NET好,是真的嗎?
          答:在Sun公司提供的樣例程序Pet Store上,Microsoft聲稱不如相同的.NET程序好。
          而Sun公司反駁說這一程序不能真正體現(xiàn)J2EE的性能,并且指責(zé)Microsoft在數(shù)據(jù)庫上做了
          優(yōu)化。
          作者沒有學(xué)習(xí)過.NET因而不能妄下斷言。
          無論如何,大型分布式程序中的性能瓶頸通常首先來自于錯誤的設(shè)計。

          Q1.6 聽你說了這么多,我想學(xué)著玩玩。
          答:除非你想靠它當(dāng)飯吃或者作為技術(shù)儲備,否則請不要浪費你的時間。
          Flash要好玩得多。計算機游戲就更加好玩了。

          Q1.7 學(xué)習(xí)J2EE該怎么開始?
          答:首先,下載一個免費的J2EE服務(wù)器。其次,去java.sun.com下載J2EE的API。第三,找
          一本好的參考書。最后,找一個順手的IDE。
          J2EE服務(wù)器。你可以用Sun的J2EE SDK(免費),或者Weblogic(性能最好,但是太大,而
          且作者不推薦盜版行為),
          或者JBoss(免費,就是文檔太少),或者JRun(開發(fā)版免費,作者用這個)。
          參考書作者感覺Wrox的《J2EE服務(wù)器端高級編程》不錯,但是太老(作者手頭的是2001年
          中文版)。
          (似乎很多人不喜歡這本書......所以你得自己判斷它是否適合你。)
          你還需要去下載一些最新的技術(shù)資料(當(dāng)然肯定是英文的)。
          IDE如果你的機器配置夠好(內(nèi)存至少512M以上,256M或以下請勿考慮),可以用IBM的WS
          AD,不然就繼續(xù)用Eclipse或者其他。
          你也可以經(jīng)常去水木清華的Java版逛逛,但是在發(fā)貼前先看看精華區(qū)里有沒有你要的答案

          Q1.8 我下了一個J2EE服務(wù)器但是不會配置。
          答:請認(rèn)真閱讀隨機指導(dǎo)文檔,不同的服務(wù)器的配置都不一樣,作者愛莫能助。

          Q1.9 我發(fā)現(xiàn)你沒有提到Tomcat。
          答:Tomcat只是一個Web服務(wù)器,更準(zhǔn)確地說主要只是一個Web Container。
          如果你想要學(xué)習(xí)EJB的話,Tomcat無法滿足你的需要。

          二、 Servlet/JSP篇

          Q2.1 什么是Servlet?
          答:一個Servlet是一個Java類。它處理Http(s)請求并作出響應(yīng),包括返回一個HTML頁面
          或轉(zhuǎn)交給其他URL處理。
          Servlet必須運行在一個Web Container例如Tomcat中。
          Servlet必須是javax.servlet.http.HttpServlet的子類,
          你可以繼承doGet()或者doPost()方法,兩者分別對應(yīng)于Http(s)中的Get請求和Post請求。


          Q2.2 我怎么獲得Http請求里的參數(shù)?
          答:HttpRequest的getParameter()方法。例如:String paramValue = request.getPara
          meter("paramName");

          Q2.3 我怎么返回結(jié)果?
          答:你可以利用相關(guān)API打開一個輸出流,并向流中直接寫入一個HTML頁面。
          但是作者完全不贊成這樣做。一方面這樣做會很羅嗦。
          另一方面從Model-View-Controller模式(在《J2EE核心模式》中被歸為Front Controlle
          r模式)的觀點來看,
          你應(yīng)當(dāng)提供一些HTML或者JSP作為視圖(view),而Servlet則根據(jù)請求參數(shù)決定轉(zhuǎn)到哪一
          個視圖。
          你可以利用response.sendRedirect(...)方法或request.getDispatcher(...).forward()
          方法來實現(xiàn)。

          Q2.4 sendRedirect()和forward()有什么不同?
          答:sendRedirect()是向瀏覽器發(fā)送一個redirect通知,瀏覽器向新的URL發(fā)送一個新的請
          求。
          而forward是在服務(wù)器端直接將請求轉(zhuǎn)到新的URL,對于瀏覽器是透明的。
          換而言之,sendRedirect()應(yīng)當(dāng)將共享數(shù)據(jù)放在session中,forward應(yīng)當(dāng)將共享數(shù)據(jù)放在
          request中(當(dāng)然你也可以放在session中,但放在request中可以有效減小session中的數(shù)
          據(jù)量,從而改善性能)。
          前者瀏覽器的地址欄顯示的是新的URL,后者瀏覽器的地址欄顯示的是Servlet的URL。
          因而當(dāng)刷新目標(biāo)URL時,兩者會造成一些差別。

          Q2.5 我寫了一個Servlet程序,怎么運行它?
          答:開發(fā)J2EE程序有一個部署(deploy)的概念,實際上是開發(fā)——部署——運行的三部
          曲。
          大多數(shù)服務(wù)器支持Hot deploy。你只需要在相應(yīng)的Application目錄(具體路徑依賴于服務(wù)
          器)下面
          建立一個符合WAR或EAR格式(參見Q2.7,Q2.8)的目錄,啟動服務(wù)器,就可以通過瀏覽器
          訪問了。
          特別的,你的Servlet的class文件應(yīng)當(dāng)放在/WEB-INF/classes目錄中。
          注意J2EE SDK不支持Hot deploy,你需要通過它的deploy tool來部署。
          Tomcat只支持WAR格式。

          Q2.6 EAR和WAR有什么不同?
          答:EAR是一個完整的J2EE應(yīng)用程序,包括Web部分和EJB部分。
          WAR只是其中的Web部分。

          Q2.7 EAR格式是怎樣的?
          答:一個EAR可以包含任意多個WAR或EJB JAR,并且包含一個META-INF的目錄。
          在/META-INF中包含了一個application.xml,其中描述了這個EAR包含哪些模塊,以及安全
          性配置。
          細節(jié)請看參考書。

          Q2.8 WAR格式是怎樣的?
          答:一個WAR包含一個WEB-INF的目錄,這個目錄下包含classes目錄,lib目錄和web.xml。

          /WEB-INF/classes存放按package組織的class文件,/WEB-INF/lib目錄存放jar文件,
          web.xml描述了很多東西,請讀參考書。

          Q2.9 我的普通HTML文件/JSP文件應(yīng)當(dāng)放在哪里?
          答:放在除了/WEB-INF以外的其他地方。

          感謝antegg網(wǎng)友對于安全性的提醒:
          如果你想直接用http://url/***.jsp的方式來訪問,就要像上面說得那樣放。
          但是這樣的做法是不安全的,安全的做法是把所有的JSP頁面放在/WEB-INF目錄下面,并且

          通過WEB-CONTAINER來訪問。

          作者意見:
          我更喜歡用filter來做安全性檢查。
          在MVC模式中,JSP只是一個視圖而已,一般無需特別擔(dān)憂安全性。和普通的html放在一起
          也利于維護。

          Q2.10 我訪問不到servlet,甚至連HTML文件都訪問不到!
          答:
          第一你沒啟動服務(wù)器。
          第二你敲錯了端口。
          第三你沒有正確配置context-path。
          第四你的服務(wù)器不支持auto reload或者你關(guān)閉了這一選項,你得重啟服務(wù)器或重新部署W
          AR。
          第五確認(rèn)你沒有把HTML放在/WEB-INF目錄下,那是訪問不到的。

          Q2.11 我能訪問HTML但是訪問不到servlet。
          答:請檢查你的web.xml文件。確保你正確定義了<servlet>和<servlet-mapping>元素。

          前者標(biāo)識了一個servlet,后者將一個相對于context-path的URL映射到一個servlet。
          在Tomcat中你可以通過/context-path/servlet/package/servletname的形式訪問servlet

          但是這只是Tomcat的便捷訪問方式,并不是正式規(guī)范。
          細節(jié)請看參考書。

          Q2.12? 什么是JSP?它和Servlet有什么區(qū)別?
          答:你可以將JSP當(dāng)做一個可擴充的HTML來對待。
          雖然在本質(zhì)上JSP文件會被服務(wù)器自動翻譯為相應(yīng)的Servlet來執(zhí)行。
          可以說Servlet是面向Java程序員而JSP是面向HTML程序員的,除此之外兩者功能完全等價

          Q2.13? 我的JSP顯示的漢字是亂碼。
          答:在你的JSP開頭加上一行 <%@ page contentType="text/html; charset=gb18030"%>

          如果你已經(jīng)聲明了page我想你知道該怎么修改。

          Q2.14? 為什么使用gb18030而不是gb2312?
          答:gb18030是繼gb2312之后的下一代漢字編碼標(biāo)準(zhǔn),最終將過渡到Unicode。

          Q2.15? 在JSP里面怎么引用Java Bean。
          答:首先,確認(rèn)你要引用的類在/WEB-INF/classes下或在/WEB-INF/lib的某個jar內(nèi)。
          其次,在JSP里加一行 <jsp:useBean id="..." scope="..." class="..."/>
          具體解釋請看參考書。

          Q2.16? 我想在servlet間傳遞數(shù)據(jù)。
          答:利用session。在Servlet/JSP中,你可以在4個地方保存數(shù)據(jù)。
          1) page,本頁面。
          2) session,用來存放客戶相關(guān)的信息,比如購物車,對應(yīng)接口為javax.servlet.http.H
          ttpSession。
          session機制實際上是cookie和URL Rewriting的抽象,服務(wù)器會自動使用cookie或URL Re
          writing來實現(xiàn)。
          3) request,可以在forward()時傳遞信息,對應(yīng)接口為javax.servlet.http.HttpReques
          t。
          4) application,或稱context,存放全局信息,對應(yīng)接口為javax.servlet.ServletCont
          ext。

          Q2.17? 怎么調(diào)用cookie?
          答:作者建議使用session,你總是會遇到某些禁用cookie的用戶。這時session會自動使
          用URL重寫來實現(xiàn)。

          Q2.18? 怎么在JSP里面實現(xiàn)文件下載?
          答:實際上這是一個HTML的問題。答案是一個超鏈接<a>。

          Q2.19? 怎么實現(xiàn)文件上傳?
          答:客戶端是HTML問題,在form中設(shè)置method為post,enctype為multi-part/form-data,
          加一個<input type="file">。
          而在接收的servlet中只是一個I/O問題,你可以使用jakarta的file-upload庫。

          Q2.20? 我想讓頁面自動刷新,比如聊天室。
          答:這是一個HTML問題,在<head>部分中加一條<meta http-equiv="refresh" content="
          5" url="...">。
          這是所謂的Client-pull,客戶端刷新技術(shù)。
          相對的還有Server-push,服務(wù)器端刷新技術(shù),但是這一技術(shù)由于要占用服務(wù)器端資源而會
          在大量訪問時出現(xiàn)瓶頸現(xiàn)象,參見http://216.239.33.104/search?q=cache:autUfoakirY
          J:www.kfunigraz.ac.at/edvndwww/books/books/javaenterprise/servlet/ch06_03.htm+
          server-push+servlet&hl=zh-CN&ie=UTF-8

          Q2.21? 我想讓用戶登錄以后才能訪問頁面。
          答:使用聲明式安全措施。
          你只需要在web.xml中定義安全角色(Role),并定義受保護的URL集合只能由特定Role訪
          問。
          大多數(shù)服務(wù)器支持基于數(shù)據(jù)庫的用戶映射,你只要在相應(yīng)數(shù)據(jù)庫中建立兩張表并配置服務(wù)
          器就可以了。
          注意J2EE SDK不支持基于數(shù)據(jù)庫的用戶映射。
          細節(jié)請看參考書和服務(wù)器文檔。
          不過在商業(yè)環(huán)境中,J2EE所提供的聲明式安全措施仍然偏弱。一般商業(yè)程序會使用數(shù)據(jù)庫
          存儲user-role-privilege模型來達到安全性要求,細節(jié)請詢問你的構(gòu)架設(shè)計師。

          Q2.22? 我想要能注冊用戶。
          答:參看Q2.21。在接受注冊請求的Servlet中執(zhí)行寫入數(shù)據(jù)庫操作即可。

          Q2.23? 怎么在JSP中訪問數(shù)據(jù)庫?
          答:標(biāo)準(zhǔn)做法是使用DAO模式,定義一個Java bean來訪問數(shù)據(jù)庫并在JSP中使用。
          然而,當(dāng)你的數(shù)據(jù)庫模式很簡單時,你可以使用JSTL中的<sql:query>標(biāo)簽來快速訪問。

          在一般的J2EE項目中,JSP處于表示層(展現(xiàn)層),需要先后通過業(yè)務(wù)層和集成層才會訪問
          到數(shù)據(jù)庫,所以這個問題確實只會在很小的程序中才會遇到。

          Q2.24? 什么是JSTL?
          答:JSTL是Jsp Standard Tag Library的縮寫。這是一組通用標(biāo)簽并將成為JSP 2.0的一部
          分。
          其中包含賦值<c:set>,分支<c:if>,循環(huán)<c:forEach>,查詢數(shù)據(jù)庫<sql:query>,更新數(shù)
          據(jù)庫<sql:update>
          等。目前你需要像添加自定義標(biāo)簽庫一樣來添加JSTL,但是可以預(yù)計JSP 2.0會將JSTL作為
          組成部分。
          標(biāo)簽庫可以在http://jakarta.apache.org下載。注意JSTL需要在支持JSP 1.2或更高版本
          的容器下運行。
          幫助文件可以閱讀sun的JSTL正式規(guī)范。

          posted @ 2006-12-12 11:12 保爾任 閱讀(205) | 評論 (0)編輯 收藏

           ? (1) 若在定義中出現(xiàn)了常數(shù)初始化字符,則大寫static final基本類型標(biāo)識符中的所有字母,單詞之間用“_”連接。eg:private static final int MAX_LENGTH = 1000;Java包(Package)全都是小寫字母,即便中間的單詞亦是如此。

            (2) 為了常規(guī)用途而創(chuàng)建一個類時,請采取“經(jīng)典形式”,并包含對下述元素的定義: (規(guī)范要求,如果兩個對象進行equals比較時如果返回true,那么它們的hashcode要求返回相等的值。但hashcode一樣時兩個對象不==)
          equals()
          hashCode()
          toString()
          clone()(mplement Cloneable)
          mplement Serializable

            (3) 對于自己創(chuàng)建的每一個類,都考慮置入一個main(),其中包含了用于測試那個類的代碼。為使用一個項目中的類,我們沒必要刪除測試代碼。若進行了任何形式的改動,可方便地返回測試。這些代碼也可作為如何使用類的一個示例使用。main()方法在類定義的最底部。

          ??? (4)

          ??? (5) 設(shè)計一個類時,請設(shè)身處地為客戶程序員考慮一下(類的使用方法應(yīng)該是非常明確的)。然后,再設(shè)身處地為管理代碼的人考慮一下(預(yù)計有可能進行哪些形式的修改,想想用什么方法可把它們變得更簡單)。

            (6) 使類盡可能短小精悍,而且只解決一個特定的問題。下面是對類設(shè)計的一些建議:

            ■一個復(fù)雜的開關(guān)語句:考慮采用“多形”機制

            ■數(shù)量眾多的方法涉及到類型差別極大的操作:考慮用幾個類來分別實現(xiàn)

            ■許多成員變量在特征上有很大的差別:考慮使用幾個類 。

            (7) 讓一切東西都盡可能地“私有”——private。可使庫的某一部分“公共化”(一個方法、類或者一個字段等等),就永遠不能把它拿出。若強行拿出,就可能破壞其他人現(xiàn)有的代碼,使他們不得不重新編寫和設(shè)計。若只公布自己必須公布的,就可放心大膽地改變其他任何東西。在多線程環(huán)境中,隱私是特別重要的一個因素——只有private字段才能在非同步使用的情況下受到保護。

            (8) 謹(jǐn)惕“巨大對象綜合癥”。對一些習(xí)慣于順序編程思維、且初涉OOP領(lǐng)域的新手,往往喜歡先寫一個順序執(zhí)行的程序,再把它嵌入一個或兩個巨大的對象里。根據(jù)編程原理,對象表達的應(yīng)該是應(yīng)用程序的概念,而非應(yīng)用程序本身。

            (9) 若不得已進行一些不太雅觀的編程,至少應(yīng)該把那些代碼置于一個類的內(nèi)部。

            (10) 任何時候只要發(fā)現(xiàn)類與類之間結(jié)合得非常緊密,就需要考慮是否采用內(nèi)部類,從而改善編碼及維護工作。

            (11) 盡可能細致地加上注釋,并用javadoc注釋文檔語法生成自己的程序文檔。

            (12) 避免使用“魔術(shù)數(shù)字”,這些數(shù)字很難與代碼很好地配合。如以后需要修改它,無疑會成為一場噩夢,因為根本不知道“100”到底是指“數(shù)組大小”還是“其他全然不同的東西”。所以,我們應(yīng)創(chuàng)建一個常數(shù),并為其使用具有說服力的描述性名稱,并在整個程序中都采用常數(shù)標(biāo)識符。這樣可使程序更易理解以及更易維護。

            (13) 涉及構(gòu)建器和異常的時候,通常希望重新丟棄在構(gòu)建器中捕獲的任何異常——如果它造成了那個對象的創(chuàng)建失敗。這樣一來,調(diào)用者就不會以為那個對象已正確地創(chuàng)建,從而盲目地繼續(xù)。

            (14) 當(dāng)客戶程序員用完對象以后,若你的類要求進行任何清除工作,可考慮將清除代碼置于一個良好定義的方法里,采用類似于cleanup()這樣的名字,明確表明自己的用途。除此以外,可在類內(nèi)放置一個boolean(布爾)標(biāo)記,指出對象是否已被清除。在類的finalize()方法里,請確定對象已被清除,并已丟棄了從RuntimeException繼承的一個類(如果還沒有的話),從而指出一個編程錯誤。在采取象這樣的方案之前,請確定finalize()能夠在自己的系統(tǒng)中工作(可能需要調(diào)用System.runFinalizersonExit(true),從而確保這一行為)。

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

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

            (17) 創(chuàng)建大小固定的對象集合時,請將它們傳輸至一個數(shù)組(若準(zhǔn)備從一個方法里返回這個集合,更應(yīng)如此操作)。這樣一來,我們就可享受到數(shù)組在編譯期進行類型檢查的好處。此外,為使用它們,數(shù)組的接收者也許并不需要將對象“造型”到數(shù)組里。

            (18) 盡量使用interfaces,不要使用abstract類。若已知某樣?xùn)|西準(zhǔn)備成為一個基礎(chǔ)類,那么第一個選擇應(yīng)是將其變成一個interface(接口)。只有在不得不使用方法定義或者成員變量的時候,才需要將其變成一個abstract(抽象)類。接口主要描述了客戶希望做什么事情,而一個類則致力于(或允許)具體的實施細節(jié)。

            (19) 在構(gòu)建器內(nèi)部,只進行那些將對象設(shè)為正確狀態(tài)所需的工作。盡可能地避免調(diào)用其他方法,因為那些方法可能被其他人覆蓋或取消,從而在構(gòu)建過程中產(chǎn)生不可預(yù)知的結(jié)果(參見第7章的詳細說明)。

            (20) 對象不應(yīng)只是簡單地容納一些數(shù)據(jù);它們的行為也應(yīng)得到良好的定義。

            (21) 在現(xiàn)成類的基礎(chǔ)上創(chuàng)建新類時,請首先選擇“新建”或“創(chuàng)作”。只有自己的設(shè)計要求必須繼承時,才應(yīng)考慮這方面的問題。若在本來允許新建的場合使用了繼承,則整個設(shè)計會變得沒有必要地復(fù)雜。

            (22) 用繼承及方法覆蓋來表示行為間的差異,而用字段表示狀態(tài)間的區(qū)別。一個非常極端的例子是通過對不同類的繼承來表示顏色,這是絕對應(yīng)該避免的:應(yīng)直接使用一個“顏色”字段。

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

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

            (25) 用合理的設(shè)計方案消除“偽功能”。也就是說,假若只需要創(chuàng)建類的一個對象,就不要提前限制自己使用應(yīng)用程序,并加上一條“只生成其中一個”注釋。請考慮將其封裝成一個“獨生子”的形式。若在主程序里有大量散亂的代碼,用于創(chuàng)建自己的對象,請考慮采納一種創(chuàng)造性的方案,將些代碼封裝起來。

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

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

            (28) 請記住,閱讀代碼的時間比寫代碼的時間多得多。思路清晰的設(shè)計可獲得易于理解的程序,但注釋、細致的解釋以及一些示例往往具有不可估量的價值。無論對你自己,還是對后來的人,它們都是相當(dāng)重要的。如對此仍有懷疑,那么請試想自己試圖從聯(lián)機Java文檔里找出有用信息時碰到的挫折,這樣或許能將你說服。

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

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

          ??? (31) JAVA 對枚舉的支持不好,但是下面的代碼是一種很有用的模板:
          class Colour {
          public static final Colour BLACK = new Colour(0, 0, 0);
          public static final Colour RED = new Colour(0xFF, 0, 0);
          public static final Colour GREEN = new Colour(0, 0xFF, 0);
          public static final Colour BLUE = new Colour(0, 0, 0xFF);
          public static final Colour WHITE = new Colour(0xFF, 0xFF, 0xFF);
          }
          這種技術(shù)實現(xiàn)了RED, GREEN, BLUE 等可以象其他語言的枚舉類型一樣使用的常量。 他們可以用 == 操作符來比較。 但是這樣使用有一個缺陷:如果一個用戶用這樣的方法來創(chuàng)建顏色 BLACK new Colour(0,0,0) 那么這就是另外一個對象,==操作符就會產(chǎn)生錯誤。她的 equal() 方法仍然有效。由于這個原因,這個技術(shù)的缺陷最好注明在文檔中,或者只在自己的包中使用。

          posted @ 2006-12-12 11:11 保爾任 閱讀(178) | 評論 (0)編輯 收藏

          按照編譯原理的觀點,程序運行時的內(nèi)存分配有三種策略,分別是靜態(tài)的,棧式的,和堆式的.
          靜態(tài)存儲分配是指在編譯時就能確定每個數(shù)據(jù)目標(biāo)在運行時刻的存儲空間需求,因而在編譯時就可以給他們分配固定的內(nèi)存空間.這種分配策略要求程序代碼中不允許有可變數(shù)據(jù)結(jié)構(gòu)(比如可變數(shù)組)的存在,也不允許有嵌套或者遞歸的結(jié)構(gòu)出現(xiàn),因為它們都會導(dǎo)致編譯程序無法計算準(zhǔn)確的存儲空間需求.
          棧式存儲分配也可稱為動態(tài)存儲分配,是由一個類似于堆棧的運行棧來實現(xiàn)的.和靜態(tài)存儲分配相反,在棧式存儲方案中,程序?qū)?shù)據(jù)區(qū)的需求在編譯時是完全未知的,只有到運行的時候才能夠知道,但是規(guī)定在運行中進入一個程序模塊時,必須知道該程序模塊所需的數(shù)據(jù)區(qū)大小才能夠為其分配內(nèi)存.和我們在數(shù)據(jù)結(jié)構(gòu)所熟知的棧一樣,棧式存儲分配按照先進后出的原則進行分配。
          靜態(tài)存儲分配要求在編譯時能知道所有變量的存儲要求,棧式存儲分配要求在過程的入口處必須知道所有的存儲要求,而堆式存儲分配則專門負責(zé)在編譯時或運行時模塊入口處都無法確定存儲要求的數(shù)據(jù)結(jié)構(gòu)的內(nèi)存分配,比如可變長度串和對象實例.堆由大片的可利用塊或空閑塊組成,堆中的內(nèi)存可以按照任意順序分配和釋放.

          2.2 堆和棧的比較
          上面的定義從編譯原理的教材中總結(jié)而來,除靜態(tài)存儲分配之外,都顯得很呆板和難以理解,下面撇開靜態(tài)存儲分配,集中比較堆和棧:
          從堆和棧的功能和作用來通俗的比較,堆主要用來存放對象的,棧主要是用來執(zhí)行程序的.而這種不同又主要是由于堆和棧的特點決定的:
          在編程中,例如C/C++中,所有的方法調(diào)用都是通過棧來進行的,所有的局部變量,形式參數(shù)都是從棧中分配內(nèi)存空間的。實際上也不是什么分配,只是從棧頂向上用就行,就好像工廠中的傳送帶(conveyor belt)一樣,Stack Pointer會自動指引你到放東西的位置,你所要做的只是把東西放下來就行.退出函數(shù)的時候,修改棧指針就可以把棧中的內(nèi)容銷毀.這樣的模式速度最快, 當(dāng)然要用來運行程序了.需要注意的是,在分配的時候,比如為一個即將要調(diào)用的程序模塊分配數(shù)據(jù)區(qū)時,應(yīng)事先知道這個數(shù)據(jù)區(qū)的大小,也就說是雖然分配是在程序運行時進行的,但是分配的大小多少是確定的,不變的,而這個"大小多少"是在編譯時確定的,不是在運行時.
          堆是應(yīng)用程序在運行的時候請求操作系統(tǒng)分配給自己內(nèi)存,由于從操作系統(tǒng)管理的內(nèi)存分配,所以在分配和銷毀時都要占用時間,因此用堆的效率非常低.但是堆的優(yōu)點在于,編譯器不必知道要從堆里分配多少存儲空間,也不必知道存儲的數(shù)據(jù)要在堆里停留多長的時間,因此,用堆保存數(shù)據(jù)時會得到更大的靈活性。事實上,面向?qū)ο蟮亩鄳B(tài)性,堆內(nèi)存分配是必不可少的,因為多態(tài)變量所需的存儲空間只有在運行時創(chuàng)建了對象之后才能確定.在C++中,要求創(chuàng)建一個對象時,只需用 new命令編制相關(guān)的代碼即可。執(zhí)行這些代碼時,會在堆里自動進行數(shù)據(jù)的保存.當(dāng)然,為達到這種靈活性,必然會付出一定的代價:在堆里分配存儲空間時會花掉更長的時間!這也正是導(dǎo)致我們剛才所說的效率低的原因,看來列寧同志說的好,人的優(yōu)點往往也是人的缺點,人的缺點往往也是人的優(yōu)點(暈~).


          2.3 JVM中的堆和棧
          JVM是基于堆棧的虛擬機.JVM為每個新創(chuàng)建的線程都分配一個堆棧.也就是說,對于一個Java程序來說,它的運行就是通過對堆棧的操作來完成的。堆棧以幀為單位保存線程的狀態(tài)。JVM對堆棧只進行兩種操作:以幀為單位的壓棧和出棧操作。
          我們知道,某個線程正在執(zhí)行的方法稱為此線程的當(dāng)前方法.我們可能不知道,當(dāng)前方法使用的幀稱為當(dāng)前幀。當(dāng)線程激活一個Java方法,JVM就會在線程的 Java堆棧里新壓入一個幀。這個幀自然成為了當(dāng)前幀.在此方法執(zhí)行期間,這個幀將用來保存參數(shù),局部變量,中間計算過程和其他數(shù)據(jù).這個幀在這里和編譯原理中的活動紀(jì)錄的概念是差不多的.
          從Java的這種分配機制來看,堆棧又可以這樣理解:堆棧(Stack)是操作系統(tǒng)在建立某個進程時或者線程(在支持多線程的操作系統(tǒng)中是線程)為這個線程建立的存儲區(qū)域,該區(qū)域具有先進后出的特性。
          每一個Java應(yīng)用都唯一對應(yīng)一個JVM實例,每一個實例唯一對應(yīng)一個堆。應(yīng)用程序在運行中所創(chuàng)建的所有類實例或數(shù)組都放在這個堆中,并由應(yīng)用所有的線程共享.跟C/C++不同,Java中分配堆內(nèi)存是自動初始化的。Java中所有對象的存儲空間都是在堆中分配的,但是這個對象的引用卻是在堆棧中分配,也就是說在建立一個對象時從兩個地方都分配內(nèi)存,在堆中分配的內(nèi)存實際建立這個對象,而在堆棧中分配的內(nèi)存只是一個指向這個堆對象的指針(引用)而已。


          2.4 GC的思考
          Java為什么慢?JVM的存在當(dāng)然是一個原因,但有人說,在Java中,除了簡單類型(int,char等)的數(shù)據(jù)結(jié)構(gòu),其它都是在堆中分配內(nèi)存(所以說Java的一切都是對象),這也是程序慢的原因之一。
          我的想法是(應(yīng)該說代表TIJ的觀點),如果沒有Garbage Collector(GC),上面的說法就是成立的.堆不象棧是連續(xù)的空間,沒有辦法指望堆本身的內(nèi)存分配能夠象堆棧一樣擁有傳送帶般的速度,因為,誰會為你整理龐大的堆空間,讓你幾乎沒有延遲的從堆中獲取新的空間呢?
          這個時候,GC站出來解決問題.我們都知道GC用來清除內(nèi)存垃圾,為堆騰出空間供程序使用,但GC同時也擔(dān)負了另外一個重要的任務(wù),就是要讓Java中堆的內(nèi)存分配和其他語言中堆棧的內(nèi)存分配一樣快,因為速度的問題幾乎是眾口一詞的對Java的詬病.要達到這樣的目的,就必須使堆的分配也能夠做到象傳送帶一樣,不用自己操心去找空閑空間.這樣,GC除了負責(zé)清除Garbage外,還要負責(zé)整理堆中的對象,把它們轉(zhuǎn)移到一個遠離Garbage的純凈空間中無間隔的排列起來,就象堆棧中一樣緊湊,這樣Heap Pointer就可以方便的指向傳送帶的起始位置,或者說一個未使用的空間,為下一個需要分配內(nèi)存的對象"指引方向".因此可以這樣說,垃圾收集影響了對象的創(chuàng)建速度,聽起來很怪,對不對?
          那GC怎樣在堆中找到所有存活的對象呢?前面說了,在建立一個對象時,在堆中分配實際建立這個對象的內(nèi)存,而在堆棧中分配一個指向這個堆對象的指針(引用),那么只要在堆棧(也有可能在靜態(tài)存儲區(qū))找到這個引用,就可以跟蹤到所有存活的對象.找到之后,GC將它們從一個堆的塊中移到另外一個堆的塊中,并將它們一個挨一個的排列起來,就象我們上面說的那樣,模擬出了一個棧的結(jié)構(gòu),但又不是先進后出的分配,而是可以任意分配的,在速度可以保證的情況下, Isn't it great?
          但是,列寧同志說了,人的優(yōu)點往往也是人的缺點,人的缺點往往也是人的優(yōu)點(再暈~~).GC()的運行要占用一個線程,這本身就是一個降低程序運行性能的缺陷,更何況這個線程還要在堆中把內(nèi)存翻來覆去的折騰.不僅如此,如上面所說,堆中存活的對象被搬移了位置,那么所有對這些對象的引用都要重新賦值.這些開銷都會導(dǎo)致性能的降低.
          此消彼長,GC()的優(yōu)點帶來的效益是否蓋過了它的缺點導(dǎo)致的損失,我也沒有太多的體會,Bruce Eckel 是Java的支持者,王婆賣瓜,話不能全信.個人總的感覺是,Java還是很慢,它的發(fā)展還需要時間.


          上面的體會是我看了TIJ.3rdEdition.Revision4.0中第四章之后得出的,內(nèi)容和前面的有些不同.我沒有看過侯捷的中文版本,但我覺得,在關(guān)鍵問題上,原版的TIJ的確更值得一讀.所以和中文版配合起來學(xué)習(xí)是比較不錯的選擇.
          我只能算一個Java的初學(xué)者,沒想到起了這么個題目,卻受到這么多人的關(guān)注,欣喜之余,也決心盡力寫好下面的每一篇.不過這一篇完了,我就該準(zhǔn)備赴美簽證了,如果成功,那就要等到8月27號CS的研究生院開學(xué)之后,才有時間會開始研究下一章了,希望可以多從原版中獲取一點經(jīng)驗.

          posted @ 2006-12-12 11:06 保爾任 閱讀(750) | 評論 (0)編輯 收藏

          數(shù)組和List的轉(zhuǎn)換:

                  List<Integer> l = new ArrayList<Integer>();
                  l.add(1);
                  l.add(2);
                  l.add(3);
                  Integer[] ints = l.toArray(new Integer[0]);
                  List<Integer> l2 = Arrays.asList(ints);
                  assert l.equals(l2);
                  System.out.println("ok");

          數(shù)字類型轉(zhuǎn)換成字符串型:
          String s = String.valueOf(value); 

          數(shù)字對象轉(zhuǎn)換成字符串型
          Long l = new Long(10);
          String s = l.toString();

          數(shù)字類型/字符串轉(zhuǎn)換成數(shù)字對象:
          byte b = 169;
          String b = "169";
          Byte bo = new Byte( b );

          short t = 169;
          String b = "169";
          Short to = new Short( t );

          int i = 169;
          String b = "169";
          Integer io = new Integer( i );

          long l = 169;
          String b = "169";
          Long lo = new Long( l );

          float f = 169f;
          String b = "169";
          Float fo = new Float( f );

          double d = 169f;
          String b = "169";
          Double dObj = new Double( d ); 

          字符串型轉(zhuǎn)換成各種數(shù)字類型:
          String s = "169";
          byte b = Byte.parseByte( s );
          short t = Short.parseShort( s );
          int i = Integer.parseInt( s );
          long l = Long.parseLong( s );
          float f = Float.parseFloat( s );
          double d = Double.parseDouble( s );

          數(shù)字對象轉(zhuǎn)換成數(shù)字類型:(*o為一數(shù)字對象)
          b = bo.byteValue();
          t = to.shortValue();
          i = io.intValue();
          l = lo.longValue();
          f = fo.floatValue();
          d = dObj.doubleValue();

          posted @ 2006-12-12 10:19 保爾任 閱讀(544) | 評論 (0)編輯 收藏

          第一章 一般技術(shù)
          1.java只有唯一一種參數(shù)傳遞方式:by value(值傳遞)。對于primitive types(基本型別)很容易理解,對于object references(對象引用),傳遞的是object reference的拷貝。
          2.polymorphism(多態(tài))優(yōu)于instanceof:instanceof很容易被誤用,很多場合都應(yīng)該以多態(tài)代替,無論何時看到instanceof,請判斷是否可以改進以消除它。
          3.避免創(chuàng)建重復(fù)對象。比如一個類A的某個方法新建了一個類B,且此類B不會改變,則每次建立該類A的一個對象就會新建B的對象,此時應(yīng)把
          B設(shè)為private static final。
          4.清除過期的對象引用。
          5.避免使用終結(jié)函數(shù)。因為終結(jié)函數(shù)可能得不到執(zhí)行或很久后得到執(zhí)行,所以要避免使用。顯示的中止方法通常與try-finally結(jié)構(gòu)結(jié)合使用,防止出現(xiàn)異常時終結(jié)函數(shù)得不到執(zhí)行。
          eg: Foo foo = new Foo(...);
          ??? try{
          ??????? //do what must be done with foo???
          ??? }finally{
          ??????? foo.terminate();
          ??? }
          6.通過私有構(gòu)造函數(shù)來強化不可實例化的能力。比如一些工具類不希望被實例化,然而在缺少顯示構(gòu)造函數(shù)時編譯器會自動提供一個默認(rèn)構(gòu)造函數(shù),為防止以上情況要構(gòu)造一個顯示的私有的構(gòu)造函數(shù)。
          eg:public class UtilityClass{
          ???? private UtilityClass(){
          ???? }
          ?? }
          7.通過私有構(gòu)造函數(shù)強化singleton屬性。singleton是指這樣的類,它只能實例化一次。singleton通常被用來代表那些本質(zhì)上具有唯一性的系統(tǒng)組件,比如視頻顯示或者文件系統(tǒng)。
          ? eg:public class Elvis{
          ?????? public static final Elvis INSTANCE = new Elvis();
          ?????? private Elvis(){
          ?????? }
          ???? }
          8.考慮用靜態(tài)工廠方法代替構(gòu)造函數(shù),但如果沒有其他強烈的因素,最好還是簡單的使用構(gòu)造函數(shù),畢竟它是語言規(guī)范。靜態(tài)工廠方法實際上是一個簡單的靜態(tài)方法,他返回的是類的一個實例。
          ? 有點:a.與構(gòu)造函數(shù)不同,靜態(tài)工廠方法具有名字。
          ??????? b.與構(gòu)造函數(shù)不同,它們每次被調(diào)用的時候不要求非得創(chuàng)建一個對象。
          ??????? c.與構(gòu)造函數(shù)不同,與構(gòu)造函數(shù)不同,它們可以返回一個原類型的子類型對象。
          第二章 所有對象都通用的方法(equals(),hashCode(),toString(),clone(),Comparable接口)
          一.按規(guī)則使用equals():
          1.使用equals的規(guī)則:
          ? a.如果一個class的兩個對象占據(jù)不同的內(nèi)存空間也可被視為邏輯相等的話,那么得為這個class提供一個equals()
          ? b.檢查是否等于this
          ? c.比較關(guān)鍵域以判斷兩個對象是否相等
          ? d.如果有java.lang.Object以外的任何base class實現(xiàn)了equals(),那么就應(yīng)該調(diào)用super.equals()
          ? e.如果只允許同一個class所產(chǎn)生的對象被視為相等,則通常使用getClass()
          ??? eg1:一般情況
          ??? public boolean equals(Object obj){
          ??????? if(this == obj){
          ????????? return true;
          ??????? }
          ??????? if(obj != nul && getClass() == obj.getClass()){
          ????????? Test test = (Test)obj;
          ????????? if(***){//相等條件
          ????????????? return true;
          ????????? }
          ??????? }
          ??????? return false;
          ????? }
          ??? eg2:調(diào)用super.equals()情況
          ??? public boolean equals(Object obj){
          ????? if(super.equals(obj)){//已經(jīng)包含了this == obj; obj !=null && getClass() == obj.getClass()的判斷
          ??????? Test test = (Test)obj;
          ????????? if(***){//相等條件
          ????????????? return true;
          ????????? }
          ??????? }
          ??????? return false;
          ????? }

          ? f.只有在不得不對derived class對象與base classes對象進行比較的場合中,才使用instanceof,并且你應(yīng)該明白這樣做帶來的可能問題和復(fù)雜性,并且derived class和base classes都用instanceof實現(xiàn)equals()時,這種比較不會展現(xiàn)“對稱相等性”。
          ??? Base b;Derived d;//分別表示父類、子類
          ??? 1)父類實現(xiàn)equals,子類繼承父類的equals,b.equals(d) == d.equals(d);
          ??? 2)父類子類分別實現(xiàn)了equals,b.equals(d) != d.equals(b);
          ??? 3)父類未實現(xiàn)equals,子類實現(xiàn)了equals,b.equals(d) != d.equals(b);
          2.對于既不是float也不是double類型的primitive types,使用==操作符;對于對象引用域,可以遞歸的調(diào)用equals方法;對于float域,先使用Float.floatToIntBits轉(zhuǎn)換成int類型值,然后使用==操作符比較int類型的值;對于double域,先使用Double.doubleToLongBits轉(zhuǎn)換成int類型的值,然后使用==操作符比較long類型的值.(這是由于存在Float.NaN、-0.0f以及類似的double類型的常量)
          二.hashCode():
          1。改寫equals時總是要改寫hashCode方法,如果不這樣作,會導(dǎo)致該類無法與所有基于散列值(hash)的集合類在一起正常工作,這樣的集合類包括HashMap、HashSet、HashTable
          2。hashCode方法的簡單方法:
          ? 1。把某個非零數(shù)值(比如17),保存在int result變量里。
          ? 2。對于對象中每一個關(guān)鍵域f(指equals方法中考慮的每一個域),完成以下步驟:
          ? a)為該域計算int類型的散列碼c:
          ??? i.該域為boolean型,c = f ? 0 : 1
          ??? ii.byte, char, short, int型, c = (int)f
          ??? iii.long型, c = (int)(f ^ (f >>> 32))
          ??? iv.float型, c = Float.floatToIntBits(f)
          ??? v.double型, Double.doubleToLongBits(f)得到long型,然后按iii計算散列值
          ??? vi.如果是對象引用,c = (this.*** == null) ? 0 : this.***.hashCode();
          ??? vii.如果該域是個數(shù)組,則把其中每一個元素當(dāng)作單獨的域來處理
          ?? b)result = 37 * result + c;//把每個c都組合到result中
          ?? 3。返回result
          ?? eg1:
          ?public int hashCode() {
          ???? int result = 17;
          ???? //對于關(guān)鍵域是id的情況
          ???? int idValue = (this.getId() == null) ? 0 : this.getId().hashCode();
          ???? result = (result * 37) + idValue;
          ???? //如果還有第二個關(guān)鍵域name
          ???? //int nameValue = (this.getName() == null) ? 0 : this.getName().hashCode();
          ???? //result = (result * 37) + nameValue;
          ???? this.hashValue = result;
          ?return this.hashValue;
          ?}
          ??? eg2:
          ?如果一個類是非可變的,并且計算散列碼代價較大,則應(yīng)把散列碼存到對象內(nèi)部:
          ?private int hashValue = 17;//先定義hashValue,不需要get/set方法
          ?........................
          ?public int hashCode() {//對于關(guān)鍵域是id的情況
          ???? if (this.hashValue == 17) {
          ??int result = 17;
          ??int idValue = (this.getId() == null) ? 0 : this.getId().hashCode();
          ??result = (result * 37) + idValue;
          ??//如果還有第二個關(guān)鍵域name
          ??//int nameValue = (this.getName() == null) ? 0 : this.getName().hashCode();
          ??//result = (result * 37) + nameValue;
          ??this.hashValue = result;
          ???? }
          ???? return this.hashValue;
          ?}
          三。toString():會使這個類用起來更加方便。
          四。謹(jǐn)慎的改寫clone()。實現(xiàn)拷貝的方法有兩個:一是實現(xiàn)cloneable接口(effective java 39頁,沒仔細看),二是提供拷貝構(gòu)造函數(shù)
          ? public Yum(Yum yum);
          ? 或是上面的微小變形:提供一個靜態(tài)工廠來代替構(gòu)造函數(shù):
          ? public static Yum newInstance(Yum yum);
          五、用到搜索、排序、計算極值的情況時,考慮實現(xiàn)Comparable接口。
          public int compareTo(Object o)//方法不需要手工檢查參數(shù)的類型,如參數(shù)類型不符合會拋出ClassCastException;如參數(shù)為null,該方法拋出NullPointerException。
          第三章 類和接口
          1。使類和成員(變量、方法、內(nèi)部類、內(nèi)部接口)的可訪問能力最小化。
          2。private和friendly成員都是一個類實現(xiàn)中的一部分,并不會影響到導(dǎo)出API。然而,如果這些域所在的類實現(xiàn)了Serializable接口,那么這些成員可能會被泄漏到導(dǎo)出API中。
          3。如果一個方法改寫了超類中的一個方法,那么子類中該方法的訪問級別不能低于父類中該方法的訪問級別。特別是:類實現(xiàn)了接口,那么接口中的方法在這個類中必須聲明為公有的,因為接口中方法默認(rèn)為public abstract。
          六、異常處理
          1.決不可忽略異常,即catch后什么也不做。
          2.決不可掩蓋異常
          try{
          ? e1;//異常1
          ? e2;//異常2
          }catch(Exception e){
          ? e.printStackTrace()
          }//只能捕獲異常2
          辦法:要仔細分析,用棧來保存異常
          3.覆寫異常處理時:
          父類不拋出異常時,自類不能拋出異常。
          父類拋出異常時,自類三種情況:a)不拋出異常b)拋出父類異常c)拋出父類異常的派生異常。
          4.只要有finally塊就一定會進入,即使try-catch塊有return/break/continue語句。
          5.養(yǎng)成將try/catch塊放在循環(huán)外的習(xí)慣,在不啟動JIT時節(jié)省時間。

          posted @ 2006-12-05 16:16 保爾任 閱讀(174) | 評論 (0)編輯 收藏

          1。自然數(shù)是0,1,2……
          2。素數(shù)是2,3,5……(不包括1的只能背1和它本身整除的自然數(shù))

          import java.util.Scanner;

          public class Prime {

              //最基本的做法

              private int prime1(int num) {
                  int i = 0, s = 0;
                  label1: for (int n = 2; n <= num; n++) {
                      for (int m = 2; m * m <= n; m++) {
                          if (n % m == 0)
                              continue label1;
                      }
                      s++;
                      i++;
                      //System.out.println("第" + i + "個素數(shù)是:" + n);
                  }
                  return s;
              }

              //6N±1法

              private int prime2(int num){
                  int i = 0, s = 0;
                  for(int n = 2; n <=3; n ++){
                      s++;
                      i++;
                      //System.out.println("第" + i + "個素數(shù)是:" + n);
                  }
                  label1: for(int n = 1; ; n++) {
                      label2: for (int m = 0; m <= 1; m++) {
                          int tmp = 2 * (3 * n + m) - 1;
                          if (tmp > num)
                              break label1;
                          for(int k = 2; k * k <= tmp; k++)
                              if (tmp % k == 0)
                                  if (m == 0)
                                      continue label2;
                                  else
                                      continue label1;
                          s++;
                          i++;
                          //System.out.println("第" + i + "個素數(shù)是:" + tmp);
                      }
                  }
                  return s;
              }

              public static void main(String args[]) {
                  Scanner in = new Scanner(System.in);
                  int num = in.nextInt();
                  long start = System.currentTimeMillis();
                  int sum = new Prime().prime1(num);
                  long end = System.currentTimeMillis();
                  System.out.println("方法一共" + sum + "個素數(shù)");
                  System.out.println("用時:" + (end - start));
                  start = System.currentTimeMillis();
                  sum = new Prime().prime2(num);
                  end = System.currentTimeMillis();
                  System.out.println("方法二共" + sum + "個素數(shù)");
                  System.out.println("用時:" + (end - start));
                 
              }
          }

          輸入:1000000

          運行結(jié)果:

          方法一共78498個素數(shù)
          用時:3434
          方法二共78498個素數(shù)
          用時:3453

          (看來基本方法比6N±1法還要更快些,奇怪了,我的程序?qū)懙臎]什么問題阿)


          【1】求10000以內(nèi)的所有素數(shù)。
          素數(shù)是除了1和它本身之外再不能被其他數(shù)整除的自然數(shù)。由于找不到一個通項公式來表示所有的素數(shù),所以對于數(shù)學(xué)家來說,素數(shù)一直是一個未解之謎。像著名的 哥德巴赫猜想、孿生素數(shù)猜想,幾百年來不知吸引了世界上多少優(yōu)秀的數(shù)學(xué)家。盡管他們苦心鉆研,嘔心瀝血,但至今仍然未見分曉。
          自從有了計算機之后,人們借助于計算機的威力,已經(jīng)找到了2216091以內(nèi)的所有素數(shù)。
          求素數(shù)的方法有很多種,最簡單的方法是根據(jù)素數(shù)的定義來求。對于一個自然數(shù)N,用大于1小于N的各個自然數(shù)都去除一下N,如果都除不盡,則N為素數(shù),否則N為合數(shù)。
          但是,如果用素數(shù)定義的方法來編制計算機程序,它的效率一定是非常低的,其中有許多地方都值得改進。
          第一,對于一個自然數(shù)N,只要能被一個非1非自身的數(shù)整除,它就肯定不是素數(shù),所以不
          必再用其他的數(shù)去除。
          第二,對于N來說,只需用小于N的素數(shù)去除就可以了。例如,如果N能被15整除,實際
          上就能被3和5整除,如果N不能被3和5整除,那么N也決不會被15整除。
          第三,對于N來說,不必用從2到N一1的所有素數(shù)去除,只需用小于等于√N(根號N)的所有素數(shù)去除就可以了。這一點可以用反證法來證明:
          如果N是合數(shù),則一定存在大于1小于N的整數(shù)d1和d2,使得N=d1×d2。
          如果d1和d2均大于√N,則有:N=d1×d2>√N×√N=N。
          而這是不可能的,所以,d1和d2中必有一個小于或等于√N。
          基于上述分析,設(shè)計算法如下:
          (1)用2,3,5,7逐個試除N的方法求出100以內(nèi)的所有素數(shù)。
          (2)用100以內(nèi)的所有素數(shù)逐個試除的方法求出10000以內(nèi)的素數(shù)。
          首先,將2,3,5,7分別存放在a[1]、a[2]、a[3]、a[4]中,以后每求出一個素數(shù),只要不大于100,就依次存放在A數(shù)組中的一個單元 中。當(dāng)我們求100—10000之間的素數(shù)時,可依次用a[1]-a[2]的素數(shù)去試除N,這個范圍內(nèi)的素數(shù)可以不保存,直接打印。

          【2】用篩法求素數(shù)。
          簡單介紹一下厄拉多塞篩法。厄拉多塞是一位古希臘數(shù)學(xué)家,他在尋找素數(shù)時,采用了一種與眾不同的方法:先將2-N的各數(shù)寫在紙上:

          在2的上面畫一個圓圈,然后劃去2的其他倍數(shù);第一個既未畫圈又沒有被劃去的數(shù)是3,將它畫圈,再劃去3的其他倍數(shù);現(xiàn)在既未畫圈又沒有被劃去的第一個數(shù) 是5,將它畫圈,并劃去5的其他倍數(shù)……依次類推,一直到所有小于或等于N的各數(shù)都畫了圈或劃去為止。這時,表中畫了圈的以及未劃去的那些數(shù)正好就是小于 N的素數(shù)。

          這很像一面篩子,把滿足條件的數(shù)留下來,把不滿足條件的數(shù)篩掉。由于這種方法是厄拉多塞首先發(fā)明的,所以,后人就把這種方法稱作厄拉多塞篩法。
          在計算機中,篩法可以用給數(shù)組單元置零的方法來實現(xiàn)。具體來說就是:首先開一個數(shù)組:a[i],i=1,2,3,…,同時,令所有的數(shù)組元素都等于下標(biāo) 值,即a[i]=i,當(dāng)i不是素數(shù)時,令a[i]=0 。當(dāng)輸出結(jié)果時,只要判斷a[i]是否等于零即可,如果a[i]=0,則令i=i+1,檢查下一個a[i]。
          篩法是計算機程序設(shè)計中常用的算法之一。

          【3】用6N±1法求素數(shù)。
          任何一個自然數(shù),總可以表示成為如下的形式之一:
          6N,6N+1,6N+2,6N+3,6N+4,6N+5 (N=0,1,2,…)
          顯然,當(dāng)N≥1時,6N,6N+2,6N+3,6N+4都不是素數(shù),只有形如6N+1和6N+5的自然數(shù)有可能是素數(shù)。所以,除了2和3之外,所有的素數(shù)都可以表示成6N±1的形式(N為自然數(shù))。
          根據(jù)上述分析,我們可以構(gòu)造另一面篩子,只對形如6 N±1的自然數(shù)進行篩選,這樣就可以大大減少篩選的次數(shù),從而進一步提高程序的運行效率和速度。

          在程序上,我們可以用一個二重循環(huán)實現(xiàn)這一點,外循環(huán)i按3的倍數(shù)遞增,內(nèi)循環(huán)j為0-1的循環(huán),則2(i+j)-1恰好就是形如6N±1的自然數(shù)。

          posted @ 2006-11-20 15:28 保爾任 閱讀(3217) | 評論 (6)編輯 收藏
          ?

          插入排序:

          package org.rut.util.algorithm.support;

          import org.rut.util.algorithm.SortUtil;
          /**
          ?* @author treeroot
          ?* @since 2006-2-2
          ?* @version 1.0
          ?*/
          public class InsertSort implements SortUtil.Sort{

          ??? /* (non-Javadoc)
          ???? * @see org.rut.util.algorithm.SortUtil.Sort#sort(int[])
          ???? */
          ??? public void sort(int[] data) {
          ??????? int temp;
          ??????? for(int i=1;i<data.length;i++){
          ??????????? for(int j=i;(j>0)&&(data[j]<data[j-1]);j--){
          ??????????????? SortUtil.swap(data,j,j-1);
          ??????????? }
          ??????? }???????
          ??? }

          }
          冒泡排序:

          package org.rut.util.algorithm.support;

          import org.rut.util.algorithm.SortUtil;

          /**
          ?* @author treeroot
          ?* @since 2006-2-2
          ?* @version 1.0
          ?*/
          public class BubbleSort implements SortUtil.Sort{

          ??? /* (non-Javadoc)
          ???? * @see org.rut.util.algorithm.SortUtil.Sort#sort(int[])
          ???? */
          ??? public void sort(int[] data) {
          ??????? int temp;
          ??????? for(int i=0;i<data.length;i++){
          ??????????? for(int j=data.length-1;j>i;j--){
          ??????????????? if(data[j]<data[j-1]){
          ??????????????????? SortUtil.swap(data,j,j-1);
          ??????????????? }
          ??????????? }
          ??????? }
          ??? }

          }

          選擇排序:

          package org.rut.util.algorithm.support;

          import org.rut.util.algorithm.SortUtil;

          /**
          ?* @author treeroot
          ?* @since 2006-2-2
          ?* @version 1.0
          ?*/
          public class SelectionSort implements SortUtil.Sort {

          ??? /*
          ???? * (non-Javadoc)
          ???? *
          ???? * @see org.rut.util.algorithm.SortUtil.Sort#sort(int[])
          ???? */
          ??? public void sort(int[] data) {
          ??????? int temp;
          ??????? for (int i = 0; i < data.length; i++) {
          ??????????? int lowIndex = i;
          ??????????? for (int j = data.length - 1; j > i; j--) {
          ??????????????? if (data[j] < data[lowIndex]) {
          ??????????????????? lowIndex = j;
          ??????????????? }
          ??????????? }
          ??????????? SortUtil.swap(data,i,lowIndex);
          ??????? }
          ??? }

          }

          Shell排序:

          package org.rut.util.algorithm.support;

          import org.rut.util.algorithm.SortUtil;

          /**
          ?* @author treeroot
          ?* @since 2006-2-2
          ?* @version 1.0
          ?*/
          public class ShellSort implements SortUtil.Sort{

          ??? /* (non-Javadoc)
          ???? * @see org.rut.util.algorithm.SortUtil.Sort#sort(int[])
          ???? */
          ??? public void sort(int[] data) {
          ??????? for(int i=data.length/2;i>2;i/=2){
          ??????????? for(int j=0;j<i;j++){
          ??????????????? insertSort(data,j,i);
          ??????????? }
          ??????? }
          ??????? insertSort(data,0,1);
          ??? }

          ??? /**
          ???? * @param data
          ???? * @param j
          ???? * @param i
          ???? */
          ??? private void insertSort(int[] data, int start, int inc) {
          ??????? int temp;
          ??????? for(int i=start+inc;i<data.length;i+=inc){
          ??????????? for(int j=i;(j>=inc)&&(data[j]<data[j-inc]);j-=inc){
          ??????????????? SortUtil.swap(data,j,j-inc);
          ??????????? }
          ??????? }
          ??? }

          }

          快速排序:

          package org.rut.util.algorithm.support;

          import org.rut.util.algorithm.SortUtil;

          /**
          ?* @author treeroot
          ?* @since 2006-2-2
          ?* @version 1.0
          ?*/
          public class QuickSort implements SortUtil.Sort{

          ??? /* (non-Javadoc)
          ???? * @see org.rut.util.algorithm.SortUtil.Sort#sort(int[])
          ???? */
          ??? public void sort(int[] data) {
          ??????? quickSort(data,0,data.length-1);???????
          ??? }
          ??? private void quickSort(int[] data,int i,int j){
          ??????? int pivotIndex=(i+j)/2;
          ??????? //swap
          ??????? SortUtil.swap(data,pivotIndex,j);
          ???????
          ??????? int k=partition(data,i-1,j,data[j]);
          ??????? SortUtil.swap(data,k,j);
          ??????? if((k-i)>1) quickSort(data,i,k-1);
          ??????? if((j-k)>1) quickSort(data,k+1,j);
          ???????
          ??? }
          ??? /**
          ???? * @param data
          ???? * @param i
          ???? * @param j
          ???? * @return
          ???? */
          ??? private int partition(int[] data, int l, int r,int pivot) {
          ??????? do{
          ?????????? while(data[++l]<pivot);
          ?????????? while((r!=0)&&data[--r]>pivot);
          ?????????? SortUtil.swap(data,l,r);
          ??????? }
          ??????? while(l<r);
          ??????? SortUtil.swap(data,l,r);???????
          ??????? return l;
          ??? }

          }
          改進后的快速排序:

          package org.rut.util.algorithm.support;

          import org.rut.util.algorithm.SortUtil;

          /**
          ?* @author treeroot
          ?* @since 2006-2-2
          ?* @version 1.0
          ?*/
          public class ImprovedQuickSort implements SortUtil.Sort {

          ??? private static int MAX_STACK_SIZE=4096;
          ??? private static int THRESHOLD=10;
          ??? /* (non-Javadoc)
          ???? * @see org.rut.util.algorithm.SortUtil.Sort#sort(int[])
          ???? */
          ??? public void sort(int[] data) {
          ??????? int[] stack=new int[MAX_STACK_SIZE];
          ???????
          ??????? int top=-1;
          ??????? int pivot;
          ??????? int pivotIndex,l,r;
          ???????
          ??????? stack[++top]=0;
          ??????? stack[++top]=data.length-1;
          ???????
          ??????? while(top>0){
          ??????????? int j=stack[top--];
          ??????????? int i=stack[top--];
          ???????????
          ??????????? pivotIndex=(i+j)/2;
          ??????????? pivot=data[pivotIndex];
          ???????????
          ??????????? SortUtil.swap(data,pivotIndex,j);
          ???????????
          ??????????? //partition
          ??????????? l=i-1;
          ??????????? r=j;
          ??????????? do{
          ??????????????? while(data[++l]<pivot);
          ??????????????? while((r!=0)&&(data[--r]>pivot));
          ??????????????? SortUtil.swap(data,l,r);
          ??????????? }
          ??????????? while(l<r);
          ??????????? SortUtil.swap(data,l,r);
          ??????????? SortUtil.swap(data,l,j);
          ???????????
          ??????????? if((l-i)>THRESHOLD){
          ??????????????? stack[++top]=i;
          ??????????????? stack[++top]=l-1;
          ??????????? }
          ??????????? if((j-l)>THRESHOLD){
          ??????????????? stack[++top]=l+1;
          ??????????????? stack[++top]=j;
          ??????????? }
          ???????????
          ??????? }
          ??????? //new InsertSort().sort(data);
          ??????? insertSort(data);
          ??? }
          ??? /**
          ???? * @param data
          ???? */
          ??? private void insertSort(int[] data) {
          ??????? int temp;
          ??????? for(int i=1;i<data.length;i++){
          ??????????? for(int j=i;(j>0)&&(data[j]<data[j-1]);j--){
          ??????????????? SortUtil.swap(data,j,j-1);
          ??????????? }
          ??????? }??????
          ??? }

          }

          歸并排序:

          package org.rut.util.algorithm.support;

          import org.rut.util.algorithm.SortUtil;

          /**
          ?* @author treeroot
          ?* @since 2006-2-2
          ?* @version 1.0
          ?*/
          public class MergeSort implements SortUtil.Sort{

          ??? /* (non-Javadoc)
          ???? * @see org.rut.util.algorithm.SortUtil.Sort#sort(int[])
          ???? */
          ??? public void sort(int[] data) {
          ??????? int[] temp=new int[data.length];
          ??????? mergeSort(data,temp,0,data.length-1);
          ??? }
          ???
          ??? private void mergeSort(int[] data,int[] temp,int l,int r){
          ??????? int mid=(l+r)/2;
          ??????? if(l==r) return ;
          ??????? mergeSort(data,temp,l,mid);
          ??????? mergeSort(data,temp,mid+1,r);
          ??????? for(int i=l;i<=r;i++){
          ??????????? temp[i]=data[i];
          ??????? }
          ??????? int i1=l;
          ??????? int i2=mid+1;
          ??????? for(int cur=l;cur<=r;cur++){
          ??????????? if(i1==mid+1)
          ??????????????? data[cur]=temp[i2++];
          ??????????? else if(i2>r)
          ??????????????? data[cur]=temp[i1++];
          ??????????? else if(temp[i1]<temp[i2])
          ??????????????? data[cur]=temp[i1++];
          ??????????? else
          ??????????????? data[cur]=temp[i2++];???????????
          ??????? }
          ??? }

          }

          改進后的歸并排序:

          package org.rut.util.algorithm.support;

          import org.rut.util.algorithm.SortUtil;

          /**
          ?* @author treeroot
          ?* @since 2006-2-2
          ?* @version 1.0
          ?*/
          public class ImprovedMergeSort implements SortUtil.Sort {

          ??? private static final int THRESHOLD = 10;

          ??? /*
          ???? * (non-Javadoc)
          ???? *
          ???? * @see org.rut.util.algorithm.SortUtil.Sort#sort(int[])
          ???? */
          ??? public void sort(int[] data) {
          ??????? int[] temp=new int[data.length];
          ??????? mergeSort(data,temp,0,data.length-1);
          ??? }

          ??? private void mergeSort(int[] data, int[] temp, int l, int r) {
          ??????? int i, j, k;
          ??????? int mid = (l + r) / 2;
          ??????? if (l == r)
          ??????????? return;
          ??????? if ((mid - l) >= THRESHOLD)
          ??????????? mergeSort(data, temp, l, mid);
          ??????? else
          ??????????? insertSort(data, l, mid - l + 1);
          ??????? if ((r - mid) > THRESHOLD)
          ??????????? mergeSort(data, temp, mid + 1, r);
          ??????? else
          ??????????? insertSort(data, mid + 1, r - mid);

          ??????? for (i = l; i <= mid; i++) {
          ??????????? temp[i] = data[i];
          ??????? }
          ??????? for (j = 1; j <= r - mid; j++) {
          ??????????? temp[r - j + 1] = data[j + mid];
          ??????? }
          ??????? int a = temp[l];
          ??????? int b = temp[r];
          ??????? for (i = l, j = r, k = l; k <= r; k++) {
          ??????????? if (a < b) {
          ??????????????? data[k] = temp[i++];
          ??????????????? a = temp[i];
          ??????????? } else {
          ??????????????? data[k] = temp[j--];
          ??????????????? b = temp[j];
          ??????????? }
          ??????? }
          ??? }

          ??? /**
          ???? * @param data
          ???? * @param l
          ???? * @param i
          ???? */
          ??? private void insertSort(int[] data, int start, int len) {
          ??????? for(int i=start+1;i<start+len;i++){
          ??????????? for(int j=i;(j>start) && data[j]<data[j-1];j--){
          ??????????????? SortUtil.swap(data,j,j-1);
          ??????????? }
          ??????? }
          ??? }

          }
          堆排序:

          package org.rut.util.algorithm.support;

          import org.rut.util.algorithm.SortUtil;

          /**
          ?* @author treeroot
          ?* @since 2006-2-2
          ?* @version 1.0
          ?*/
          public class HeapSort implements SortUtil.Sort{

          ??? /* (non-Javadoc)
          ???? * @see org.rut.util.algorithm.SortUtil.Sort#sort(int[])
          ???? */
          ??? public void sort(int[] data) {
          ??????? MaxHeap h=new MaxHeap();
          ??????? h.init(data);
          ??????? for(int i=0;i<data.length;i++)
          ??????????? h.remove();
          ??????? System.arraycopy(h.queue,1,data,0,data.length);
          ??? }


          ???? private static class MaxHeap{
          ????????
          ???????
          ??????? void init(int[] data){
          ??????????? this.queue=new int[data.length+1];
          ??????????? for(int i=0;i<data.length;i++){
          ??????????????? queue[++size]=data[i];
          ??????????????? fixUp(size);
          ??????????? }
          ??????? }
          ????????
          ??????? private int size=0;

          ??????? private int[] queue;
          ???????????????
          ??????? public int get() {
          ??????????? return queue[1];
          ??????? }

          ??????? public void remove() {
          ??????????? SortUtil.swap(queue,1,size--);
          ??????????? fixDown(1);
          ??????? }
          ??????? //fixdown
          ??????? private void fixDown(int k) {
          ??????????? int j;
          ??????????? while ((j = k << 1) <= size) {
          ??????????????? if (j < size && queue[j]<queue[j+1])
          ??????????????????? j++;
          ??????????????? if (queue[k]>queue[j]) //不用交換
          ??????????????????? break;
          ??????????????? SortUtil.swap(queue,j,k);
          ??????????????? k = j;
          ??????????? }
          ??????? }
          ??????? private void fixUp(int k) {
          ??????????? while (k > 1) {
          ??????????????? int j = k >> 1;
          ??????????????? if (queue[j]>queue[k])
          ??????????????????? break;
          ??????????????? SortUtil.swap(queue,j,k);
          ??????????????? k = j;
          ??????????? }
          ??????? }

          ??? }

          }

          ?

          SortUtil:

          package org.rut.util.algorithm;

          import org.rut.util.algorithm.support.BubbleSort;
          import org.rut.util.algorithm.support.HeapSort;
          import org.rut.util.algorithm.support.ImprovedMergeSort;
          import org.rut.util.algorithm.support.ImprovedQuickSort;
          import org.rut.util.algorithm.support.InsertSort;
          import org.rut.util.algorithm.support.MergeSort;
          import org.rut.util.algorithm.support.QuickSort;
          import org.rut.util.algorithm.support.SelectionSort;
          import org.rut.util.algorithm.support.ShellSort;

          /**
          ?* @author treeroot
          ?* @since 2006-2-2
          ?* @version 1.0
          ?*/
          public class SortUtil {
          ??? public final static int INSERT = 1;

          ??? public final static int BUBBLE = 2;

          ??? public final static int SELECTION = 3;

          ??? public final static int SHELL = 4;

          ??? public final static int QUICK = 5;

          ??? public final static int IMPROVED_QUICK = 6;

          ??? public final static int MERGE = 7;

          ??? public final static int IMPROVED_MERGE = 8;

          ??? public final static int HEAP = 9;

          ??? public static void sort(int[] data) {
          ??????? sort(data, IMPROVED_QUICK);
          ??? }
          ??? private static String[] name={
          ??????????? "insert","bubble","selection","shell","quick","improved_quick","merge","improved_merge","heap"
          ??? };
          ???
          ??? private static Sort[] impl=new Sort[]{
          ??????????? new InsertSort(),
          ??????????? new BubbleSort(),
          ??????????? new SelectionSort(),
          ??????????? new ShellSort(),
          ??????????? new QuickSort(),
          ??????????? new ImprovedQuickSort(),
          ??????????? new MergeSort(),
          ??????????? new ImprovedMergeSort(),
          ??????????? new HeapSort()
          ??? };

          ??? public static String toString(int algorithm){
          ??????? return name[algorithm-1];
          ??? }
          ???
          ??? public static void sort(int[] data, int algorithm) {
          ??????? impl[algorithm-1].sort(data);
          ??? }

          ??? public static interface Sort {
          ??????? public void sort(int[] data);
          ??? }

          ??? public static void swap(int[] data, int i, int j) {
          ??????? int temp = data[i];
          ??????? data[i] = data[j];
          ??????? data[j] = temp;
          ??? }
          }

          posted @ 2006-11-20 15:27 保爾任 閱讀(196) | 評論 (0)編輯 收藏

          1、 運行tomcat后打開網(wǎng)頁,瀏覽器顯示如下錯誤:

          Unable to find a javac compiler;

          com.sun.tools.javac.Main is not on the classpath.

          Perhaps JAVA_HOME does not point to the JDK

          首先,你必需檢查一下自己的環(huán)境變量是不是正確;這個我想大家都會,只是有時候重裝JDK而忘了改,不過檢查一下看看就行了。

          其次:在JDK的lib目錄下有一個tools.jar文件,你把它拷到Tomcat安裝目錄下的common\lib目錄下,或者在tomcat下作如下修改,效果一樣

          o_tomcat5.0.28

          最后:如果不可以,在打開tomcat的configue tomcatg ,找到j(luò)ava,在java optioons里填上:-Djava.home=C:\Program Files\Java\jdk1.5.0_04;就好了。

          posted @ 2006-11-16 11:00 保爾任 閱讀(127) | 評論 (0)編輯 收藏
          (轉(zhuǎn)自http://leaf.jdk.cn/article.asp?id=39)
          ??? 雖然項目全部采用了UTF-8編碼,所有的源文件*.java,*.jsc,*.html,*.ftl都采用了UTF-8編碼。可是還是出現(xiàn)了亂碼問題。很是不爽,后來找到了tomcat,和resin的配置。
          1. Tomcat的配置。(conf/server.xml)
            ????<!--?Define?a?non-SSL?HTTP/1.1?Connector?on?port?8080?-->
            ????
            <Connector?port="80"?maxHttpHeaderSize="8192"
            ???????????????maxThreads
            ="150"?minSpareThreads="25"?maxSpareThreads="75"
            ???????????????enableLookups
            ="false"?redirectPort="8443"?acceptCount="100"
            ???????????????connectionTimeout
            ="20000"?disableUploadTimeout="true"?URIEncoding="UTF-8"/>
          2. Resin的配置。(conf/resin.conf)

            character-encoding

            Resin 1.1
            child of: resin, server, host-default, host, web-app-default, web-app
            default: The default value is ISO-8859-1.

            Specifies the default character encoding for the environment.

            <web-app id='/'>
            <character-encoding>shift_jis</character-encoding>
            ...
            </web-app>

          ???? 這個是resin doc里面的我是在web-app-default里面加上了encoding的配置

          ????<web-app-default>
          ??????
          <character-encoding>UTF-8</character-encoding>
          ??????
          ????
          </web-app-default>

          希望對你的項目有幫助。

          posted @ 2006-11-16 10:59 保爾任 閱讀(222) | 評論 (0)編輯 收藏
          僅列出標(biāo)題
          共8頁: 上一頁 1 2 3 4 5 6 7 8 下一頁 

          <2025年5月>
          27282930123
          45678910
          11121314151617
          18192021222324
          25262728293031
          1234567

          常用鏈接

          留言簿(4)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 诏安县| 徐汇区| 思茅市| 鄯善县| 内黄县| 双城市| 精河县| 白山市| 宝应县| 恭城| 双柏县| 鹤庆县| 武汉市| 湖北省| 色达县| 高邮市| 务川| 蒙阴县| 和顺县| 驻马店市| 桓仁| 沈阳市| 夹江县| 灵武市| 绍兴市| 太和县| 大新县| 拉萨市| 化隆| 库伦旗| 锡林郭勒盟| 长子县| 法库县| 顺平县| 乌兰察布市| 潜江市| 建瓯市| 繁峙县| 堆龙德庆县| 叶城县| 宁明县|