<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    csusky

    常用鏈接

    統計

    最新評論

    2008年2月18日 #

    異步IO的關閉事件

    JAVA SOCKET只定義了四種事件

    public static final int OP_READ = 1 << 0;
    public static final int OP_WRITE = 1 << 2;
    public static final int OP_CONNECT = 1 << 3;
    public static final int OP_ACCEPT = 1 << 4;

    是沒有關閉事件的,我們怎么判斷一個連接是否關閉呢?
    如果你的selector注冊了一個OP_READ事件,那么在連接關閉的時候將會產生一個OP_READ事件
    也就是說本來阻塞的selector此時將會被喚醒,但是如果試圖在此事件的通道中讀取數據將會返回-1
    如下:

    Set<SelectionKey> readyKeys = selector.selectedKeys();

    = readyKeys.iterator()

    SelectionKey key 
    = (SelectionKey)i.next();

    if (operation == SelectionKey.OP_READ &&
                             key.isReadable())
                    
    {
                        ReadableByteChannel incomingChannel 
    = (ReadableByteChannel)key.channel(); 
    //此時將會得到-1,表明該鏈接已關閉
    int n = incomingChannel.read(readBuffer);
    }
    此時我們需要取消該KEY 如下:
    if (n == -1)
                
    {
                    key.cancel();
                      //關閉輸入輸出 
                      sc.socket().shutdownOutput();
                      sc.socket().shutdownInput();
                       //關閉SOCKET
                       sc.socket().close();
                      //關閉通道
                       incomingChannel.close();
                }

    posted @ 2009-11-10 22:28 曉宇 閱讀(424) | 評論 (1)編輯 收藏

    ExecutorFilter

    1 . 用Executors構造一個新的線程池
    ExecutorService executor = Executors.newCachedThreadPool();

    方法 newCachedThreadPool();
    創建一個可根據需要創建新線程的線程池,但是在以前構造的線程可用時將重用它們,并在需要時使用提供的 ThreadFactory 創建新線程。
    2. 用構造的線程池創建ExecutorFilter
    ExecutorFilter es= new ExecutorFilter(executor));

    在ExecutorFilter內部:
    只需要將相應的事件分發到到線程池的相應線程即可,但是SessionCreated事件只能在主線程中,不能分發
    觸發方法
    1 .
    首先構造一個IoFilterEvent,這個IoFilterEvent包含1、事件的類型,2、下一個過濾器
    然后觸發該時間的處理方法。
     if (eventTypes.contains(IoEventType.SESSION_OPENED)) {
                fireEvent(
    new IoFilterEvent(nextFilter, IoEventType.SESSION_OPENED,
                        session, 
    null));
            }

    2 .
    從線程池中取出一個線程執行事件處理
    protected void fireEvent(IoFilterEvent event) {
            getExecutor().execute(event);
        }


    在構造ExecutorFilter 時如果沒有傳入IoEventType則默認只對如下幾種幾件感興趣
    EXCEPTION_CAUGHT
    MESSAGE_RECEIVED
    MESSAGE_SENT
    SESSION_CLOSED
    SESSION_IDLE
    SESSION_OPENED
    當然還需要覆蓋相應的事件處理方法 如上所示

    posted @ 2008-12-12 11:33 曉宇 閱讀(1557) | 評論 (0)編輯 收藏

    ORACLE的塊大小

    參數db_block_size;
    這個參數只能設置成底層操作系統物理塊大小的整數倍,最好是2的n次方倍。
    如WINDOWS下4KB,8KB,16KB
    且該參數需要在建庫的時候指定,一旦指定不能更改。
    雖然在ORACLE9I以上可以指定表空間的數據庫大小,允許同時使用包括非默認大小在內的數據庫塊大小。不過需要設置指定大小數據塊的buffer_cache.

    小的塊:
    小的塊降低塊競爭,因為每個塊中的行較少.
    小的塊對于小的行有益.
    小的塊對于隨意的訪問較好.如果一個塊不太可能在讀入內存后被修改,那么塊的大小越小使用buffer cache越有效率。當內存資源很珍貴時尤為重要,因為數據庫的buffer cache是被限制大小的。
    劣勢:
    小塊的管理消費相對大.
    因為行的大小你可能只在塊中存儲很小數目的行,這可能導致額外的I/O。
    小塊可能導致更多的索引塊被讀取

    大的塊
    好處:
    更少的管理消費和更多存儲數據的空間.
    大塊對于有順序的讀取較好.  譬如說全表掃描
    大塊對很大的行較好
    大塊改進了索引讀取的性能.大的塊可以在一個塊中容納更多的索引條目,降低了大的索引級的數量.越少的index level意味著在遍歷索引分支的時候越少的I/O。
    劣勢:
    大塊不適合在OLTP中用作索引塊,因為它們增加了在索引葉塊上的塊競爭。
    如果你是隨意的訪問小的行并有大的塊,buffer cache就被浪費了。例如,8 KB的block size 和50 byte row size,你浪費了7,950



     

    posted @ 2008-11-25 15:45 曉宇 閱讀(1767) | 評論 (0)編輯 收藏

    TIPS

    將進酒  杯莫停  -------> 亭名:  悲默亭

    全球通史

    《詩經·采薇》

    昔我往矣,楊柳依依 今我來思,雨雪霏霏

    posted @ 2008-11-10 16:31 曉宇 閱讀(185) | 評論 (0)編輯 收藏

    SPRING整合IBMMQ實現全局事物

         摘要: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance...  閱讀全文

    posted @ 2008-10-27 17:01 曉宇 閱讀(2412) | 評論 (0)編輯 收藏

    Lucene的切詞 analysis包

    在搜索引擎中,切詞語是一個重要的部分,其中包括專有名詞的提取、詞的分割、詞的格式化等等。
    TokenStream 類幾乎是所有這些類的基類
    有兩個需要被子類實現的方法Token next() 和 close()
    首先來看analysis包,這個包主要是提供一些簡單的詞匯化處理
    Tokenizer結尾的類是將要處理的字符串進行分割成Token流,而根據分割的依據的又產生了以下幾個Tokenizer類
    首先Tokenizer類是所有以Tokenizer結尾的類的基
    然后是CharTokenizer,所有的以Tokenizer結尾的類都是從這個類繼承的
    這個類中有一個抽象方法
      protected abstract boolean isTokenChar(char c);
    另外一個需要被子類覆寫的方法
      protected char normalize(char c) {};
    是對單個字符進行處理的方法譬如說將英文字母全部轉化為小寫

    還有一個變量
    protected Reader input;
    這個讀取器是這些類所處理的數據的   數據源
    輸入一個Reader ,產生一個Token流


    這個方法是是否進行切分的依據,依次讀取char流,然后用這個方法對每個char進行檢測,如果返回false則將預先存儲在
    詞匯緩沖區中的char數組作為一個Token返回
    LetterTokenizer :
          protected boolean isTokenChar(char c) {
                  return Character.isLetter(c);
          }
    WhitespaceTokenizer:
          protected boolean isTokenChar(char c) {
                  return !Character.isWhitespace(c);
          } 
    LowerCaseTokenizer extends LetterTokenizer:
    protected char normalize(char c) {
          return Character.toLowerCase(c);
       }

       在構造函數中調用super(in);進行和 LetterTokenizer同樣的操作,但是在詞匯化之前所有的詞都轉化為小寫了
     
    然后是以Filter結尾的類,這個類簇主要是對已經詞匯化的Token流進行進一步的處理
     輸入是Token流 , 輸出仍然是Token流。
    TokenFilter extends TokenStream  是所有這些類的父類
    protected TokenStream input;
    在TokenFilter 中有一個TokenStream 變量,是Filter類簇處理的數據源,而Filter類簇又是繼承了TokenStream 類的
    有一個public final Token next()方法,這個方法以TokenStream.next()產生的Token流 為處理源,產生的仍然是Token流
    只不過中間有一些處理的過程
    LowerCaseFilter:將所有的Token流的轉化為小寫
         t.termText = t.termText.toLowerCase();
    StopFilter:過濾掉一些停止詞,這些停止詞由構造函數指定
         for (Token token = input.next(); token != null; token = input.next())
          if (!stopWords.contains(token.termText))
            return token;


    比較一下Tokenizer類簇和Filter類簇,可以知道
    Tokenizer類簇主要是對輸入的Reader流,實際上是字符流按照一定的規則進行分割,產生出Token流
    其輸入是字符串的Reader流形式,輸出是Token流

    Filter類簇主要是對輸入的Token流進行更進一步的處理,如去除停止詞,轉化為小寫
    主要為一些格式化操作。
    由于Filter類簇的輸入輸出相同,所以可以嵌套幾個不同的Filter類,以達到預期的處理目的。
    前一個Filter類的輸出作為后一個Filter類的輸入
    而Tokenizer類簇由于輸入輸出不同,所以不能嵌套







    posted @ 2008-05-30 14:47 曉宇 閱讀(1029) | 評論 (1)編輯 收藏

    JDK1.5的自動裝箱功能

    在JAVA JDK1.5以后具有的自動裝箱與拆箱的功能,所謂的自動裝箱
    與拆箱也就是把基本的數據類型自動的轉為封裝類型。

    如:自動裝箱,它可以直接把基本類型賦值給封裝類型

    Integer num = 10 ;

    Double d = 2d ;

    自動拆箱,它可以把封裝類型賦值給基本類型

    int num = new Integer(10);

    double d = new Double(2d);

    自動裝箱與拆箱的功能事實上是編譯器來幫您的忙,編譯器在編譯時期依您所編寫的語法,決定是否進行裝箱或拆箱動作。在自動裝箱時對于值從-128到127之間的值,它們被裝箱為Integer對象后,會存在內存中被重用,所以范例4.6中使用==進行比較時,i1 與 i2實際上參考至同一個對象。如果超過了從-128到127之間的值,被裝箱后的Integer對象并不會被重用,即相當于每次裝箱時都新建一個Integer對象,所以范例4.7使用==進行比較時,i1與i2參考的是不同的對象。所以不要過分依賴自動裝箱與拆箱,您還是必須知道基本數據類型與對象的差異。

        public void testBoxingUnboxing() {

            int i = 10;

            Integer inta = i;

            inta++;

            inta += 1;

            int j = inta;

            assertTrue(j == inta);結果是:true//junit里面的方法

            assertTrue(j == new Integer(j)); 結果是:true

            assertTrue(10000 == new Integer(10000)); 結果是:true

        }

    Integer i = 100.相當于編譯器自動為您作以下的語法編譯:

    Integer i = new Integer(100).所以自動裝箱與拆箱的功能是所謂的“編譯器蜜糖”(Compiler Sugar),雖然使用這個功能很方便,但在程序運行階段您得了解Java的語義。例如下面的程序是可以通過編譯的:

    Integer i = null.int j = i.這樣的語法在編譯時期是合法的,但是在運行時期會有錯誤,因為這種寫法相當于:

    Integer i = null.int j = i.intValue().null表示i沒有參考至任何的對象實體,它可以合法地指定給對象參考名稱。由于實際上i并沒有參考至任何的對象,所以也就不可能操作intValue()方法,這樣上面的寫法在運行時會出現NullPointerException錯誤。

    自動裝箱、拆箱的功能提供了方便性,但隱藏了一些細節,所以必須小心。再來看范例4.6,您認為結果是什么呢?

    Ü. 范例4.6 AutoBoxDemo2.java

    public class AutoBoxDemo2 {

    public static void main(String[] args) {
    Integer i1 = 100;

    Integer i2 = 100;

    if (i1 == i2)

    System.out.println("i1 == i2");

    else

    System.out.println("i1 != i2").

    }

    }

    從自動裝箱與拆箱的機制來看,可能會覺得結果是顯示i1 == i2,您是對的。那么范例4.7的這個程序,您覺得結果是什么?

    Ü. 范例4.7 AutoBoxDemo3.java

    public class AutoBoxDemo3 {

    public static void main(String[] args) {

    Integer i1 = 200;

    Integer i2 = 200;

    if (i1 == i2)

    System.out.println("i1 == i2");

    else

    System.out.println("i1 != i2");

    }

    }

    結果是顯示i1 != i2這有些令人驚訝,兩個范例語法完全一樣,只不過改個數值而已,結果卻相反。

    其實這與==運算符的比較有關,在第3章中介紹過==是用來比較兩個基本數據類型的變量值是否相等,事實上==也用于判斷兩個對象引用名稱是否參考至同一個對象。

    在自動裝箱時對于值從–128127之間的值,它們被裝箱為Integer對象后,會存在內存中被重用,所以范例4.6中使用==進行比較時,i1 i2實際上參考至同一個對象。如果超過了從–128127之間的值,被裝箱后的Integer對象并不會被重用,即相當于每次裝箱時都新建一個Integer對象,所以范例4.7使用==進行比較時,i1i2參考的是不同的對象。

    所以不要過分依賴自動裝箱與拆箱,您還是必須知道基本數據類型與對象的差異。范例4.7最好還是依正規的方式來寫,而不是依賴編譯器蜜糖(Compiler Sugar)。例如范例4.7必須改寫為范例4.8才是正確的。

    Ü. 范例4.8 AutoBoxDemo4.java

    public class AutoBoxDemo4 {
    public static void main(String[] args) {

    Integer i1 = 200;

    Integer i2 = 200;

    if (i1.equals(i2))

    System.out.println("i1 == i2");

    else

    System.out.println("i1 != i2");

    }

    }

    結果這次是顯示i1 == i2使用這樣的寫法,相信也會比較放心一些,對于這些方便但隱藏細節的功能到底要不要用呢?基本上只有一個原則:如果您不確定就不要用。

    posted @ 2008-05-16 11:33 曉宇 閱讀(446) | 評論 (0)編輯 收藏

    關于IndexWriter中的3個性能參數

    IndexWriter中有3個重要的性能參數
    mergeFactor           默認為10
    minMergeDocs      默認為10
    maxMergeDocs     默認為Integer.maxValue

    maxMergeDocs     一個段中所能包含的最大的doc數,達到這個數目即不再將段進行合并 一般不改變這個值
    minMergeDocs      是指在RAMDirectory中保存的Doc的個數,達到minMergeDocs 個即要合并到硬盤上去(在硬盤上新建一個段)
    mergeFactor           合并因子,是控制硬盤上的段的合并的,每次在硬盤上新建一個段之后即執行
                                     targetMergeDocs*=mergeFactor(一開始targetMergeDocs=minMergeDocs) 如果硬盤上的doc數目大于等于                            targetMergeDocs則將硬盤上最后建立的mergeFactor個段進行合并成一個段

    拿默認的參數舉例:
    如果硬盤上面已經有9個段  每個段分別存儲了10個Document,共(90個DOC),這時候如果程序再向硬盤合并一個新的段(含10個DOC),合并完之后targetMergeDocs=10*10  程序檢查已經合并的最后(按照創建的時間先后順序)mergeFactor個段的document的總和100是否大于等于targetMergeDocs(這里是100,剛好滿足要求)于是程序又將硬盤上面的后10個段合并為一個新的段。

    另外一個例子:
    doc數目            段數目
      1000---------------9個
      100-----------------9個
      10   ----------------9個
    這時如果再象硬盤中新建一個新的包含了10個doc的段
        doc數目            段數目
      (1) 1000----------------9個

      (2)  100-----------------9個

      (3)   10  ----------------9個
                                         
      (4)    10 ----------------1個
    這時候(3)(4)首先合并成一個新的段(3-4)包含100個doc
     然后(2)(3-4)和并成一個新段(2-3-4)包含1000個doc
    然后(1)(2-3-4)合并成一個新的段  包含10000個doc
    最后合并成一個段


    private final void maybeMergeSegments() throws IOException {
        
    long targetMergeDocs = minMergeDocs;
        
    while (targetMergeDocs <= maxMergeDocs) {
          
    // find segments smaller than current target size
          int minSegment = segmentInfos.size();
          
    int mergeDocs = 0;
          
    while (--minSegment >= 0{
            SegmentInfo si 
    = segmentInfos.info(minSegment);
            
    if (si.docCount >= targetMergeDocs)
              
    break;
            mergeDocs 
    += si.docCount;
          }


          
    if (mergeDocs >= targetMergeDocs)          // found a merge to do
            mergeSegments(minSegment+1);
          
    else
            
    break;

          targetMergeDocs 
    *= mergeFactor;        // increase target size
          System.out.println("- -- - -targetMergeDocs:"+targetMergeDocs);
          
    try {Thread.sleep(5000);} catch(Exception e) {};
        }

      }

    posted @ 2008-05-15 19:27 曉宇 閱讀(1430) | 評論 (0)編輯 收藏

    HIBERNATE的一對多和多對一關聯

    HIBERNATE一多對關聯中  要求在持久化類中定義集合類屬性時,必須把屬性聲明為接口,因為HIBERNATE在調用持久化類的SET/GET方法時傳遞的是HIBERNATE自己定義的集合類。
    在定義集合時,一般先初始化為集合實現類的一個實例 : private Set orders=new HashSet(),這樣可以避免訪問空集合出現NullPointerException.

    posted @ 2008-05-14 11:01 曉宇 閱讀(237) | 評論 (0)編輯 收藏

    Lucene索引文件的格式

    segments文件的格式: (段的信息)
    int:  =-1    查看文件是否是Lucene合法的文件格式
    long:        版本號,每更新一次該文件將會將版本號加1
    int:         用來命名新段
    int:         段的數目
    String + int 段的信息 String是段的名稱  int是段中所含的doc數目
    String + int 同上


    .fnm的文件格式:   (Field的信息)
    int:               Field的個數,最少為1,最少有一個Field("",false),在初始化的時候寫入(暫時不知道原因); 名稱為空字符串,未索引,        未               向           量化。readVInt()讀取
    String: byte      String是 Field的名稱  byte指示該Field 是否被索引,是否向量化 (值有:11,10,01)第一個1代表被索引,第二個代表被向量化
    String: byte Field 同上
         

     

    .fdx的文件格式:主要是提供對.fdt中存儲的document的隨即讀取
    long :       第一個document在.fdt文件中的位置
    long:        第二個document在.fdt文件中的位置


    .fdt的文件格式:  .fdt文件存儲了一系列document的信息
    VInt:        該document中的isStored屬性為true的域的個數
    (VInt:)      如果該field的isStored屬性為true則得到該field的fieldNumber,暫時不知道這個fieldNumber是怎么產生的,有什么用,初步估計是按照field創建的順序產生的,每次再上一個field的fieldNumber基礎上加1。
    byte:        如果該field的isTokenized屬性為true寫入1否則寫入false。
    String:      該field的stringValue()值。
    一個document結束,下面的數據將會開始一個新的document,每個新的document的開始點的文件位置都會在.fdx中有記載,便于隨即訪問

     

    posted @ 2008-04-21 17:52 曉宇 閱讀(483) | 評論 (0)編輯 收藏

    org.apache.lucene.index.SegmentInfos

    final class SegmentInfos extends Vector
    可以看出該類實際上是一個Vector   以及封裝了對該Vevtor的一些操作
    實際上封裝的是對segments文件的一些讀寫操作
    先來看下segments文件的格式

    segments文件的格式:
    int:  =-1         文件是否是Lucene合法的文件格式正常情況下為 -1
    long:             版本號,每更新一次該文件將會將版本號加1
    int:                用來命名新段
    int:                段的數目
    String + int  段的信息 String是段的名稱  int是段中所含的doc數目
    String + int  同上

    所以用Lucene的API,我們可以簡單的打印出其segments的所有信息

    try {
       //DataInputStream fis = new DataInputStream(new FileInputStream("C:\\sf\\snow\\segments"));
       FSDirectory dir=FSDirectory.getDirectory("C:/sf/snow", false);
        InputStream input = dir.openFile("segments");
       System.out.println("Format:"+input.readInt());             //得到文件標志,是否為正常的segments文件
       System.out.println("version:"+input.readLong());        //得到版本號
       System.out.println("name:"+input.readInt());                //得到用來重命名新段的int,暫時不知道有什么用
       int n=input.readInt();                                                          //段的數目
       System.out.println("SegmentNum:"+n);                          
       for(int i=0;i<n;i++) {                                                           //用循環打印出所有段的信息 名稱和長度
        System.out.println("segment "+i+" - name:"+input.readString()+" num:"+input.readInt());
       }
      } catch (Exception e) {

      }
    當然,該類提供了更為復雜的訪問和更新segments文件的方法
     final void read(Directory directory)    將所有的段信息保存在本vector中
    final void write(Directory directory)    跟新該segment文件的內容,主要是為了添加段,
    主要是更新 版本號 段的數目,跟新完這些后即可往segment文件后添加新段的信息。

    posted @ 2008-04-18 17:02 曉宇 閱讀(365) | 評論 (0)編輯 收藏

    org.apache.lucene.index.SegmentInfo

    segment(段)的信息
    該類比較簡單,貼出其全部代碼

    import org.apache.lucene.store.Directory;

    final class SegmentInfo {
      public String name;        //在索引目錄中唯一的名稱 
      public int docCount;      // 該段中doc的數目
      public Directory dir;      // 該段所存在的Dirrectory

      public SegmentInfo(String name, int docCount, Directory dir) {
        this.name = name;
        this.docCount = docCount;
        this.dir = dir;
      }
    }

    posted @ 2008-04-18 16:45 曉宇 閱讀(264) | 評論 (0)編輯 收藏

    org.apache.lucene.store.RAMInputStream

    該類是從RAMFile中讀數據用的
    最重要的一個方法:
    該方法存在著從RAMFile的多個byte[1024]中讀取數據的情況,所以應該在循環中進行處理

     public void readInternal(byte[] dest, int destOffset, int len) {
        int remainder = len;
        int start = pointer;
        while (remainder != 0) {
          int bufferNumber = start/BUFFER_SIZE; //  buffer的序號
          int bufferOffset = start%BUFFER_SIZE; //    buffer偏移量
          int bytesInBuffer = BUFFER_SIZE - bufferOffset;// 在當前buffer中剩下的字節數
          //如果緩沖區中剩余的字節大于len,則讀出len長度的字節,如果不夠則讀出剩余的字節數
          // bytesToCopy表示實際讀出的字節數
          int bytesToCopy = bytesInBuffer >= remainder ? remainder : bytesInBuffer;
          byte[] buffer = (byte[])file.buffers.elementAt(bufferNumber);
          System.arraycopy(buffer, bufferOffset, dest, destOffset, bytesToCopy);
          destOffset += bytesToCopy;       //增加已經復制的byte數據長度 到  dest中的偏移量
          start += bytesToCopy;                 //RAMFile文件指針,用來確定bufferNumber 和bytesInBuffer   相當于內存中的分頁
          remainder -= bytesToCopy;       //剩余的還未復制的字節數
        }
        pointer += len;//文件指針位置
      }

    posted @ 2008-04-18 11:45 曉宇 閱讀(224) | 評論 (0)編輯 收藏

    org.apache.lucene.store.RAMOutputStream

    這是OutputStream的一個子類,其輸出設備是內存,準確來說是RAMFile,即將數據寫入到RAMFile的Vector中去。
    該類有一個最重要的方法,現在把它整個貼出來

    public void flushBuffer(byte[] src, int len) {
        int bufferNumber = pointer/BUFFER_SIZE;   //buffer序列,即當前所寫Buffer在RAMFile中的Vector中的序列號
        int bufferOffset = pointer%BUFFER_SIZE;   //偏移量,即當前所寫字節在當前Buffer中的偏移量。
        int bytesInBuffer = BUFFER_SIZE - bufferOffset; //當前Buffer的剩余可寫字節數
       //bytesToCopy是實際寫入的字節數,如果當前Bufer的剩余字節數大于需要寫的字節的總數則寫入所有字節
       //否則,將當前Buffer寫滿即可,剩余的字節將寫入下一個Buffer
        int bytesToCopy = bytesInBuffer >= len ? len : bytesInBuffer;

        if (bufferNumber == file.buffers.size())
          file.buffers.addElement(new byte[BUFFER_SIZE]); //在RAMFile中添加新的byte[1024]元素

        byte[] buffer = (byte[])file.buffers.elementAt(bufferNumber);
        System.arraycopy(src, 0, buffer, bufferOffset, bytesToCopy);

        if (bytesToCopy < len) {     // not all in one buffer,
          int srcOffset = bytesToCopy;
          bytesToCopy = len - bytesToCopy;    // remaining bytes 剩余的未寫入的字節數
          bufferNumber++;                         //將buffer數增加1
          if (bufferNumber == file.buffers.size()) 
            file.buffers.addElement(new byte[BUFFER_SIZE]);
          buffer = (byte[])file.buffers.elementAt(bufferNumber); //剩余字節寫入下一個Buffer
          System.arraycopy(src, srcOffset, buffer, 0, bytesToCopy);
        }
        pointer += len;
        if (pointer > file.length)
          file.length = pointer;        //移位文件指針          在原有的基礎上加上實際寫入的字節總數

        file.lastModified = System.currentTimeMillis(); //修改文件的最后修改時間為當前時間
      }

    從指定的字節數組復制指定長度的字節到RAMFile中去。由于RAMFile中Vector的元素是byte[1024]所以可能存在做一次該操作
    要操作兩個Vector元素的情況。即先將當前byte[1024]數組填滿,再新建一個元素裝載剩余的字節。

    另外還有一個writeTo(OutputStream out)方法,將RAMFile中的數據輸出到另一個輸出流



    posted @ 2008-04-18 11:38 曉宇 閱讀(229) | 評論 (0)編輯 收藏

    org.apache.lucene.store.RAMFile

    這個類比較簡單
    import java.util.Vector;
    class RAMFile {
      Vector buffers = new Vector();
      long length;
      long lastModified = System.currentTimeMillis();
    }

    可以理解為一個存儲在內存中的文件,buffers是存儲數據的容器,length是容器中數據的總的字節數
    lastModified 是最后修改時間。

    在實際使用過程中容器buffers存放的對象是一個byte[1024]數組。

    posted @ 2008-04-18 11:23 曉宇 閱讀(287) | 評論 (0)編輯 收藏

    org.apache.lucene.store.OutputStream

    OutputStream
    這是一個Abstract類,是Lucene自己的一個文件輸出流的基類
    BUFFER_SIZE = 1024  緩沖區 大小為 1024bit
    bufferStart = 0 文件位置指針
    bufferPosition = 0 內存緩沖區指針

     public final void writeByte(byte b) throws IOException {
        if (bufferPosition >= BUFFER_SIZE)
          flush();
        buffer[bufferPosition++] = b;
      }
    幾乎所有的寫入函數都要調用這個函數,如果緩沖區的當前容量已經等于他的最大容量,則將緩沖區中的數據寫入文件。

    public final void writeBytes(byte[] b, int length) throws IOException
    批量寫byte進入內存緩沖

    public final void writeInt(int i) throws IOException
    寫入整形數據

    public final void writeLong(long i) throws IOException
    寫入長整型數據,即結合移位運算調用兩次writeInt(int i)

    另外,最值得注意的是在該類中有兩個最特殊的函數
    writeVInt(int i) /   writeVLong(long i),
    先說
    writeVInt(int  i )   {
     while ((i & ~0x7F) != 0) {                               
          writeByte((byte)((i & 0x7f) | 0x80));
          i >>>= 7;
        }
        writeByte((byte)i);
    }
    ~0x7F==~(0111 1111)==(1000 0000)==0x80
    ((i & ~0x7F) != 0) 這一句判斷i是否大于0x80,如果不是則說明該int只有一個字節的有效數據,其他字節都是0,直接轉化為Byte寫入。
    如果大于0x80則
    (i & 0x7f) | 0x80
    i&0x7f 只對后7位進行處理,|0x80將第8位置1,與前面的7個bit構成一個字節,置1的原因是說明該字節并不是一個完整的整形數,需要與其他的字節合起來才能構成一個整形數字。
    這個算法相當于將一個32bit的整形數字按照每7位編碼成一個字節進行存儲,將按照整形數的大小存儲1-5個字節。

    writeVLong(long i)方法大致與其相同。

    final void writeChars(String s, int start, int length)
    將字符串轉化成UTF-8編碼的格式進行存儲。
    附:

    UNICODE值 UTF-8編碼
    U-00000000 - U-0000007F: 0xxxxxxx
    U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
    U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx

    可見對于在 0x00-0x7F范圍內的UNICODE值(最大有效數位:7位),將會編碼成單字節的,會大大節約存儲空間。
    對于在           0x80-0x7FF范圍內的UNICODE(最大有效數位:11位),會編碼成雙字節的。先存儲原字節低5位的數位,且將最高位和次高位都置1,再次高位置0(writeByte((byte)(0xC0 | (code >> 6)));)。然后存儲后6位的字節,將前兩位置10(writeByte((byte)(0x80 | (code & 0x3F)));)
    對于其他的UNICODE值則
    writeByte((byte)(0xE0 | (code >>> 12)));               4位
     writeByte((byte)(0x80 | ((code >> 6) & 0x3F)));   5位
     writeByte((byte)(0x80 | (code & 0x3F)));            3- 5位

    final void writeString(String s) throws IOException
    該函數首先用s.length()判斷該String總共有多少個字符
    然后首先調用writeVInt寫入這個字符長度
    再調用writeChars(s,s.length())寫入字符


    在inputStream中的readString()方法則與其相反,首先用readVInt()方法讀取字符長度len 然后讀取len長度的字符

    protected final void flush() throws IOException
    該方法調用另外一個方法flushBuffer將緩沖區中的數據輸出,然后清空緩沖區;

    abstract void flushBuffer(byte[] b, int len) throws IOException
    可見flushBuffer方法是abstract的,即需要其子類對該方法進行覆寫,以定位該輸出流的輸出方式。

    final long getFilePointer() throws IOException
    得到文件指針的位置,即得到輸出流已經輸出的字節數。

    public void seek(long pos) throws IOException
    輸出緩沖區的內容,然后將文件指針定位到long所指示的文件位置。

    abstract long length() throws IOException
    返回文件中已有的字節數。需要子類實現。

















    posted @ 2008-04-16 21:24 曉宇 閱讀(218) | 評論 (0)編輯 收藏

    org.apache.lucene.store.FSDirectory

    FSDirectory繼承了abstract類Directory
    在該類中既有該類的一些初始化操作,又有對FSDirectory對象本身的一些操作,這是為什么把其構造函數設置為私有的一部分原因

    static final Hashtable DIRECTORIES = new Hashtable();
    每新建一個FSDirectory都會將其加入到該Hashtable中來。名稱是FSDirectory對應的File   值是該FSDirectory。
    注意:final對象并非是不可更改的

    static final String LOCK_DIR =
        System.getProperty("org.apache.lucene.lockdir",
          System.getProperty("java.io.tmpdir"));
    首先看用戶是否注冊了"org.apache.lucene.lockdir"屬性,如果沒有則用JAVA虛擬機固有的"java.io.tmpdir"屬性
    這個屬性是一個路徑,代表lucene的鎖文件鎖放的位置。

    static final boolean DISABLE_LOCKS =
          Boolean.getBoolean("disableLuceneLocks") || Constants.JAVA_1_1;
    如果用戶注冊了"disableLuceneLocks"屬性且為false,或者JAVA的版本是1.1則無法使用鎖。

    static FSDirectory getDirectory(String path, boolean create)
    static FSDirectory getDirectory(File file, boolean create)
    從得到一個指定路徑或者文件的FSDirectory如果在則取出,如果不存在則用其私有的構造函數構造一個
    該類還有3個非static的類變量
      private File directory = null;      索引目錄
      private int refCount;                   鎖目錄
      private File lockDir;                    索引目錄數目
    實際上,初始化一個FSDirectory只需要初始化這3個變量即可
    如果create的值為true 則:如果索引目錄是已經存在的目錄,則會遍歷該目錄然后刪除每一個文件,如果鎖目錄是已存在的也會用list返回所有的文件然后調用file.delete() 刪除。 如果目錄不存在則創建一個新的。

    注意:list()方法   會先用文件名進行排序然后返回(a.txt會比b.txt先返回)    且delete方法刪除文件夾時,只能刪除空文件夾。如果失敗則跳出程序,不會刪除在該文件夾之后返回的文件。(如果有aa.txt , ab/b.txt , b.txt , 則刪除時候由于a文件夾非空刪除失敗,則b.txt由于前面刪除失敗跳出程序,也不會被刪除,但是aa.txt被正常刪除)

    private FSDirectory(File path, boolean create) throws IOException
    私有的構造函數

    private synchronized void create() throws IOException
    創建新的directory /lockDir目錄,當目錄已存在時即清空該目錄,不存在即創建新的目錄。

    final String[] list() throws IOException
    以字符串文件名的形式返回索引目錄的所有文件

    final boolean fileExists(String name) throws IOException
    在索引目錄是否存在指定文件名的文件

    final long fileModified(String name) throws IOException
    static final long fileModified(File directory, String name)
    返回該文件的最后修改時間,directory參數為相對路徑,第一個函數的相對路徑為索引目錄

    void touchFile(String name) throws IOException
    將該文件的最后修改時間設置為當前時間

    final long fileLength(String name) throws IOException
    返回該文件的長度

    final void deleteFile(String name) throws IOException
    刪除該文件

    final synchronized void renameFile(String from, String to) throws IOException
    重命名該文件
    該方法會首先檢測新的文件名命名的文件是否已經存在如果存在即刪除該文件,然后再將文件重新命名為新的文件名。
    doug cutting在該方法的注釋上寫到:
    1.刪除操作和重命名的操作不是原子的。
    2.重命名操作在有些虛擬機上面不能正確的工作,如果重命名失敗則會采用手動copy的方法。使用輸入輸出流將舊的文件的內容寫入到新的文件中去,然后刪除舊的文件。
    注意:該方法必須是同步的。

    final OutputStream createFile(String name) throws IOException
    用指定的文件名創建一個新的可寫的空文件  實際上返回的是FSOutputStream,注意這里的OutputStream并不是java的基礎類。而是doug cutting自己寫的一個文件隨即訪問類。同理FSInputStream和InputStream也是Lucene自己的類。

    final InputStream openFile(String name) throws IOException
    從一個存在的文件打開一個輸入流

    getLockPrefix()
    在FSDirectory中還有
     private static MessageDigest DIGESTER;這個靜態變量是提供加密功能的
    DIGESTER=MessageDigest.getInstance("MD5"),-----MD5加密算法
    或者可以DIGESTER=MessageDigest.getInstance("SHA"),-----SHA加密算法
    用于對鎖目錄的   文件名的加密
    用法如下:
    digest = DIGESTER.digest(dirName.getBytes());  dirName是需要被加密的字符串,這里是索引文件的目錄名,
    在FSContext中,其應用在 getLockPrefix() 該方法是為某個索引目錄創建其對應的鎖目錄文件名。
    首先返回經過加密后的byte[] 數組digest,然后將digest按照每4個bit轉化為一個16進制的字符,存進一個StringBuffer中
    其轉化類似與Base64編碼方式,不過要簡單得多。

    方法
    Lockl  makeLock(String name)
    是從Directory中擴展而來的,該方法返回一個Lock對象,該對象將會在介紹完Lucene的輸入輸出流之后介紹。
    該方法比較簡單,首先是調用了getLockPrefix() 方法,返回文件鎖的部分對象名,然后在該名稱后面加上鎖的特征名
    譬如說讀寫鎖 事務鎖
    其名稱類似于下:
    lucene-12c90c2c381bc7acbc4846b4ce97593b-write.lock
    lucene-12c90c2c381bc7acbc4846b4ce97593b-commit.lock
    這兩種鎖機制將會在后面介紹
    最后通過一個匿名的內部類返回一個經過重載的Lock對象,該內部類中的方法有鎖的創建,得到,釋放,以及檢測,另外還有一個toString()方法返回鎖的名稱。



    在FSDirectory類中有OutputStream和InputStream的實現類,這兩個虛類只是定義了一些操作,并沒有定義輸入或者輸出的設備。
    Lucene在輸入輸出的設計上,將會由子類定義輸入輸出的設備。
    FSOutputStream
    在FSOutputStream中有一個 RandomAccess File=new RandomAccessFile(path, "rw");
    在對該輸出流的操作將用調用該file的相應方法實現
    最重要的
      public final void flushBuffer(byte[] b, int size) throws IOException {
        file.write(b, 0, size);
      }
    flushBuffer的調用將會將byte中的0--size范圍的數據寫入到文件path中去。


    FSInputStream
    最重要的
    protected final void readInternal(byte[] b, int offset, int len)
           throws IOException {
        synchronized (file) {
          long position = getFilePointer();     //得到該當前文件指針
          if (position != file.position) {
            file.seek(position);
            file.position = position;
          }
          int total = 0;
          do {

       //從文件中讀取指定長度的字節到字節數組
       // 在其基類InputStream中的refill()方法  將會調用  readInternal(buffer, 0, bufferLength);首先從文件中讀取字節到緩沖數組。
      //  在InputStream中每次讀取操作都會調用readInternal方法,或者通過refill()方法間接調用該方法。
            int i = file.read(b, offset+total, len-total);       //將文件中的數據讀到緩沖區
            if (i == -1)
              throw new IOException("read past EOF");
            file.position += i;
            total += i;
          } while (total < len);
        }
      }





















    posted @ 2008-04-10 21:35 曉宇 閱讀(1097) | 評論 (0)編輯 收藏

    org.apache.lucene.document.DateField

    該類提供了日期和字符串之間的相互轉化,實際上是 long型和String型的相互轉化,轉化時用到了一個不常用的
    Long.toString(long,int);方法。是按指定的方式對long型進行轉化
    第一個參數是要轉化的long,第二個參數是轉化時候的基數,如果基數是10就相當于方法Long.toString(long);
    這里使用的參數是最大值,即36== 10個數字+26個英文字母。這樣轉化出來的字符串長度比較短,占用比較少的空間,
    另外,在轉化時,統一了轉化后的字符串長度,如果不足9位(日期的long轉化后最高為9位,1970之后的日期可正確轉換),
    統一長度后的字符串可以通過比較字符串來比較日期的大小。


    日期轉化成的字符串類似于
    0fev8eza3
    本來應該是fev8eza3 采取了不足9位補0的方法。

      private static int DATE_LEN = Long.toString(1000L*365*24*60*60*1000,
                Character.MAX_RADIX).length();
    計算出從1970年開始后1000年的時間轉化為字符串后的長度,所有轉化后的時間都不應超過這個長度,如果不足則在前面補0

    可以通過字符串轉化為日期的函數計算出能表示的最大日期為
    stringToTime("zzzzzzzzz");
    打印出來是 Fri Apr 22 19:04:28 CST 5188  
    所以該函數能轉化的日期范圍為 1970-1-1~~5188-4-22


    日期轉化為字符串
    public static String timeToString(long time)

    字符串轉化為日期
    public static long stringToTime(String s)


    實際上 函數 LongToString(long i,int radix) 相當于  先將i轉化為radix進制的整數,然后再用函數
    LongToString(i)轉化為字符串。所以radix的值應該在2--36之間如果不是 則按照10進制計算。

    posted @ 2008-04-10 19:26 曉宇 閱讀(341) | 評論 (0)編輯 收藏

    org.apache.lucene.document.Document

    Document是一些Field的集合,每個Field有一個名字和文本值,當中的某些Field可能會隨著Documnet被存儲。這樣,每個Document應該至少包含一個可以唯一標示它的被存儲的Field

    //Field集合
    List fields = new Vector();
    //增強因子,作用于該Document的所有Field
    private float boost = 1.0f;
    //向Document中添加Field
    public final void add(Field field) {
        fields.add(field);
      }
    //刪除指定名稱的第一個Field
    public final void removeField(String name)
    //刪除所有擁有指定名稱的Field
    public final void removeFields(String name)
    //得到指定名稱的第一個Field
    public final Field getField(String name)
    //以數組的形式返回指定名稱的所有Field
    public final Field[] getFields(String name)
    //得到所有Field的一個枚舉
    public final Enumeration fields()

    該類也重載了toString()方法
    打印出所有Field的信息




    posted @ 2008-04-08 20:27 曉宇 閱讀(734) | 評論 (0)編輯 收藏

    org.apache.lucene.document.Field類

    package org.apache.lucene.document;   
    Field
    是Document的一部分,每個Field有兩個部分組成 名字-值 對 名字是String 值 可以是String 和 Reader,如果是KeyWord類型的Field,那么值將不會被進一步處理,像URL,Date等等。Field被存儲在Index中,以便于能以Hits的形式返回原有的Document
    Field有3 個Boolean形的標識
      private boolean isStored = false;     被存儲  
      private boolean isIndexed = true;    被索引
      private boolean isTokenized = true  被分割
    通過調整這3個boolean的值,可以確定該Field的類型
      Keyword      true, true, false           一般存儲 URL DATE 等關鍵字
      UnIndexed    true, false, false          一般是隨HITS查詢結果一起返回的信息
      Text              true, true, true           
      UnStored      false, true, true

    另外,還有一個重載的toString方法 可以打印出該Field的類型

    float boost = 1.0f;    增強因子,用于排序的評分,作用于擁有該域(field)的所有文檔(document)

      
     

    posted @ 2008-04-08 20:07 曉宇 閱讀(936) | 評論 (0)編輯 收藏

    數據庫的事務 JDBC

    TRANSACTION_NONE:
      正式地講,TRANSACTION_NONE不是一個有效的事務級別。
      根據java.sql Connection API文件,這個級別表示事務是
      不被支持的,因此理論上說你不能使用TRANSACTION_NONE作
      為一個自變量賦給Connection.setTransactionIsolation()
      方法。事實上,雖然一些數據庫實施了這個事務級別,但是
      Oracle9i卻沒有實施。


     臟讀取(TRANSACTION_READ_UNCOMMITTE):
    (允許的操作       讀-讀  讀-寫 寫-讀     (臟數據,不可重復讀,虛讀) )
       表示,這個事務級別
      允許讀取臟數據,什么是臟數據?就是指還沒有提交的數據.
      因為這個級別,是允許一個事務(A)讀取另一個事務(B)
      還沒有提交的數據.一旦事務B發生異常退出.而修改了的數據
      卻還沒提交,或者新插入的數據和刪除了的數據都還沒有
      提交,導致事務A拿到了一些臟數據,或者錯誤數據;
      因此在這個事務級別里是會發生臟讀,重復讀,錯誤讀取;

     禁止臟讀(TRANSACTION_READ_COMMITTED):
    (允許的操作       讀-讀  讀-寫 (不可重復讀,虛讀))
      在這個級別中,事務A
      只能讀取一些提交的數據,如事務B添加了一條記錄,但是
      如果事務B沒有提交,那么事務A是讀不到的,所以該事務級別,
      把臟讀給屏蔽掉了.---卻允許重復讀取,和錯誤讀取.

      什么是重復讀取呢?譬如,事務A讀取了一個數據,這個數據
      的值為"helloworld",事務A準備利用這個數據來更新一下
      其他數據,但這個時候事務B開始對這個數據進行修改,并且
      提交---"hello 無名氏",由于是已經提交了,所以事務A是可以
      看到這個數據的,當事務A在沒提交事務之前,它想看下數據
      是否正確,這個時候它發現,新讀出的數據已經和原來的數據
      不一樣了(這就是重復讀取);




     允許重復讀取(TRANSACTION_REPEATABLE_READ):
    (允許的操作       讀-讀  讀-寫(僅允許插入,不允許刪除和修改)(虛讀))
      在這個級別中,
      是禁止了臟讀,和取消了不可重復讀取,但是沒有禁止錯誤讀取;
      這個級別的事務比較嚴格,當一個事務A在讀取一個值的時候
      是不允許另一個事務對該值進行修改的;
      為了允許重復讀取,可以選用該級別,因為TRANSACTION_READ_
      COMMITED這個事務級別,是允許重復讀取提交的數據的,如果
      事務A在讀取一個數值的時候,值為"Hello World!",但這個時
      候事務B對"Hello World"值進行修改了,改為"Hello EveryOne"
      然后提交,當事務A再次去讀取這個值的時候,去發現原來讀到
      的值改變了,變成了"Hello EveryOne",為了防止出現這種情況
      可以禁止重復提交,目的是為了重復讀取不會出錯!那么這個
      時候就可以選擇
    TRANSACTION_REPEATABLE_READ這個級別,
      這個級別就是用來禁止重復提交的.
    (實際上是加了行鎖,鎖定了選中的數據,不允許修改,但是允許插入新的數據)
      雖然這個時候是禁止了重復提交,但卻可以添加刪除,
      比如事務A,作了個查詢語句"select * from 無名氏 "; 這個時候是允許事務B做這樣的操作的:
      "insert into 無名氏 values(2,'aaa')"; 這個時候,
      事務A再次做讀取操作的時候,卻發現數據莫名其妙的
      多了一條,這就是所謂的---幻影讀取;




     禁止幻讀(TRANSACTION_SERIALIZABLE):
     事務的最高級別(串行化  操作)事務級別最高,所耗費的性能也越多.
      禁止幻讀禁止了臟讀,禁止了重復提交和幻讀.
      也就是當事務A在按條件查詢的時候,事務A一旦沒有提
      交,任何事務都不能對事務A的資源進行操作--- 保證
      事務A的操作真正的原子性!


     注意:在Oracle中只支持兩種級別:

      TRANSACTION_READ_COMMITTED(默認的級別)(只有提交后
         才可以讀取)而每一個終端進行自己的DML操作 都自動開啟了一個事務

      TRANSACTION_SERIALIZABLE(竄行化操作)

    posted @ 2008-03-05 09:34 曉宇 閱讀(323) | 評論 (0)編輯 收藏

    字符串池

    Java運行環境有一個字符串池,由String類維護。執行語句String str="abc"時,首先查看字符串池中是否存在字符串"abc",如果存在則直接將"abc"賦給str,如果不存在則先在字符串池中新建一個字符串"abc",然后再將其賦給str。執行語句String str=new String("abc")時,不管字符串池中是否存在字符串"abc",直接新建一個字符串"abc"(注意:新建的字符串"abc"不是在字符串池中),然后將其付給str。前一語句的效率高,后一語句的效率低,因為新建字符串占用內存空間。String str = new String()創建了一個空字符串,與String str=new String("")相同。

    public String intern()
    返回字符串對象的規范化表示形式。

    一個初始為空的字符串池,它由類 String 私有地維護。

    當調用 intern 方法時,如果池已經包含一個等于此 String 對象的字符串(用 equals(Object) 方法確定),則返回池中的字符串。否則,將此 String 對象添加到池中,并返回此 String 對象的引用。

    它遵循以下規則:對于任意兩個字符串 st,當且僅當 s.equals(t)true 時,s.intern() == t.intern() 才為 true

      String.intern();
    再補充介紹一點:存在于.class文件中的常量池,在運行期間被jvm裝載,并且可以擴充。String的intern()方法就是擴充常量池的一個方法;當一個String實例str調用intern()方法時,java查找常量池中是否有相同unicode的字符串常量,如果有,則返回其引用,如果沒有,則在常量池中增加一個unicode等于str的字符串并返回它的引用。
    例3:
    String   s0=”kvill”;
    String   s1=new   String(“kvill”);
    String   s2=new   String(“kvill”);
    System.out.println(s0==s1);
    S1.intern();
    S2=s2.intern();
    System.out.println(s0==s1);
    System.out.prntln(s0==s1.intern());
    System.out.println(s0==s2);
    結果為:
    False
    False   //雖然執行了s1.intern(),但它的返回值沒有賦給s1
    True
    True
    最后再破除一個錯誤的理解:
    有人說,“使用String.intern()方法可以將一個String類保存到一個全局的String表中,如果具有相同值的unicode字符串已經在這個表中,那么該方法返回表中已有字符串的地址,如果在表中沒有相同值的字符串,則將自己的地址注冊到表中”如果把這個全局的String表理解為常量吃的話,最后一句話“如果在表中沒有相同值的字符串,則將自己的地址注冊到表中”是錯的。
    例4:
    String   s1=new   String(“kvill”);
    String   s2=s1.intern();
    System.out.println(s1==s1.intern());
    System.out.println(s1+”   ”+s2);
    System.out.println(s2==s1.intern());
    結果是:
    False
    Kvill   kvill
    True
    我們沒有聲明一個”kvill”常量,所以常量池中一開始沒有”kvill”的,當我們調用s1.intern()后就在常量池中新添加了一個”kvill”常量,原來的不在常量池中的”kvill”仍然存在,也就不是“把自己的地址注冊到常量池中”了。
    例5:
    String   str1=”java”;
    String   str2=”blog”;
    String   s=str1+str2;
    System.out.println(s==”javablog”);
    結果是false。Jvm確實對型如String   str1=”java”;的String對象放在常量池里,但是它是在編譯時那么做的,而String   s=str1+str2;是在運行時刻才能知道,也就是說str1+str2是在堆里創建的,所以結果為false了。


    比較兩個已經存在于字符串池中字符串對象可以用"=="進行,擁有比equals操作符更快的速度

    posted @ 2008-03-04 09:40 曉宇 閱讀(843) | 評論 (0)編輯 收藏

    JAVA中的異常處理

    在JAVA中,如果某個方法不能夠按照正常的途徑完成它的任務,就可以通過另外一個途徑退出方法。在這種情況下,方法并不立刻返回,而是拋出一個封裝了錯誤信息的對象。需要注意的是,這個方法將會立刻退出,且并不返回正常情況下的值。此外,調用這個方法的代碼也將無法執行,取而代之的是,異常處理機制開始搜索能夠處理這種錯誤條件的異常處理器。

    如果在子類中覆蓋了超類的一個方法,那么子類方法聲明的已檢查異常不能超過超類方法中聲明的異常圍。(也就是說子類中拋出的異常范圍更小或者根本不拋出異常)。需要說明的是,如果超類方法沒有拋出任何異常,子類也不能拋出任何已檢查異常。例如,如果覆蓋JComponent.paintComponent()方法,那么    由于超類中沒有拋出任何異常,所以自定義的paintComponent()方法也不能拋出任何異常。

    如何拋出異常:
    譬如在讀一個輸入流時,如果期待的字節數為1024,而當讀到700字節的時候遇到一個EOF信號,這是我們未預期的,正是我們要拋出的異常,下面就是拋出改異常的語句

    1String readData() throws EOFException {
    2   while ({
    3      if(!in.hasNext()) {
    4          if(n<len)
    5            throw new EOFException();
    6     }
      
    7  }

    8}

    這是拋出已存在標準的異常,步驟為:
    1.找到一個合適的異常類
    2.創建該類的對象
    3.將異常拋出
    一旦拋出異常,該方法就不可能返回到調用者,這就意味著不必為返回的默認值或錯誤代碼擔憂了。

    在程序中可能會遇到任何標準異常都無法正確描述清楚的問題,這時候就需要定義自己的異常類,定義自己的異常類需要繼承自Exception 或者其子類,如繼承自IOException  一般要為自定義的Exception定義兩個構造函數,一個是默認的無參的構造函數,另一個是帶一個描述詳細錯誤信息String的構造函數(超類的toString()方法會打印出這些詳細信息。

    1public class MyException extends IOException {
    2
    3    MyException () {}
    4    
    5    MyException (String info) {
    6       super(info);
    7   }

    8}

    現在就可以像使用標準Exception類一樣使用自定義的Exception類了

    1String readData() throws MyException {
    2   while () {
    3      if(!in.hasNext()) {
    4          if(n<len)
    5            throw new MyException();
    6    }
      
    7 }

    8}

    可以用超類Throwable類的getMessage方法得到詳細的錯誤描述信息。或者toString()方法打印該信息。

    捕獲異常:
    如果語句exp1有可能拋出一個異常,或者說是調用了能夠拋出一個異常的方法,那么我們可以在程序中捕獲該異常

    1try {
    2 exp1;
    3 exp2;
    4 .
    5
    6}
     catch(Exception e) {
    7   e.
    8}

    9

    或者是并不捕獲,再次拋出該異常

    再次拋出異常,異常鏈:
    可以在catch塊中再次拋出新的異常,這樣可以隱藏原始的錯誤細節

    1catch(SQLException sqle) {
    2    throw new ServletException("caused by SQLException "+e.getMessage());
    3}

    另一種更好的方法
    既拋出高級的異常,又不會丟失原始的異常信息。

    1}catch(SQLException sqle) 
    2    Throwable ta=new ServletException("caused by SQLException");
    3    ta.setCause(e);
    4    throw ta;
    5}

    6當捕獲拋出的異常ta時,可以用ta.getCause(),得到原始的異常


    finally子句,發生三種情況時,會執行finally子句
    1.try代碼正常執行
    2.try代碼拋出一個在catch子句中捕獲的異常
    3.try代碼拋出一個未在catch子句中捕獲的異常
    就是說無論異常如何,finally子句中的代碼都會執行
    一般把一些資源回收的工作交給finally子句

    最好把try-catch塊和try-finally塊分開,這樣可以提高代碼的清晰度

     1try {
     2   try {
     3      //可能拋出IOException的語句
     4    }
     finally {
     5    is.close();
     6 }

     7}
     catch(IOExceotion e) {
     8   
     9}

    10

    這樣的另外一個好處是,可以catch到finally子句中拋出的異常。這也就引發了另外一個問題,如果在try和finally中都拋出了異常,而且是不同類型的,那么最后返回的是那一個呢?答案是finally中拋出的異常會覆蓋try中拋出的異常。從而丟失了try當中拋出的異常信息,讓拋出該方法的使用者不能跟蹤異常信息。所以,在finally中執行的語句最好是不要拋出異常,但是不幸的是我們有時候不得不在finally中執行一些清楚操作如:關閉輸入流InputStream,但是InputStream類的設計者并沒有這么設計(將會拋出異常)。

    由于finally子句總是會執行,所以在finally中包含return值時,是很危險的。假設我們利用return語句從try中返回,在方法返回之前finally子句的內容將會被執行。如果finally子句當中也包含一個return 語句,那么這個return語句所返回的值,將會覆蓋原先return的值。

    1public int f(int n) {
    2  try {
    3      return n*n;
    4    }
     finally {
    5     if(n==2)
    6       return 2;
    7  }

    8}

    在這里如果調用i=f(2);那么i的值將會是2而不是期望的2*2=4


    最后最重要的一點,關于一個方法是捕獲一個異常還是拋出給調用他的方法
    有一點規則   
                                    早拋出,晚捕獲,盡量把異常拋出給調用它的函數

    posted @ 2008-02-26 14:05 曉宇 閱讀(528) | 評論 (0)編輯 收藏

    JAVA 的字符編碼的問題

    首先講一講字符集
     JDK1.4版本引入了java.nio包加入了Charset類來統一字符集的轉換,字符集給出了雙字節Unicode碼序列與本地字符編碼中采用的字節序列之間的映射
    字符集的名稱不區分大小寫,可以用官方名稱或者任何一個別名調用靜態方法forName來獲得Charset

    1Charset charset=Charset.forName("GBK");
    Charset的aliases()方法返回一個Set<String>集合,表示該字符集所具有的所有別名
    Set<String> aliases=charset.aliases();
    查看虛擬機所支持的字符集以及名字可用Charset的靜態方法availableCharsets()返回一個SortedMap
    1 Map<String,Charset> map=Charset.availableCharsets();
    2            for(String setname:map.keySet()) {
    3                System.out.println(setname);
    4            }
    一旦有了字符集,就可以用他在本地字符串和Unicode字節序列之間轉換
    如下將一個字符串編碼為gbk    charset.encode(str)函數返回一個ByteBuffer對象
    1String str=new String("曉宇");
    2            ByteBuffer  buff = charset.encode(str);
    3            byte[] bytes=buff.array();
    同樣,可以將剛才按照GBK方式編碼的字節流解碼為正確的字符流
    1ByteBuffer by=ByteBuffer.wrap(bytes, 0, bytes.length);
    2      CharBuffer cb=charset.decode(by);//返回一個CharBuffer對象,已經將字節數組轉化為字符數組
    3      System.out.println("  "+cb.toString());


    實際上String類中集成了Charset對象的使用,String類有兩個用于編碼和解碼的函數

    1byte[] bytes = str.getBytes("CharsetName");  //將字符串str按照指定名稱的Charset進行編碼成字節數組
    2            String str2=new String(bytes,"CharsetName"); //將字節數組按照指定名稱的Charset解碼為字符串


    在輸入輸出流中有兩個用于將字節流轉化為指定編碼格式的字符流的類  InputStreamReader/OutputStreamReader
    這兩個類是讀寫流和讀寫器的橋梁 用法如下

    1InputStreamReader isr = new InputStreamReader(in, "charsetname");
    2            BufferedReader br = new BufferedReader(isr);//加入一個BufferedReader,可以用到該類的readLine()

    該用法在我的另外一篇socket備忘的里面體現了,可將網絡傳過來的utf-8格式編碼的字節流正確的解碼,以至于顯示的時候不會出現亂碼





    posted @ 2008-02-24 16:44 曉宇 閱讀(519) | 評論 (0)編輯 收藏

    SOCKET基礎 學習筆記

         摘要:     1package com;   2   3import java.io.BufferedReader;   4import java.io.ByteArrayOutputStream;   5import java.io.Filte...  閱讀全文

    posted @ 2008-02-22 16:26 曉宇 閱讀(238) | 評論 (0)編輯 收藏

    字節數組和其他類型的轉換

     

    //整數到字節數組的轉換 軟件測試專業網站:51Testing軟件測試網 h$_g8Lbx g s
       public byte[] intToByte(int intValue) {
    O R-v0OS&{;u0    byte[] result = new byte[4];
    e!sm#DN0    result[0] = (byte) ( (intValue & 0xFF000000) >> 24);軟件測試專業網站:51Testing軟件測試網3e Ou-l*l
        result[1] = (byte) ( (intValue & 0x00FF0000) >> 16);軟件測試專業網站:51Testing軟件測試網%F3hN!XoC
        result[2] = (byte) ( (intValue & 0x0000FF00) >> 8);
    d"TS)ro;L`;A:eI0    result[3] = (byte) ( (intValue & 0x000000FF));軟件測試專業網站:51Testing軟件測試網t1^O{;_,S"e `
        return result;軟件測試專業網站:51Testing軟件測試網q*~[? n M"i
      }

      //字節數組到整數的轉換 軟件測試專業網站:51Testing軟件測試網i f9``3@0LZK&R
      public static int byteToInt(byte[] b) { 軟件測試專業網站:51Testing軟件測試網Rb~,Ws"u1m
    public static int byteToInt(byte[] byteVal) {
    2X/cH bIM0      int result = 0;軟件測試專業網站:51Testing軟件測試網7e5~3p"J r\ _
          for (int i = 0; i < byteVal.length; i++) {
    1i {T q a2eT V_.^!Q0        int tmpVal = (byteVal[i] << (8 * (3 - i)));
    &?x%pQ4_9T7k0        switch (i) {軟件測試專業網站:51Testing軟件測試網A P/u[ C,J&FA#f
              case 0:軟件測試專業網站:51Testing軟件測試網B,}\z`]8UU
                tmpVal = tmpVal & 0xFF000000;軟件測試專業網站:51Testing軟件測試網*yS6X$y9n*md~
                break;
    piL/jY)lkZ?0          case 1:軟件測試專業網站:51Testing軟件測試網5D#YS%w3f X|g
                tmpVal = tmpVal & 0x00FF0000;軟件測試專業網站:51Testing軟件測試網c?Iu I w
                break;軟件測試專業網站:51Testing軟件測試網~&E3Vmp0_;}@
              case 2:
    6j3t1F;iX+K4{0            tmpVal = tmpVal & 0x0000FF00;軟件測試專業網站:51Testing軟件測試網5fD7H.i y R a/q
                break;
    ]5b:h MMa!K0          case 3:
    )^~_.\A0            tmpVal = tmpVal & 0x000000FF;
    t2}8J f7A E~eH2[0            break;軟件測試專業網站:51Testing軟件測試網\? d:MN#D#iN
            }
    MJ:c.rxWE0X"e"^*@0        result = result | tmpVal;軟件測試專業網站:51Testing軟件測試網9R lE\Q(g&SAJ
          }軟件測試專業網站:51Testing軟件測試網1ixe#~9]lyF},T
          return result;軟件測試專業網站:51Testing軟件測試網d,d"L^/fC?*upX
        }

      //字符到字節轉換
    Q N"P6tq.b@~0  public static byte[] charToByte(char ch){ 軟件測試專業網站:51Testing軟件測試網%]|X,~-vb'?$SU
        int temp=(int)ch; 軟件測試專業網站:51Testing軟件測試網'L9cx"B:` Ak
        byte[] b=new byte[2]; 軟件測試專業網站:51Testing軟件測試網2C8j1U/i1[ ls
        for (int i=b.length-1;i>-1;i--){
    'C3^]_V:qz0      b = new Integer(temp&0xff).byteValue();      //將最高位保存在最低位 軟件測試專業網站:51Testing軟件測試網 |S`"I h%YQU\(g
          temp = temp >> 8;       //向右移8位 軟件測試專業網站:51Testing軟件測試網!s/jv'Z2R
        } 軟件測試專業網站:51Testing軟件測試網R+C:w4LY4Xu!M
        return b; 軟件測試專業網站:51Testing軟件測試網%n+x/Y ZV`6T\
      }

      //字節到字符轉換 軟件測試專業網站:51Testing軟件測試網n:gg'^!_@NF p*@B
      public static char byteToChar(byte[] b){ 軟件測試專業網站:51Testing軟件測試網(}xOQ:b
        int s=0;
    ZuYw~ac0    if(b[0]>0) 軟件測試專業網站:51Testing軟件測試網'Tsc(rq
          s+=b[0]; 軟件測試專業網站:51Testing軟件測試網5G]%j*fg)wk$Z
        else 軟件測試專業網站:51Testing軟件測試網q#i2kb@
          s+=256+b[0];
    #iCo0kd+|i0    s*=256; 軟件測試專業網站:51Testing軟件測試網X%Zn?-k9h5q
        if(b[1]>0) 軟件測試專業網站:51Testing軟件測試網(E0v"C(Bv4Q
          s+=b[1];
    +{`z$m a R0    else 軟件測試專業網站:51Testing軟件測試網D^j aODKA3T
          s+=256+b[1]; 軟件測試專業網站:51Testing軟件測試網\ hv8We}U)b
        char ch=(char)s; 軟件測試專業網站:51Testing軟件測試網#JLO"h;NH*AHb0LLt!m
        return ch; 軟件測試專業網站:51Testing軟件測試網$g,Mu0?JT#Ef&xi
      }

      //浮點到字節轉換
    de4w8L-MpD8{\0  public static byte[] doubleToByte(double d){ 軟件測試專業網站:51Testing軟件測試網,~9g3FuQ9q;sMCa
        byte[] b=new byte[8]; 軟件測試專業網站:51Testing軟件測試網 UY:}1oV:\ {-uZ7Ed
        long l=Double.doubleToLongBits(d);
    ar8f3|"@b(g'L0    for(int i=0;i<b.length;i++){
    %t4roUTU;zgxI0      b=new Long(l).byteValue(); 軟件測試專業網站:51Testing軟件測試網*I%@7R#} bb0P
          l=l>>8;
    E$^+\"u!h!Y3^0    } 軟件測試專業網站:51Testing軟件測試網%])x)S)u)i,ul
        return b;
    n`(]3K k`0  }

      //字節到浮點轉換
    KwV!^Kt2q#p j0  public static double byteToDouble(byte[] b){
    :C.}9o GI9r0    long l;

        l=b[0]; 軟件測試專業網站:51Testing軟件測試網^4_/j4oL\*l b
        l&=0xff; 軟件測試專業網站:51Testing軟件測試網8x-fA;H.\
        l|=((long)b[1]<<8); 軟件測試專業網站:51Testing軟件測試網P\Z9tk
        l&=0xffff;
    u;A&Vs3n.p]*K/B0    l|=((long)b[2]<<16);
    )_ ^,F\ n3o0    l&=0xffffff;
    ` z3~*PIe aMK0    l|=((long)b[3]<<24);
    DM!ldb&|U(A*J0    l&=0xffffffffl;
    'i)k yh\0    l|=((long)b[4]<<32); 軟件測試專業網站:51Testing軟件測試網 T$D_Wr:M!_,E^a
        l&=0xffffffffffl;

        l|=((long)b[5]<<40); 軟件測試專業網站:51Testing軟件測試網g3m+GN$_)J1h,F.^(u.H d
        l&=0xffffffffffffl;
    ,gJ&o'u N7\Sp0    l|=((long)b[6]<<48);
    :m@s7\WCt+B0    l&=0xffffffffffffffl;
    ,x)T:eO e2d![0    l|=((long)b[7]<<56);
    }^MS.x%_7P0    return Double.longBitsToDouble(l); 軟件測試專業網站:51Testing軟件測試網Q)at;{k9Tq
      }

    posted @ 2008-02-22 15:06 曉宇 閱讀(754) | 評論 (0)編輯 收藏

    TREEMAP的排序機制

         摘要: 1package com.sf;  2  3import java.text.CollationKey;  4import java.text.Collator;  5import java.util.Comparator;  6import java.util.Map;  7...  閱讀全文

    posted @ 2008-02-19 15:16 曉宇 閱讀(2104) | 評論 (1)編輯 收藏

    HASHTABLE的內部實現

     

    public class TestHashtable {
        
    public static void main(String[] args){
            Hashtable ht 
    = new Hashtable();
            ht.put(
    "sichuan","chengdu"); //改變以下四行代碼的順序,可能會改變輸出內容的順序    
            ht.put("hunan","changsha");     
            ht.put(
    "beijing","beijing");    
            ht.put(
    "anhui","hefei");    
            
        Enumeration e 
    = ht.keys();
        
    while(e.hasMoreElements()) {
            Object key 
    = e.nextElement();
            Object value 
    = ht.get(key);            
            System.out.println(key 
    + " " + value + " " + key.hashCode() + " " + value.hashCode());
        }

            
        }

    }

     為了講述Hashtable鍵排序的問題,我們先來看Hashtable的結構圖:

    Hashtable.GIF

            從上面的結構圖可以看出,Hashtable的實質就是一個數組+鏈表。圖中的Entry就是鏈表的實現,Entry的結構中包含了對自己的另一個實例的引用next,用以指向另外一個Entry。而圖中標有數字的部分是一個Entry數組,數字就是這個Entry數組的index。那么往Hashtable增加鍵值對的時候,index會根據鍵的hashcode、Entry數組的長度共同決定,從而決定鍵值對存放在Entry數組的哪個位置。從這種意義來說,當鍵一定,Entry數組的長度一定的情況下,所得到的index肯定是相同的,也就是說插入順序應該不會影響輸出的順序才對。然而,還有一個重要的因素沒有考慮,就是計算index出現相同值的情況。譬如代碼中 "sichuan" 和 "anhui",所得到的index是相同的,在這個時候,Entry的鏈表功能就發揮作用了:put方法通過Entry的next屬性獲得對另外一個Entry的引用,然后將后來者放入其中。根據debug得出的結果,"sichuan", "anhui"的index同為2,"hunan"的index為6,"beijing"的index為1,在輸出的時候,會以index遞減的方式獲得鍵值對。很明顯,會改變的輸出順序只有"sichuan"和"anhui"了,也就是說輸出只有兩種可能:"hunan" - "sichuan" - "anhui" - "beijing"和"hunan" - "anhui" - "sichuan" - "beijing"。以下是運行了示例代碼之后,Hashtable的結果:

    Hashtable1.GIF

        
            以上的討論基于Java展開的,在C#中的Hashtable實現會有所不同,但是我相信兩者的設計應該是差不多的。感謝葉漂
    和quitgame,給了我思考的機會,也讓我感到了基礎知識的匱乏,看來是要補補基礎知識了。   

            [補充]:在Hashtable的實現代碼中,有一個名為rehash的方法用于擴充Hashtable的容量。很明顯,當rehash方法被調用
    以后,每一個鍵值對相應的index也會改變,也就等于將鍵值對重新排序了。這也是往不同容量的Hashtable放入相同的鍵值對會輸出不同的鍵值對序列的原因。在Java中,觸發rehash方法的條件很簡單:hahtable中的鍵值對超過某一閥值。默認情況下,該閥值等于hashtable中Entry數組的長度×0.75

    posted @ 2008-02-19 11:07 曉宇 閱讀(1663) | 評論 (0)編輯 收藏

    JDBC中的連接方式 thin模式 和 DataSource方式

    一、最常用的是thin模式   利用DriverManager得到Connection
    Class.forName("oracle.jdbc.driver.OracleDriver").newInstance(); 
    String url
    ="jdbc:oracle:thin:@localhost:1521:orcl"//orcl為數據庫的SID 
    String user="test"
    String password
    ="test"
    Connection conn
    = DriverManager.getConnection(url,user,password);

    二、JDBC的另外一種方式是三層結構,就是在應用服務器(tomcat/weblogic)上面建立DataSource
    1Context ctx=new InitiaContext(Hashtable env);
    2DataSource ds=(DataSource)ctx.lookUp("jdbc/OraDB");
    3Connection con=ds.getConnection();

    應用中配置如下:未測試,僅轉載。
       
    一、配置server.xml
    找到配置發布應用程序的地方:<Context path=”” docBase=”d:\_webs”/>
    將這個改為:
    <Context path="" docBase="D:\ _web" debug="0">
     
    <!—聲明一個數據源,程序通過JNDI找到該數據源。
    name指出數據源的名稱為jdbc/OraDB,
    auth表明連接池管理的權限,
    type指出該數據源的類型-->
    <Resource name="jdbc/OraDB" auth="SERVLET" type="javax.sql.DataSource"/>
     
    <!—配置該數據源的屬性
    name指出數據源的名稱
    -->
    <ResourceParams name="jdbc/OraDB">
     
    <!—連接數據庫的用戶名-->
    <parameter>
    <name>username</name>
               <value>chenws</value>
    </parameter>
        
     <!—連接數據庫的密碼-->
     <parameter>
               <name>password</name>
            <value>admin</value>
     </parameter>
               
     <!—連接數據庫的jdbc驅動程序-->
     <parameter>
               <name>driverClassName</name>
               <value>oracle.jdbc.driver.OracleDriver</value>
     </parameter>
     
      <!—連接數據庫的url-->
     <parameter>
               <name>url</name>
               <value>jdbc:oracle:thin:@192.168.39.215:1521:jetchin</value>
     </parameter>
    </ResourceParams>
            
    </Context>
     
    實際上,配置數據源的屬性不止以上那些,還有其他的比如連接時間的限制,連接數量的限制等等。這里我們并沒有給出,只是使用了tomcat提供的默認屬性罷了。
    注意:要讓該數據源有效,必須將oracleclasses12.zip改名為classes12.jar,然后拷貝到
    tomcat安裝目錄下的/common/lib中。
     
    二、配置web.xml
    web應用程序目錄下的web-inf中,打開web.xml,加入如下的配置:
    定義數據源參照:
    <resource-ref>
     <!—數據源描述,可有可無-->
     <description>Oracle Datasource example</description>
     
      <!--數據源名稱-->
     <res-ref-name>jdbc/OraDB</res-ref-name>
     
      <!—數據源類型-->
    <res-type>javax.sql.DataSource</res-type>
     
     <!—連接池管理權限-->
     <res-auth>SERVLET</res-auth>
    </resource-ref>


    另外在weblogic中進行配置可參考如下文章:


    WebLogic Server7.0中Oracle的JDBC Pool/DataSource配置指南
     
     

    第一步,去oracle下載最新的 oracle JDBC driver。

    一共2個文件,ojdbc14.jar和nls_charset12.zip。
    第一個文件是驅動程序所在,第二個是支持國際化的包。

    接下來,把這兩個文件加入 WLS 的 classpath。
    修改 Bea\Weblogic\server\bin\startWLS.cmd(或者相應的Unix啟動文件,.sh結尾的),

    在文件開頭加入 PRE_CLASSPATH=C:\ojdbc14.jar;C:\nls_charset12.zip。注意文件的路徑。

    最后可以考慮把Bea\Weblogic\server\lib\classes12.zip刪除,我不保證正確性,只是怕有兼容性問題。

    最后,啟動weblogic server,進入console,在Connection Pool里邊,填入一下資料。

    General欄目:

    Name:MyOracle_CP

    URL:jdbc:oracle:thin:@server:port:sid (自己按照情況修改!)
    比如:jdbc:oracle:thin:@192.168.0.2:1521:Crystal

    DriverName:oracle.jdbc.driver.OracleDriver

    Properties
    user=SYS (這里是用戶,最好不要用SYS,SYSTEM等系統用戶)

    ACL Name: (空)

    password: 用戶密碼

     


    如圖:



    Connection欄目:


    CapacityIncrement = 50
    MaxCapacity = 100
    Capacity Increment: 5
    Login Delay Seconds: 0 seconds
    Refresh Period: 10 minutes
    Supports Local Transaction 不要打勾
    Allow Shrinking 打勾
    Shrink Period: 15 minutes
    Prepared Statement Cache Size: 5

    如圖:



    Test欄目:

    TestTableName:SCOTT.EMP (這里需要改動,適應你自己的表,wls用來做連接測試用的)
    TestConnectionsOnReleasetrue 打勾
    TestConnectionsOnReservetrue 打勾

    最后,點擊Apply,然后去Targets,選中你的server,點右箭頭,使你的server進入Chosen欄目,最后Apply。(如圖)




    查看dos窗口,是否有錯誤,如果沒有則繼續,如果有的話,自己查看填寫的內容。

    至此,Connection Pool已經配置完畢。


    接下來,該配置(TX)DataSource了。

    從昨天的panel里邊,單擊DataSources/TX DataSources,進入配置界面。

    1。單擊Configure a new JDBC Tx Data Source..配置新的datasource

    2。填入以下數據:
    Name: MyDataSource (自己隨便起的)
    JNDI: jdbc/OracleDS (這里就是你lookup的時候填入的名字,自己想吧。
    Pool Name: MyOracle_CP (一定要對應你的Connection Pool的 Name)

    把后邊兩項打上勾。

    第一個是模擬2階提交模式,如果數據庫driver本身不支持的話。(就是XA標準,分布式提交)

    第二個是 行預讀。如圖:

     

     

     

     

     

     

     

     

     

     

     

     

    點擊Create,然后去Targets里邊,把你的 server放入chosen,點擊apply。。

    配置完成。。。

    最后檢查你的DataSource是否已經成功部屬:

    進入你的server的JNDI Tree,很容易就可以看到的。:)。如圖:





    posted @ 2008-02-19 10:22 曉宇 閱讀(3725) | 評論 (0)編輯 收藏

    JDBC中的批處理

    JDBC中 有兩個批處理函數  addBatch(String)  和 executeBatch();

    有兩種方法可以進行批處理 第一種是用Statement  另外一種是PreparedStatement

    一、使用Statement 

     

    1Statement sm=con.createStatement();
    2String sql="insert into errbills values()";
    3sm.addBatch(sql);
    4sql="delete from errbills where bno='124'";
    5sm.addBatch(sql);
    6sm.executeBatch();


    二、使用PreparedStatement

    1String sql="delete from errblls where bno=?";
    2PreparedStatement ps=con.prepareStatement(sql);
    3for(int i=0;i<10;i++{
    4   ps.setString(i);
    5   ps.addBatch();
    6}

    7ps.executeBatch();
    8

    注意使用Statement和PreparedStatement的區別  Statement的addBatch(String s)是帶參數的(所要執行的SQL語句) 而PreparedStatement是不帶參數的

    在JDBC中使用批處理可以提高執行速度,因為我們不需要維護多條語句,所有的操作都是在一條語句里面完成,這樣數據庫服務器不必要為每條語句都分配和維護資源(指針等),可以減輕數據庫服務器的壓力

    posted @ 2008-02-19 09:50 曉宇 閱讀(535) | 評論 (1)編輯 收藏

    JMS基礎

    JMS簡介


    1. JMS基本概念
    JMS(Java Message Service)是訪問企業消息系統的標準API,它便于消息系
    統中的Java應用程序進行消息交換,并且通過提供標準的產生、發送、接收消息的接口簡化企業應用的開發。


    2. JMS基本功能
    JMS是用于和面向消息的中間件相互通信的應用程序接口。它既支持點對點(point-to-point)的域,又支持發布/訂閱(publish/subscribe)類型的域,并且提供對下列類型的支持:經認可的消息傳遞,事務型消息的傳遞,一致性消息和具有持久性的訂閱者支持。JMS還提供了另一種方式來對您的應用與舊的后臺系統相集成。
    3. WebLogic JMS Server介紹
    WebLogic Server8.1符合JAVA規范,并通過Sun Microsystems J2EE 1.3認
    證.作為WebLogic的一部分,當然WebLogic JMS Server也完全遵從JMS規范,還支持集群,并可以應用于實際企業系統.下圖是WebLogic JMS Server體系結構.圖中可以看到WebLogic JMS Server主要組件有: WebLogic JMS servers(用于消息通信),Java客戶端,JNDI(用于域名查找), 后備存儲(用于持久消息存儲,基于文件或者JDBC數據庫).

    二. WebLogic JMS特性
    1. 消息通信模型
    JMS 支持兩種消息通信模型:點到點(point-to-point)(PTP)模型和發布/訂閱(Pub/Sub)模型。除了下列不同之外,這兩種消息通信模型非常地相似:
    PTP 模型規定了一個消息只能有一個接收者;Pub/Sub 模型允許一個消息可以有多個接收者。
    2. 消息組成
    消息傳遞系統的中心就是消息。
    一條 Message 分為三個組成部分:
    · 頭(header)是個標準字段集,客戶機和供應商都用它來標識和路由消息。
    · 屬性(property)支持把可選頭字段添加到消息。如果您的應用程序需要不使用標準頭字段對消息編目和分類,您就可以添加一個屬性到消息以實現這個編目和分類。提供 set<Type>Property(...) 和 get<Type>Property(...) 方法以設置和獲取各種 Java 類型的屬性,包括 Object。JMS 定義了一個供應商選擇提供的標準屬性集。
    · 消息的主體(body)包含要發送給接收應用程序的內容。每個消息接口特定于它所支持的內容類型。
    JMS 為不同類型的內容提供了它們各自的消息類型,但是所有消息都派生自 Message 接口。
    · StreamMessage:包含 Java 基本數值流,用標準流操作來順序的填充和讀取。
    · MapMessage:包含一組名/值對;名稱為 string 類型,而值為 Java 的基本類型。
    · TextMessage:包含一個 String。
    · ObjectMessage:包含一個 Serializable Java 對象;能使用 JDK 的集合類。
    · BytesMessage:包含未解釋字節流: 編碼主體以匹配現存的消息格式。
    · XMLMessage: 包含XML內容。擴展TextMessage,XMLMessage 類型的使用,使得消息過濾非常便利。
    3. 消息確認模式
    非事務性會話中,應用程序創建的會話有5 種確認模式,而在事務性會話中,確認模式被忽略。
    五種確認模式說明:
    · AUTO_ACKNOWLEDGE:自動確認模式。一旦接收方應用程序的方法調用從處理消息處返回,會話對象就會確認消息的接收。
    · CLIENT_ACKNOWLEDGE:客戶端確認模式。會話對象依賴于應用程序對被接收的消息調用一個acknowledge()方法。一旦這個方法被調用,會話會確認最后一次確認之后所有接收到的消息。這種模式允許應用程序以一個調用來接收,處理并確認一批消息。注意:在管理控制臺中,如果連接工廠的Acknowledge Policy(確認方針)屬性被設置為"Previous"(提前),但是你希望為一個給定的會話確認所有接收到的消息,那么就用最后一條消息來調用acknowledge()方法。
    · DUPS_OK_ACKNOWLEDGE:允許副本的確認模式。一旦接收方應用程序的方法調用從處理消息處返回,會話對象就會確認消息的接收;而且允許重復確認。在需要考慮資源使用時,這種模式非常有效。注意:如果你的應用程序無法處理重復的消息的話,你應該避免使用這種模式。如果發送消息的初始化嘗試失敗,那么重復的消息可以被重新發送。
    · NO_ACKNOWLEDGE:不確認模式。不確認收到的消息是需要的。消息發送給一個NO_ACKNOWLEDGE 會話后,它們會被WebLogic 服務器立即刪除。在這種模式下,將無法重新獲得已接收的消息,而且可能導致下面的結果:1. 消息可能丟失;和(或者)另一種情況:2. 如果發送消息的初始化嘗試失敗,會出現重復消息被發送的情況。
    · MULTICAST_NO_ACKNOWLEDGE:IP組播下的不確認模式,同樣無需確認。發送給一個MULTICAST_NO_ACKNOWLEDGE會話的消息, 會共享之前所述的NO_ACKNOWLEDGE 確認模式一樣的特征。這種模式支持希望通過IP 組播方式進行消息通信的應用程序,而且無需依賴會話確認提供的服務質量。注意:如果你的應用程序無法處理消息的丟失或者重復,那么你應該避免使用這種模式。如果發送消息的初始化嘗試失敗的話,重復的消息可能會被再次發送。
    注:在上表的5 種確認模式中,AUTO_ACKNOWLEDGE ,DUPS_OK_ACKNOWLEDGE 和
    CLIENT_ACKNOWLEDGE 是JMS 規范定義的,NO_ACKNOWLEDGE 和MULTICAST_NO_ACKNOWLEDGE是WebLogic JMS 提供的。
    三. 配置JMS
    1. 創建連接工廠
    (1) 啟動WebLogic Server8.1,登錄控制臺,選中JMS Connection Factories節點,點擊右邊的" Configure a new JMS Connection Factory...";

    (2) 填寫連接工廠的名稱SendJMSFactory和JNDI名稱SendJMSFactory,點擊"Create";

    (3) 勾上"myserver",將SendJMSFactory應用到myserver;

    2. 定義后備存儲
    (1) 選中JMS Stores節點,點擊右邊的" Configure a new JMS Connection Factory...";

    (2) 填寫文件后備存儲的名稱SendFileStore和目錄Directionary E:\BEA\user_projects\domains\mydomain\sendfilestore,點擊"Create".

    3. 創建JMS服務器
    (1) 選中JMS Servers節點,點擊右邊的" Configure a new JMSServer...";

    (2) 填寫JMS服務器的名稱SendJMSServer和Paging Store設為" SendFileStore",點擊"Create";

    (3) Target選中"myserver",將SendJMSServer應用到myserver.

    4. 創建消息隊列
    (1) 展開"SendJMSServer"節點,點擊" Configure a new JMS Queue...";

    (2) 填寫消息隊列的名稱SendJMSQueue和JNDI名稱SendJMSQueue,點擊"Create";

    四. JMS應用程序
    一個 JMS 應用程序由下列元素組成:
    · JMS 客戶機。 用 JMS API 發送和接收消息的 Java 程序。
    · 非 JMS(Non-JMS)客戶機。 認識到這一點很重要 - 舊的程序經常成為整個 JMS 應用程序的一部分,而且它們的包含應該在設計時預先考慮。
    · 消息。 在 JMS 和非 JMS 客戶機之間交換的消息的格式和內容是 JMS 應用程序設計所必須考慮的部分。
    · JMS 供應商。供應商必須提供特定于其 MOM 產品的具體的實現。
    · 受管對象。 消息傳遞系統供應商的管理員創建了一個對象,它獨立于供應商專有的技術。包括連接工廠ConnectionFactory和目的Destination。
    一種典型的 JMS 程序需要經過下列步驟才能開始消息產生和使用:
    · 通過 JNDI 查找 ConnectionFactory。
    · 通過 JNDI 查找一個或多個 Destination。
    · 用 ConnectionFactory 創建一個 Connection。
    · 用 Connection 創建一個或多個 Session。
    · 用 Session 和 Destination 創建所需的 MessageProducer 和 MessageConsumer。
    · 啟動 Connection。
    下面利用上面配置的JMS資源演示點對點消息發送和接收的過程。
    五. 設計消息發送端
    1. 使用的JMS資源
    服務器URL: t3://localhost:80
    連接工廠: SendJMSFactory
    隊列: SendJMSQueue
    2. 設計步驟
    · 初始化JNDI Tree
    Hashtable env = new Hashtable();
    env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
    env.put(Context.PROVIDER_URL, PROVIDER_URL);
    return new InitialContext(env);
    · lookup ConnectionFactory
    qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
    · lookup Destination
    queue = (Queue) ctx.lookup(queueName);
    · 用 ConnectionFactory 創建Connection
    qcon = qconFactory.createQueueConnection();
    · 用 Connection 創建一個Session
    qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
    · 用 Session 和 Destination 創建MessageProducer
    qsender = qsession.createSender(queue);
    · 啟動 Connection。
    qcon.start();
    · 發送消息
    msg = qsession.createTextMessage();
    msg.setText(message);
    qsender.send(msg);
    3. 源代碼
    package jmssample;

    import java.util.Hashtable;
    import javax.jms.*;
    import javax.naming.Context;
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;

    /** This example shows how to establish a connection
    * and send messages to the JMS queue. The classes in this
    * package operate on the same JMS queue. Run the classes together to
    * witness messages being sent and received, and to browse the queue
    * for messages. The class is used to send messages to the queue.
    *
    * @author Copyright (c) 1999-2003 by BEA Systems, Inc. All Rights Reserved.
    */
    public class QueueSend
    {
    // Defines the JNDI context factory.
    public final static String JNDI_FACTORY="weblogic.jndi.WLInitialContextFactory";

    // Defines the JNDI provider url.
    public final static String PROVIDER_URL=" t3://localhost:80";

    // Defines the JMS connection factory for the queue.
    public final static String JMS_FACTORY="SendJMSFactory";

    // Defines the queue.
    public final static String QUEUE="SendJMSQueue";


    private QueueConnectionFactory qconFactory;
    private QueueConnection qcon;
    private QueueSession qsession;
    private QueueSender qsender;
    private Queue queue;
    private TextMessage msg;

    /**
    * Creates all the necessary objects for sending
    * messages to a JMS queue.
    *
    * @param ctx JNDI initial context
    * @param queueName name of queue
    * @exception NamingException if operation cannot be performed
    * @exception JMSException if JMS fails to initialize due to internal error
    */
    public void init(Context ctx, String queueName)
    throws NamingException, JMSException
    {
    qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
    qcon = qconFactory.createQueueConnection();
    qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
    queue = (Queue) ctx.lookup(queueName);
    qsender = qsession.createSender(queue);
    msg = qsession.createTextMessage();
    qcon.start();
    }

    /**
    * Sends a message to a JMS queue.
    *
    * @param message message to be sent
    * @exception JMSException if JMS fails to send message due to internal error
    */
    public void send(String message) throws JMSException {
    msg.setText(message);
    qsender.send(msg);
    }

    /**
    * Closes JMS objects.
    * @exception JMSException if JMS fails to close objects due to internal error
    */
    public void close() throws JMSException {
    qsender.close();
    qsession.close();
    qcon.close();
    }
    /** main() method.
    *
    * @param args WebLogic Server URL
    * @exception Exception if operation fails
    */
    public static void main(String[] args) throws Exception {
    InitialContext ic = getInitialContext();
    QueueSend qs = new QueueSend();
    qs.init(ic, QUEUE);
    readAndSend(qs);
    qs.close();
    }

    private static void readAndSend(QueueSend qs)
    throws IOException, JMSException
    {
    BufferedReader msgStream = new BufferedReader(new InputStreamReader(System.in));
    String line=null;
    boolean quitNow = false;
    do {
    System.out.print("Enter message (\"quit\" to quit): ");
    line = msgStream.readLine();
    if (line != null && line.trim().length() != 0) {
    qs.send(line);
    System.out.println("JMS Message Sent: "+line+"\n");
    quitNow = line.equalsIgnoreCase("quit");
    }
    } while (! quitNow);

    }

    private static InitialContext getInitialContext()
    throws NamingException
    {
    Hashtable env = new Hashtable();
    env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
    env.put(Context.PROVIDER_URL, PROVIDER_URL);
    return new InitialContext(env);
    }

    }

    六. 設計消息接收端
    1. 使用的JMS資源
    服務器URL: t3://localhost:80
    連接工廠: SendJMSFactory
    隊列: SendJMSQueue
    2. 設計步驟
    · 初始化JNDI Tree
    Hashtable env = new Hashtable();
    env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
    env.put(Context.PROVIDER_URL, PROVIDER_URL);
    return new InitialContext(env);
    · lookup ConnectionFactory
    qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
    · lookup Destination
    queue = (Queue) ctx.lookup(queueName);
    · 用 ConnectionFactory 創建Connection
    qcon = qconFactory.createQueueConnection();
    · 用 Connection 創建一個Session
    qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
    · 用 Session 和 Destination 創建MessageConsumer
    qreceiver = qsession.createReceiver(queue);
    · 設置監聽
    qreceiver.setMessageListener(this);
    · 啟動 Connection
    qcon.start();
    3. 源代碼
    package jmssample;

    import java.util.Hashtable;
    import javax.jms.*;
    import javax.naming.Context;
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
    import java.util.Hashtable;
    import javax.jms.*;
    import javax.naming.Context;
    import javax.naming.InitialContext;
    import javax.naming.NamingException;

    /**
    * This example shows how to establish a connection to
    * and receive messages from a JMS queue. The classes in this
    * package operate on the same JMS queue. Run the classes together to
    * witness messages being sent and received, and to browse the queue
    * for messages. This class is used to receive and remove messages
    * from the queue.
    *
    * @author Copyright (c) 1999-2003 by BEA Systems, Inc. All Rights Reserved.
    */
    public class QueueReceive implements MessageListener
    {
    // Defines the JNDI context factory.
    public final static String JNDI_FACTORY="weblogic.jndi.WLInitialContextFactory";

    // Defines the JNDI provider url.
    public final static String PROVIDER_URL=" t3://localhost:80";

    // Defines the JMS connection factory for the queue.
    public final static String JMS_FACTORY="SendJMSFactory";

    // Defines the queue.
    public final static String QUEUE="SendJMSQueue";

    private QueueConnectionFactory qconFactory;
    private QueueConnection qcon;
    private QueueSession qsession;
    private QueueReceiver qreceiver;
    private Queue queue;
    private boolean quit = false;

    /**
    * Message listener interface.
    * @param msg message
    */
    public void onMessage(Message msg)
    {
    try {
    String msgText;
    if (msg instanceof TextMessage) {
    msgText = ((TextMessage)msg).getText();
    } else {
    msgText = msg.toString();
    }

    System.out.println("Message Received: "+ msgText );

    if (msgText.equalsIgnoreCase("quit")) {
    synchronized(this) {
    quit = true;
    this.notifyAll(); // Notify main thread to quit
    }
    }
    } catch (JMSException jmse) {
    jmse.printStackTrace();
    }
    }

    /**
    * Creates all the necessary objects for receiving
    * messages from a JMS queue.
    *
    * @param ctx JNDI initial context
    * @param queueName name of queue
    * @exception NamingException if operation cannot be performed
    * @exception JMSException if JMS fails to initialize due to internal error
    */
    public void init(Context ctx, String queueName)
    throws NamingException, JMSException
    {
    qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
    qcon = qconFactory.createQueueConnection();
    qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
    queue = (Queue) ctx.lookup(queueName);
    qreceiver = qsession.createReceiver(queue);
    qreceiver.setMessageListener(this);
    qcon.start();
    }

    /**
    * Closes JMS objects.
    * @exception JMSException if JMS fails to close objects due to internal error
    */
    public void close()throws JMSException
    {
    qreceiver.close();
    qsession.close();
    qcon.close();
    }
    /**
    * main() method.
    *
    * @param args WebLogic Server URL
    * @exception Exception if execution fails
    */

    public static void main(String[] args) throws Exception {

    InitialContext ic = getInitialContext();
    QueueReceive qr = new QueueReceive();
    qr.init(ic, QUEUE);

    System.out.println("JMS Ready To Receive Messages (To quit, send a \"quit\" message).");

    // Wait until a "quit" message has been received.
    synchronized(qr) {
    while (! qr.quit) {
    try {
    qr.wait();
    } catch (InterruptedException ie) {}
    }
    }
    qr.close();
    }

    private static InitialContext getInitialContext()
    throws NamingException
    {
    Hashtable env = new Hashtable();
    env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
    env.put(Context.PROVIDER_URL, PROVIDER_URL);
    return new InitialContext(env);
    }


    }
    七. 測試消息發送和接收
    1. 設置WebLogic Classpath;


    2. 轉到發送接收程序目錄編譯文件;

    3. 執行接受程序;
    4. 打開另一窗口,執行發送程序;


    5. 輸入發送消息"quit",接收程序結束.


    總結
    本文先簡要介紹了JMS的一些基本概念,繼而引入了WebLogic JMS Server的體系結構和相關特性。在此基礎之上,圖文并茂地講述了JMS在WebLogic Server 8.1上的配置。最后在解剖JMS應用程序框架的同時,以點對點為例演示了JMS的發送接收消息流程。 

    posted @ 2008-02-18 15:36 曉宇 閱讀(2091) | 評論 (1)編輯 收藏

    JMS

         摘要: 一、初始化                    String CTX_FACTORY="com.sun.fscontext.RefContextFactory";   &nb...  閱讀全文

    posted @ 2008-02-18 11:35 曉宇 閱讀(370) | 評論 (0)編輯 收藏

    主站蜘蛛池模板: 一级毛片免费不卡直观看| 亚洲精品视频观看| 免费v片在线观看品善网| 永久黄网站色视频免费直播 | 亚洲黄色高清视频| 亚洲爆乳无码专区| 亚洲av福利无码无一区二区 | 日韩精品免费一区二区三区| 成人毛片免费在线观看| 久久WWW色情成人免费观看| 亚洲天堂免费在线| 妞干网在线免费视频| 免费无遮挡无码视频网站| 国产美女无遮挡免费视频| 四虎永久精品免费观看| 亚洲成A人片77777国产| 亚洲午夜福利AV一区二区无码 | av无码久久久久不卡免费网站| 免费视频爱爱太爽了| 久草在视频免费福利| 成人免费无码大片a毛片软件| 精品久久免费视频| 亚洲国产一区二区三区| 亚洲精品美女久久777777| 亚洲另类激情综合偷自拍| 亚洲女人初试黑人巨高清| 亚洲人成色99999在线观看| 粉色视频在线观看www免费| aaa毛片免费观看| 免费A级毛片无码A∨免费| 毛片免费视频播放| 免费一级一片一毛片| 亚洲热妇无码AV在线播放| 亚洲精品视频免费看| 亚洲人成网亚洲欧洲无码| eeuss影院免费直达入口| 久久99精品视免费看| 国产一卡二卡3卡四卡免费| 国产精品冒白浆免费视频 | 国产福利在线免费| 免费大学生国产在线观看p|