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

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

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

    隨筆-31  評論-2  文章-0  trackbacks-0
      2008年10月7日

    1 set和multiset容器的能力
    set 和multiset容器的內部結構通常由平衡二叉樹(balanced binary tree)來實現。當元素放入容器中時,會按照一定的排序法則自動排序,默認是按照less<>排序規則來排序。這種自動排序的特性加速了元 素查找的過程,但是也帶來了一個問題:不可以直接修改set或multiset容器中的元素值,因為這樣做就可能違反了元素自動排序的規則。如果你希望修 改一個元素的值,必須先刪除原有的元素,再插入新的元素。

    2 set和multiset容器的操作
    Constructor and Destructor
    • set c: 創建一個空的set或multiset容器
    • set c(op): 創建一個空的使用op作為排序法則的set或multiset容器
    • set c1(c2): 創建一個已存在的set或multiset容器的復制品,容器的類型和所有元素一同復制
    • set c(beg, end): 創建一個set或multiset容器,并且以[beg, end)范圍中的元素進行初始化
    • set c(beg, end, op): 創建一個使用op作為排序法則的set或multiset容器,并且以[beg, end)范圍中的元素進行初始化
    • c.~set(): 容器的析構函數,銷毀所有的元素,釋放所有的分配內存
    上面的set可以是下面幾種形式:
    • set<type>: 以less<>為排序法則的set
    • set<type, op>: 以op為排序法則的set
    • multiset<type>: 以less<>為排序法則的multiset
    • multiset<type, op>: 以op為排序法則的multiset
    從上面我們可以看到,可以從兩個地方來指定排序法則:
    (1)作為模板參數
    例如:std::set<int, greater<int> > col1;
    這種情況下,排序法則本身作為容器類型的一部分。對于一個set或者multiset容器,只有當元素類型和排序法則類型都相同時,他們的類型才被認為相同,否則就是不同類型的容器。

    (2)作為構造函數參數
    例如:std::set<int> col1(greater<int>);
    這種情況下指定的排序法則不作為容器類型的一部分,你可以為相同類型的容器指定不同的排序規則。這通常應用于要求相同的容器類型,但排序規則可以不同的場合。

    Size and Comparing
    set 和multiset容器同樣提供size(), empty(), max_size()三個關于查詢元素數目的接口,提供==, !=, <, <=, >, >=等比較操作符。但值得注意的是比較操作符只針對相同類型的容器,元素類型和排序法則類型都必須相同。

    Special Search Operations
    set和multiset容器的內部結構對于元素的查找提供了優化空間,所以它們提供了一些特殊的查找接口,這些查找操作通常要比同名的通用算法高效,所以在相同的條件下應該優先使用這些接口。
    • count(val): 返回容器中值等于val的元素數目。
    • find(val): 返回容器中值等于val的第一個元素的iterator位置;如果沒有匹配元素,則返回end()位置。
    • lower_bound(val): 返回容器中第一個值大于或等于val的元素的iterator位置。
    • upper_bound(val): 返回容器中第一個值大于val的元素的iterator位置。
    • equal_range(val): 返回容器中值等于val的所有元素的范圍[beg, end)組成的pair<beg, end> 。
    下面我們看一個使用lower_bound(), upper_bound和equal_range(val)例子,以加深對它們的理解:
    #include <iostream>
    #include <set>
    #include "print.hpp"
    using namespace std;
    int main()
    {
        multiset<int> col1;

        col1.insert(2);
        col1.insert(5);
        col1.insert(4);
        col1.insert(6);
        col1.insert(1);
        col1.insert(5);

        PRINT_ELEMENTS(col1, "col1: ");
        cout << endl;

        multiset<int>::const_iterator pos;
        pair<multiset<int>::iterator, multiset<int>::iterator> range;

        cout << "lower_bound(3): " << *col1.lower_bound(3) << endl;
        cout << "upper_bound(3): " << *col1.upper_bound(3) << endl;
        range = col1.equal_range(3);
        cout << "equal_range(3): " << *range.first << " " << *range.second << endl;
        cout << "elements with value(3): ";
        for (pos = range.first; pos != range.second; ++pos)
        {
            cout << *pos << " ";
        }
        cout << endl;
        cout << endl;

        cout << "lower_bound(5): " << *col1.lower_bound(5) << endl;
        cout << "upper_bound(5): " << *col1.upper_bound(5) << endl;
        range = col1.equal_range(5);
        cout << "equal_range(5): " << *range.first << " " << *range.second << endl;
        cout << "elements with value(5): ";
        for (pos = range.first; pos != range.second; ++pos)
        {
            cout << *pos << " ";
        }
        cout << endl;
    }
    執行結果如下:
    col1: 1 2 4 5 5 6 

    lower_bound(3): 4
    upper_bound(3): 4
    equal_range(3): 4 4
    elements with value(3): 

    lower_bound(5): 5
    upper_bound(5): 6
    equal_range(5): 5 6
    elements with value(5): 5 5 

    Assignment
    set和multiset容器只提供最基本的賦值操作:
    • c1 = c2: 把c2的所有元素復制到c1中,同時c1原有的元素被銷毀。
    • c1.swap(c2): 交換c1和c2的元素。
    • swap(c1, c2): 同上,只不過這是一個通用算法。
    需要注意的是兩個容器的類型要一致(包括元素類型和排序法則類型)。

    Inserting and Removing Elements
    set和multiset容器的插入和刪除元素接口跟其他容器也非常類似,但在細節上卻存在差別。
    • c.insert(elem): 在容器中插入元素elem的一份拷貝,并返回新元素的iterator位置;如果是set容器,同時還返回是否插入成功的標志。
    • c.insert(pos, elem): 在容器中插入元素elem的一份拷貝,并返回新元素的iterator位置;因為set和multiset容器的元素是自動排序的,所以pos位置只是插入位置的一個提示,設置恰當的話,可以提高插入元素的效率。
    • c.insert(beg, end): 在容器中插入[beg, end)范圍中所有元素的拷貝,沒有返回值。
    • c.erase(val): 刪除容器中所有值為val的元素,返回刪除元素的數目。
    • c.erase(pos): 刪除容器中位置pos處的元素,沒有返回值。
    • c.erase(beg, end): 刪除容器中[ben, end)范圍內所有的元素,沒有返回值。
    • c.clear(): 刪除容器中所有元素,使容器成為空容器。

    其中我們重點說一下c.insert(elem)接口。
    對于set容器,它的定義如下:
    pair<iterator, bool> insert(const TYPE& val);
    而對于multiset容器,它的定義如下:
    iterator insert(const TYPE& val);
    它 們的不同就是set容器的insert接口返回的是一個pair<iterator, bool>,而multiset容器的insert接口直接返回一個iterator。這是因為set容器中不允許有重復的元素,如果容器中已經存 在一個跟插入值相同的元素,那么插入操作就會失敗,而pair中的bool值就是標識插入是否成功的。而multiset不存在這個問題。

    3 set和multiset容器的異常處理
    因為set和multiset容器的獨特內部結構,當發生異常時,也可以把影響減到最小。也就是說,跟list容器一樣,set和multiset容器的操作要么成功,要么對原有容器沒有影響。

    4 運行時指定排序法則
    通常情況下,我們是在定義容器時指定排序法則,就像下面形式:
    std::set<int, greater<int> > col1;
    或者
    std::set<int> col1;    //use default sorting criterion less<>

    然而,如果你需要在運行時動態指定容器的排序法則,或者你希望對于相同的容器類型卻有著不同的排序法則,那么就要做一些特殊處理。下面我們看一個例子:
    #include <iostream>
    #include <set>
    #include "print.hpp"
    using namespace std;

    template <typename T>
    class RuntimeCmp 
    {
        public:
            enum cmp_mode {normal, reverse};
        private:
            cmp_mode mode;
        public:
            RuntimeCmp(cmp_mode m = normal) : mode(m) {}

            bool operator() (const T& t1, const T& t2)
            {
                return mode == normal ? t1 < t2 : t1 > t2;
            }

            bool operator== (const T& rhv) 
            {
                return mode == rhv.mode;
            }
    };

    typedef set<int, RuntimeCmp<int> > IntSet;

    //pre-declare
    void fill(IntSet& col1);

    int main()
    {
        IntSet col1;
        fill(col1);
        PRINT_ELEMENTS(col1, "col1: ");

        RuntimeCmp<int> reverse_cmp(RuntimeCmp<int>::reverse);
        IntSet col2(reverse_cmp);
        fill(col2);
        PRINT_ELEMENTS(col2, "col2: ");

        if (col1 == col2) 
        {
            cout << "col1 and col2 is equal" <<endl;
        }
        else
        {
            if (col1 < col2) 
            {
                cout << "col1 is less than col2" << endl;
            }
            else 
            {
                cout << "col1 is greater than col2" << endl;
            }
        }
        return 0;
    }

    void fill(IntSet& col1) 
    {
        col1.insert(2);
        col1.insert(3);
        col1.insert(6);
        col1.insert(5);
        col1.insert(1);
        col1.insert(4);
    }
    運行結果如下:
    col1 1 2 3 4 5 6 
    col2 6 5 4 3 2 1 
    col1 is less than col2

    這里例子中,col1和col2有著相同的類型:set<int, RuntimeCmp<int> >,但是它們的排序法則卻不相同,一個升序,一個降序。這都是通過自定義的函數對象來實現的,所以函數對象比普通函數有著更加靈活與強大的控制,可 以滿足一些特殊的需求。

    posted @ 2010-10-29 13:51 xiaoxinchen 閱讀(1781) | 評論 (0)編輯 收藏
      眾所周知,Linux動態庫的默認搜索路徑是/lib/usr/lib。動態庫被創建后,一般都復制到這兩個目錄中。當程序執行時需要某動態庫,并且該動態庫還未加載到內存中,則系統會自動到這兩個默認搜索路徑中去查找相應的動態庫文件,然后加載該文件到內存中,這樣程序就可以使用該動態庫中的函數,以及該動態庫的其它資源了。在Linux 中,動態庫的搜索路徑除了默認的搜索路徑外,還可以通過以下三種方法來指定。

    方法一:在配置文件/etc/ld.so.conf中指定動態庫搜索路徑。

    可以通過編輯配置文件/etc/ld.so.conf來指定動態庫的搜索路徑,該文件中每行為一個動態庫搜索路徑。每次編輯完該文件后,都必須運行命令ldconfig使修改后的配置生效。我們通過例1來說明該方法。

    例1:

    我們通過以下命令用源程序pos_conf.c(見程序1)來創建動態庫 libpos.so,詳細創建過程請參考文[1]。

    # gcc -c pos_conf.c
          # gcc -shared -fPCI -o libpos.so pos_conf.o
          #

    #include <stdio.h>
          void pos()
          {
              printf("/root/test/conf/lib\n");

    }

           程序1: pos_conf.c

    接著通過以下命令編譯main.c(見程序2)生成目標程序pos。

    # gcc -o pos main.c -L. -lpos
          #

    void pos();
          int main()
          {
              pos();
                   return 0;
          }

    程序2: main.c

    然后把庫文件移動到目錄/root/test/conf/lib中。

    # mkdir -p /root/test/conf/lib
          # mv libpos.so /root/test/conf/lib
          #

    最后編輯配置文件/etc/ld.so.conf,在該文件中追加一行"/root/test/conf/lib"。

    運行程序pos試試。

    # ./pos
            ./pos: error while loading shared libraries: libpos.so: cannot open shared object file: No such file or directory
          #

    出錯了,系統未找到動態庫libpos.so。找找原因,原來在編輯完配置文件/etc/ld.so.conf后,沒有運行命令ldconfig,所以剛才的修改還未生效。我們運行ldconfig后再試試。

    # ldconfig
          # ./pos     /root/test/conf/lib
          #

    程序pos運行成功,并且打印出正確結果。

    方法二:通過環境變量LD_LIBRARY_PATH指定動態庫搜索路徑(!)。

    通過設定環境變量LD_LIBRARY_PATH也可以指定動態庫搜索路徑。當通過該環境變量指定多個動態庫搜索路徑時,路徑之間用冒號":"分隔。

        不過LD_LIBRARY_PATH的設定作用是全局的,過多的使用可能會影響到其他應用程序的運行,所以多用在調試。(LD_LIBRARY_PATH的缺陷和使用準則,可以參考《Why LD_LIBRARY_PATH is bad》)。通常情況下推薦還是使用gcc的-R或-rpath選項來在編譯時就指定庫的查找路徑,并且該庫的路徑信息保存在可執行文件中,運行時它會直接到該路徑查找庫,避免了使用LD_LIBRARY_PATH環境變量查找。

    下面通過例2來說明本方法。

    例2:

    我們通過以下命令用源程序pos_env.c(見程序3)來創建動態庫libpos.so。

    # gcc -c pos_env.c
          # gcc -shared -fPCI -o libpos.so pos_env.o
          #

    #include <stdio.h>
              void pos()
              {
                    printf("/root/test/env/lib\n");
              }
          程序3: pos_env.c

    測試用的可執行文件pos可以使用例1中的得到的目標程序pos,不需要再次編譯。因為pos_conf.c中的函數pos和pos_env.c中的函數pos 函數原型一致,且動態庫名相同,這就好比修改動態庫pos后重新創建該庫一樣。這也是使用動態庫的優點之一。

    然后把動態庫libpos.so移動到目錄/root/test/conf/lib中。

    # mkdir -p /root/test/env/lib
          # mv libpos.so /root/test/env/lib
          #

    我們可以使用export來設置該環境變量,在設置該環境變量后所有的命令中,該環境變量都有效。

    例如:

    # export LD_LIBRARY_PATH=/root/test/env/lib
          #

    但本文為了舉例方便,使用另一種設置環境變量的方法,既在命令前加環境變量設置,該環境變量只對該命令有效,當該命令執行完成后,該環境變量就無效了。如下述命令:

    # LD_LIBRARY_PATH=/root/test/env/lib ./pos  /root/test/env/lib
          #

    程序pos運行成功,并且打印的結果是"/root/test/env/lib",正是程序pos_env.c中的函數pos的運行結果。因此程序pos搜索到的動態庫是/root/test/env/lib/libpos.so。

    方法三:在編譯目標代碼時指定該程序的動態庫搜索路徑。

    還可以在編譯目標代碼時指定程序的動態庫搜索路徑。這是通過gcc 的參數"-Wl,-rpath,"指定(如例3所示)。當指定多個動態庫搜索路徑時,路徑之間用冒號":"分隔。

    例3:

    我們通過以下命令用源程序pos.c(見程序4)來創建動態庫libpos.so。

    # gcc -c pos.c
          # gcc -shared -fPCI -o libpos.so pos.o
          #

    #include <stdio.h>
          void pos()
          {
                    printf("./\n");
          }

          程序4: pos.c

    因為我們需要在編譯目標代碼時指定可執行文件的動態庫搜索路徑,所以需要用gcc命令重新編譯源程序main.c(見程序2)來生成可執行文件pos。

    # gcc -o pos main.c -L. -lpos -Wl,-rpath,./
          #

    再運行程序pos試試。

    # ./pos   ./
          #

    程序pos運行成功,輸出的結果正是pos.c中的函數pos的運行結果。因此程序pos搜索到的動態庫是./libpos.so。

    以上介紹了三種指定動態庫搜索路徑的方法,加上默認的動態庫搜索路徑/lib和/usr/lib,共五種動態庫的搜索路徑,那么它們搜索的先后順序是什么呢?

    在 介紹上述三種方法時,分別創建了動態庫./libpos.so、 /root/test/env/lib/libpos.so和/root/test/conf/lib/libpos.so。我們再用源程序 pos_lib.c(見程序5)來創建動態庫/lib/libpos.so,用源程序pos_usrlib.c(見程序6)來創建動態庫 /usr/lib/libpos.so。

    #include <stdio.h>
          void pos()
          {
                       printf("/lib\n");
          }

          程序5: pos_lib.c

    #include <stdio.h>
          void pos()
          {
                     printf("/usr/lib\n");
          }

          程序6: pos_usrlib.c

    這樣我們得到五個動態庫libpos.so,這些動態庫的名字相同,且都包含相同函數原型的公用函數pos。但存儲的位置不同和公用函數pos 打印的結果不同。每個動態庫中的公用函數pos都輸出該動態庫所存放的位置。這樣我們可以通過執行例3中的可執行文件pos得到的結果不同獲知其搜索到了哪個動態庫,從而獲得第1個動態庫搜索順序,然后刪除該動態庫,再執行程序pos,獲得第2個動態庫搜索路徑,再刪除第2個被搜索到的動態庫,如此往復,將可得到Linux搜索動態庫的先后順序。程序pos執行的輸出結果和搜索到的動態庫的對應關系如表1所示:

    程序pos輸出結果 使用的動態庫 對應的動態庫搜索路徑指定方式
    ./ ./libpos.so 編譯目標代碼時指定的動態庫搜索路徑
    /root/test/env/lib /root/test/env/lib/libpos.so 環境變量LD_LIBRARY_PATH指定的動態庫搜索路徑
    /root/test/conf/lib /root/test/conf/lib/libpos.so 配置文件/etc/ld.so.conf中指定的動態庫搜索路徑
    /lib /lib/libpos.so 默認的動態庫搜索路徑/lib
    /usr/lib /usr/lib/libpos.so 默認的動態庫搜索路徑/usr/lib
    表1: 程序pos輸出結果和動態庫的對應關系

    創建各個動態庫,并放置在相應的目錄中。測試環境就準備好了。執行程序pos,并在該命令行中設置環境變量LD_LIBRARY_PATH。

    # LD_LIBRARY_PATH=/root/test/env/lib ./pos  ./
          #

    根據程序pos的輸出結果可知,最先搜索的是編譯目標代碼時指定的動態庫搜索路徑。然后我們把動態庫./libpos.so刪除了,再運行上述命令試試。

    # rm libpos.so
            rm: remove regular file `libpos.so'? y
          # LD_LIBRARY_PATH=/root/test/env/lib ./pos /root/test/env/lib
          #

    根據程序pos的輸出結果可知,第2個動態庫搜索的路徑是環境變量LD_LIBRARY_PATH指定的。我們再把/root/test/env/lib/libpos.so刪除,運行上述命令。

    # rm /root/test/env/lib/libpos.so
            rm: remove regular file `/root/test/env/lib/libpos.so'? y
          # LD_LIBRARY_PATH=/root/test/env/lib ./pos  /root/test/conf/lib
          #

    第3個動態庫的搜索路徑是配置文件/etc/ld.so.conf指定的路徑。刪除動態庫/root/test/conf/lib/libpos.so后再運行上述命令。

    # rm /root/test/conf/lib/libpos.so
            rm: remove regular file `/root/test/conf/lib/libpos.so'? y
          # LD_LIBRARY_PATH=/root/test/env/lib ./pos  /lib
          #

    第4個動態庫的搜索路徑是默認搜索路徑/lib。我們再刪除動態庫/lib/libpos.so,運行上述命令。

    # rm /lib/libpos.so
            rm: remove regular file `/lib/libpos.so'? y
          # LD_LIBRARY_PATH=/root/test/env/lib ./pos  /usr/lib
          #

    最后的動態庫搜索路徑是默認搜索路徑/usr/lib。

    綜合以上結果可知,動態庫的搜索路徑搜索的先后順序是:

    1.編譯目標代碼時指定的動態庫搜索路徑;

    2.環境變量LD_LIBRARY_PATH指定的動態庫搜索路徑;

    3.配置文件/etc/ld.so.conf中指定的動態庫搜索路徑;

    4.默認的動態庫搜索路徑/lib;

    5.默認的動態庫搜索路徑/usr/lib。

    在上述1、2、3指定動態庫搜索路徑時,都可指定多個動態庫搜索路徑,其搜索的先后順序是按指定路徑的先后順序搜索的。對此本文不再舉例說明,有興趣的讀者可以參照本文的方法驗證。

    posted @ 2010-09-14 11:03 xiaoxinchen 閱讀(226) | 評論 (0)編輯 收藏

    序論
    我曾發表過文件輸入輸出的文章,現在覺得有必要再寫一點。文件 I/O 在C++中比烤蛋糕簡單多了。 在這篇文章里,我會詳細解釋ASCII和二進制文件的輸入輸出的每個細節,值得注意的是,所有這些都是用C++完成的。

    一、ASCII 輸出
    為了使用下面的方法, 你必須包含頭文件<fstream.h>(譯者注:在標準C++中,已經使用<fstream>取 代<fstream.h>,所有的C++標準頭文件都是無后綴的。)。這是 <iostream.h>的一個擴展集, 提供有緩沖的文件輸入輸出操作. 事實上, <iostream.h> 已經被<fstream.h>包含了, 所以你不必包含所有這兩個文件, 如果你想顯式包含他們,那隨便你。我們從文件操作類的設計開始, 我會講解如何進行ASCII I/O操作。 如果你猜是"fstream," 恭喜你答對了! 但這篇文章介紹的方法,我們分別使用"ifstream"?和 "ofstream" 來作輸入輸出。
    如果你用過標準控制臺流"cin"?和 "cout," 那現在的事情對你來說很簡單。 我們現在開始講輸出部分,首先聲明一個類對象。

    ofstream fout; 

    這就可以了,不過你要打開一個文件的話, 必須像這樣調用ofstream::open()。

    fout.open("output.txt"); 

    你也可以把文件名作為構造參數來打開一個文件.

    ofstream fout("output.txt");

      這是我們使用的方法, 因為這樣創建和打開一個文件看起來更簡單. 順便說一句, 如果你要打開的文件不存在,它會為你創建一個, 所以不用擔心文件創建的問題. 現在就輸出到文件,看起來和"cout"的操作很像。 對不了解控制臺輸出"cout"的人, 這里有個例子。

    int num = 150;char name[] = "John Doe";fout << "Here is a number: " << num << "\n";fout << "Now here is a string: " << name << "\n";

      現在保存文件,你必須關閉文件,或者回寫文件緩沖. 文件關閉之后就不能再操作了, 所以只有在你不再操作這個文件的時候才調用它,它會自動保存文件。 回寫緩沖區會在保持文件打開的情況下保存文件, 所以只要有必要就使用它。 回寫看起來像另一次輸出, 然后調用方法關閉。像這樣:

    fout << flush; fout.close(); 

    現在你用文本編輯器打開文件,內容看起來是這樣:

    Here is a number: 150 Now here is a string: John Doe 

      很簡單吧! 現在繼續文件輸入, 需要一點技巧, 所以先確認你已經明白了流操作,對 "<<" 和">>" 比較熟悉了, 因為你接下來還要用到他們。繼續…

    二、ASCII 輸入
    輸入和"cin" 流很像. 和剛剛討論的輸出流很像, 但你要考慮幾件事情。在我們開始復雜的內容之前, 先看一個文本:

    12 GameDev 15.45 L This is really awesome! 

    為了打開這個文件,你必須創建一個in-stream對象,?像這樣。

    ifstream fin("input.txt"); 

      現在讀入前四行. 你還記得怎么用"<<" 操作符往流里插入變量和符號吧?好,?在 "<<" (插入)?操作符之后,是">>" (提取) 操作符. 使用方法是一樣的. 看這個代碼片段.

    int number; float real; char letter, word[8]; fin >> number; fin >> word; fin >> real; fin >> letter; 

    也可以把這四行讀取文件的代碼寫為更簡單的一行。

    fin >> number >> word >> real >> letter; 

      它是如何運作的呢? 文件的每個空白之后, ">>" 操作符會停止讀取內容, 直到遇到另一個>>操作符. 因為我們讀取的每一行都被換行符分割開(是空白字符), ">>" 操作符只把這一行的內容讀入變量。這就是這個代碼也能正常工作的原因。但是,可別忘了文件的最后一行。

    This is really awesome! 

      如果你想把整行讀入一個char數組, 我們沒辦法用">>"?操作符,因為每個單詞之間的空格(空白字符)會中止文件的讀取。為了驗證:

    char sentence[101]; fin >> sentence; 

      我們想包含整個句子, "This is really awesome!" 但是因為空白, 現在它只包含了"This". 很明顯, 肯定有讀取整行的方法, 它就是getline()。這就是我們要做的。

    fin.getline(sentence, 100); 

      這是函數參數. 第一個參數顯然是用來接受的char數組. 第二個參數是在遇到換行符之前,數組允許接受的最大元素數量. 現在我們得到了想要的結果:“This is really awesome!”。
    你應該已經知道如何讀取和寫入ASCII文件了。但我們還不能罷休,因為二進制文件還在等著我們。

    三、二進制 輸入輸出
    二進制文件會復雜一點, 但還是很簡單的。 首先你要注意我們不再使用插入和提取操作符(譯者注:<< 和 >> 操作符). 你可以這么做,但它不會用二進制方式讀寫。你必須使用read() 和write() 方法讀取和寫入二進制文件. 創建一個二進制文件, 看下一行。

    ofstream fout("file.dat", ios::binary); 

      這會以二進制方式打開文件, 而不是默認的ASCII模式。首先從寫入文件開始。函數write() 有兩個參數。 第一個是指向對象的char類型的指針, 第二個是對象的大小(譯者注:字節數)。 為了說明,看例子。

    int number = 30; fout.write((char *)(&number), sizeof(number)); 

      第一個參數寫做"(char *)(&number)". 這是把一個整型變量轉為char *指針。如果你不理解,可以立刻翻閱C++的書籍,如果有必要的話。第二個參數寫作"sizeof(number)". sizeof() 返回對象大小的字節數. 就是這樣!
    二進制文件最好的地方是可以在一行把一個結構寫入文件。 如果說,你的結構有12個不同的成員。 用ASCII?文件,你不得不每次一條的寫入所有成員。 但二進制文件替你做好了。 看這個。

    struct OBJECT { int number; char letter; } obj; obj.number = 15;obj.letter = ‘M’; fout.write((char *)(&obj), sizeof(obj)); 

      這樣就寫入了整個結構! 接下來是輸入. 輸入也很簡單,因為read()?函數的參數和 write()是完全一樣的, 使用方法也相同。

    ifstream fin("file.dat", ios::binary); fin.read((char *)(&obj), sizeof(obj)); 

      我不多解釋用法, 因為它和write()是完全相同的。二進制文件比ASCII文件簡單, 但有個缺點是無法用文本編輯器編輯。 接著, 我解釋一下ifstream 和ofstream 對象的其他一些方法作為結束.

    四、更多方法
    我已經解釋了ASCII文件和二進制文件, 這里是一些沒有提及的底層方法。

    檢查文件

    你已經學會了open() 和close() 方法, 不過這里還有其它你可能用到的方法。
    方法good() 返回一個布爾值,表示文件打開是否正確。
    類似的,bad() 返回一個布爾值表示文件打開是否錯誤。 如果出錯,就不要繼續進一步的操作了。
    最后一個檢查的方法是fail(), 和bad()有點相似, 但沒那么嚴重。

    讀文件
    方法get() 每次返回一個字符。
    方法ignore(int,char) 跳過一定數量的某個字符, 但你必須傳給它兩個參數。第一個是需要跳過的字符數。 第二個是一個字符, 當遇到的時候就會停止。 例子,

    fin.ignore(100, ‘\n’); 

    會跳過100個字符,或者不足100的時候,跳過所有之前的字符,包括 ‘\n’。
    方法peek() 返回文件中的下一個字符, 但并不實際讀取它。所以如果你用peek() 查看下一個字符, 用get() 在peek()之后讀取,會得到同一個字符, 然后移動文件計數器。
    方法putback(char) 輸入字符, 一次一個, 到流中。我沒有見到過它的使用,但這個函數確實存在。

    寫文件
    只有一個你可能會關注的方法.?那就是 put(char), 它每次向輸出流中寫入一個字符。

    打開文件
    當我們用這樣的語法打開二進制文件:

    ofstream fout("file.dat", ios::binary); 

      "ios::binary"是你提供的打開選項的額外標志. 默認的, 文件以ASCII方式打開, 不存在則創建, 存在就覆蓋. 這里有些額外的標志用來改變選項。

    ios::app 添加到文件尾
    ios::ate 把文件標志放在末尾而非起始。
    ios::trunc 默認. 截斷并覆寫文件。
    ios::nocreate 文件不存在也不創建。
    ios::noreplace    文件存在則失敗。

    文件狀態
    我用過的唯一一個狀態函數是eof(), 它返回是否標志已經到了文件末尾。 我主要用在循環中。 例如, 這個代碼斷統計小寫‘e’ 在文件中出現的次數。

    ifstream fin("file.txt"); char ch; int counter; while (!fin.eof()) {      ch = fin.get();       if (ch == ‘e’) counter++; }fin.close(); 

      我從未用過這里沒有提到的其他方法。 還有很多方法,但是他們很少被使用。參考C++書籍或者文件流的幫助文檔來了解其他的方法。

    posted @ 2010-08-08 17:37 xiaoxinchen 閱讀(190) | 評論 (0)編輯 收藏

    什么是Socket
    Socket接口是TCP/IP網絡的API,Socket接口定義了許多函數或例程,程序員可以用它們來開發TCP/IP網絡上的應用程序。要學Internet上的TCP/IP網絡編程,必須理解Socket接口。
    Socket接口設計者最先是將接口放在Unix操作系統里面的。如果了解Unix系統的輸入和輸出的話,就很容易了解Socket了。網絡的 Socket數據傳輸是一種特殊的I/O,Socket也是一種文件描述符。Socket也具有一個類似于打開文件的函數調用Socket(),該函數返 回一個整型的Socket描述符,隨后的連接建立、數據傳輸等操作都是通過該Socket實現的。常用的Socket類型有兩種:流式Socket (SOCK_STREAM)和數據報式Socket(SOCK_DGRAM)。流式是一種面向連接的Socket,針對于面向連接的TCP服務應用;數據 報式Socket是一種無連接的Socket,對應于無連接的UDP服務應用。

    Socket建立
    為了建立Socket,程序可以調用Socket函數,該函數返回一個類似于文件描述符的句柄。socket函數原型為:
    int socket(int domain, int type, int protocol);
    domain指明所使用的協議族,通常為PF_INET,表示互聯網協議族(TCP/IP協議族);type參數指定socket的類型: SOCK_STREAM 或SOCK_DGRAM,Socket接口還定義了原始Socket(SOCK_RAW),允許程序使用低層協議;protocol通常賦值"0"。 Socket()調用返回一個整型socket描述符,你可以在后面的調用使用它。
    Socket描述符是一個指向內部數據結構的指針,它指向描述符表入口。調用Socket函數時,socket執行體將建立一個Socket,實際上"建立一個Socket"意味著為一個Socket數據結構分配存儲空間。Socket執行體為你管理描述符表。
    兩個網絡程序之間的一個網絡連接包括五種信息:通信協議、本地協議地址、本地主機端口、遠端主機地址和遠端協議端口。Socket數據結構中包含這五種信息。

    Socket配置
    通過socket調用返回一個socket描述符后,在使用socket進行網絡傳輸以前,必須配置該socket。面向連接的socket客戶端通過 調用Connect函數在socket數據結構中保存本地和遠端信息。無連接socket的客戶端和服務端以及面向連接socket的服務端通過調用 bind函數來配置本地信息。
    Bind函數將socket與本機上的一個端口相關聯,隨后你就可以在該端口監聽服務請求。Bind函數原型為:
    int bind(int sockfd,struct sockaddr *my_addr, int addrlen);
    Sockfd是調用socket函數返回的socket描述符,my_addr是一個指向包含有本機IP地址及端口號等信息的sockaddr類型的指針;addrlen常被設置為sizeof(struct sockaddr)。
    struct sockaddr結構類型是用來保存socket信息的:
    struct sockaddr {
    unsigned short sa_family; /* 地址族, AF_xxx */
    char sa_data[14]; /* 14 字節的協議地址 */
    };
    sa_family一般為AF_INET,代表Internet(TCP/IP)地址族;sa_data則包含該socket的IP地址和端口號。
    另外還有一種結構類型:
    struct sockaddr_in {
    short int sin_family; /* 地址族 */
    unsigned short int sin_port; /* 端口號 */
    struct in_addr sin_addr; /* IP地址 */
    unsigned char sin_zero[8]; /* 填充0 以保持與struct sockaddr同樣大小 */
    };
    這個結構更方便使用。sin_zero用來將sockaddr_in結構填充到與struct sockaddr同樣的長度,可以用bzero()或memset()函數將其置為零。指向sockaddr_in 的指針和指向sockaddr的指針可以相互轉換,這意味著如果一個函數所需參數類型是sockaddr時,你可以在函數調用的時候將一個指向 sockaddr_in的指針轉換為指向sockaddr的指針;或者相反。
    使用bind函數時,可以用下面的賦值實現自動獲得本機IP地址和隨機獲取一個沒有被占用的端口號:
    my_addr.sin_port = 0; /* 系統隨機選擇一個未被使用的端口號 */
    my_addr.sin_addr.s_addr = INADDR_ANY; /* 填入本機IP地址 */
    通過將my_addr.sin_port置為0,函數會自動為你選擇一個未占用的端口來使用。同樣,通過將my_addr.sin_addr.s_addr置為INADDR_ANY,系統會自動填入本機IP地址。
    注意在使用bind函數是需要將sin_port和sin_addr轉換成為網絡字節優先順序;而sin_addr則不需要轉換。
    計算機數據存儲有兩種字節優先順序:高位字節優先和低位字節優先。Internet上數據以高位字節優先順序在網絡上傳輸,所以對于在內部是以低位字節優先方式存儲數據的機器,在Internet上傳輸數據時就需要進行轉換,否則就會出現數據不一致。
    下面是幾個字節順序轉換函數:
    ·htonl():把32位值從主機字節序轉換成網絡字節序
    ·htons():把16位值從主機字節序轉換成網絡字節序
    ·ntohl():把32位值從網絡字節序轉換成主機字節序
    ·ntohs():把16位值從網絡字節序轉換成主機字節序
    Bind()函數在成功被調用時返回0;出現錯誤時返回"-1"并將errno置為相應的錯誤號。需要注意的是,在調用bind函數時一般不要將端口號置為小于1024的值,因為1到1024是保留端口號,你可以選擇大于1024中的任何一個沒有被占用的端口號。

    連接建立
    面向連接的客戶程序使用Connect函數來配置socket并與遠端服務器建立一個TCP連接,其函數原型為:
    int connect(int sockfd, struct sockaddr *serv_addr,int addrlen);
    Sockfd 是socket函數返回的socket描述符;serv_addr是包含遠端主機IP地址和端口號的指針;addrlen是遠端地質結構的長度。 Connect函數在出現錯誤時返回-1,并且設置errno為相應的錯誤碼。進行客戶端程序設計無須調用bind(),因為這種情況下只需知道目的機器 的IP地址,而客戶通過哪個端口與服務器建立連接并不需要關心,socket執行體為你的程序自動選擇一個未被占用的端口,并通知你的程序數據什么時候到 打斷口。
    Connect函數啟動和遠端主機的直接連接。只有面向連接的客戶程序使用socket時才需要將此socket與遠端主機相連。無連接協議從不建立直接連接。面向連接的服務器也從不啟動一個連接,它只是被動的在協議端口監聽客戶的請求。
    Listen函數使socket處于被動的監聽模式,并為該socket建立一個輸入數據隊列,將到達的服務請求保存在此隊列中,直到程序處理它們。
    int listen(int sockfd, int backlog);
    Sockfd 是Socket系統調用返回的socket 描述符;backlog指定在請求隊列中允許的最大請求數,進入的連接請求將在隊列中等待accept()它們(參考下文)。Backlog對隊列中等待 服務的請求的數目進行了限制,大多數系統缺省值為20。如果一個服務請求到來時,輸入隊列已滿,該socket將拒絕連接請求,客戶將收到一個出錯信息。
    當出現錯誤時listen函數返回-1,并置相應的errno錯誤碼。
    accept()函數讓服務器接收客戶的連接請求。在建立好輸入隊列后,服務器就調用accept函數,然后睡眠并等待客戶的連接請求。
    int accept(int sockfd, void *addr, int *addrlen);
    sockfd是被監聽的socket描述符,addr通常是一個指向sockaddr_in變量的指針,該變量用來存放提出連接請求服務的主機的信息(某 臺主機從某個端口發出該請求);addrten通常為一個指向值為sizeof(struct sockaddr_in)的整型指針變量。出現錯誤時accept函數返回-1并置相應的errno值。
    首先,當accept函數監視的 socket收到連接請求時,socket執行體將建立一個新的socket,執行體將這個新socket和請求連接進程的地址聯系起來,收到服務請求的 初始socket仍可以繼續在以前的 socket上監聽,同時可以在新的socket描述符上進行數據傳輸操作。

    數據傳輸
    Send()和recv()這兩個函數用于面向連接的socket上進行數據傳輸。
    Send()函數原型為:
    int send(int sockfd, const void *msg, int len, int flags);
    Sockfd是你想用來傳輸數據的socket描述符;msg是一個指向要發送數據的指針;Len是以字節為單位的數據的長度;flags一般情況下置為0(關于該參數的用法可參照man手冊)。
    Send()函數返回實際上發送出的字節數,可能會少于你希望發送的數據。在程序中應該將send()的返回值與欲發送的字節數進行比較。當send()返回值與len不匹配時,應該對這種情況進行處理。
    char *msg = "Hello!";
    int len, bytes_sent;
    ……
    len = strlen(msg);
    bytes_sent = send(sockfd, msg,len,0);
    ……
    recv()函數原型為:
    int recv(int sockfd,void *buf,int len,unsigned int flags);
    Sockfd是接受數據的socket描述符;buf 是存放接收數據的緩沖區;len是緩沖的長度。Flags也被置為0。Recv()返回實際上接收的字節數,當出現錯誤時,返回-1并置相應的errno值。
    Sendto()和recvfrom()用于在無連接的數據報socket方式下進行數據傳輸。由于本地socket并沒有與遠端機器建立連接,所以在發送數據時應指明目的地址。
    sendto()函數原型為:
    int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to, int tolen);
    該函數比send()函數多了兩個參數,to表示目地機的IP地址和端口號信息,而tolen常常被賦值為sizeof (struct sockaddr)。Sendto 函數也返回實際發送的數據字節長度或在出現發送錯誤時返回-1。
    Recvfrom()函數原型為:
    int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen);
    from是一個struct sockaddr類型的變量,該變量保存源機的IP地址及端口號。fromlen常置為sizeof (struct sockaddr)。當recvfrom()返回時,fromlen包含實際存入from中的數據字節數。Recvfrom()函數返回接收到的字節數或 當出現錯誤時返回-1,并置相應的errno。
    如果你對數據報socket調用了connect()函數時,你也可以利用send()和recv()進行數據傳輸,但該socket仍然是數據報socket,并且利用傳輸層的UDP服務。但在發送或接收數據報時,內核會自動為之加上目地和源地址信息。

    結束傳輸
    當所有的數據操作結束以后,你可以調用close()函數來釋放該socket,從而停止在該socket上的任何數據操作:
    close(sockfd);
    你也可以調用shutdown()函數來關閉該socket。該函數允許你只停止在某個方向上的數據傳輸,而一個方向上的數據傳輸繼續進行。如你可以關閉某socket的寫操作而允許繼續在該socket上接受數據,直至讀入所有數據。
    int shutdown(int sockfd,int how);
    Sockfd是需要關閉的socket的描述符。參數 how允許為shutdown操作選擇以下幾種方式:
    ·0-------不允許繼續接收數據
    ·1-------不允許繼續發送數據
    ·2-------不允許繼續發送和接收數據,
    ·均為允許則調用close ()
    shutdown在操作成功時返回0,在出現錯誤時返回-1并置相應errno。

    Socket編程實例
    代碼實例中的服務器通過socket連接向客戶端發送字符串"Hello, you are connected!"。只要在服務器上運行該服務器軟件,在客戶端運行客戶軟件,客戶端就會收到該字符串。
    該服務器軟件代碼如下:
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <string.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <sys/wait.h>
    #define SERVPORT 3333 /*服務器監聽端口號 */
    #define BACKLOG 10 /* 最大同時連接請求數 */
    main()
    {
    int sockfd,client_fd; /*sock_fd:監聽socket;client_fd:數據傳輸socket */
    struct sockaddr_in my_addr; /* 本機地址信息 */
    struct sockaddr_in remote_addr; /* 客戶端地址信息 */
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
    perror("socket創建出錯!"); exit(1);
    }
    my_addr.sin_family=AF_INET;
    my_addr.sin_port=htons(SERVPORT);
    my_addr.sin_addr.s_addr = INADDR_ANY;
    bzero(&(my_addr.sin_zero),8);
    if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) {
    perror("bind出錯!");
    exit(1);
    }
    if (listen(sockfd, BACKLOG) == -1) {
    perror("listen出錯!");
    exit(1);
    }
    while(1) {
    sin_size = sizeof(struct sockaddr_in);
    if ((client_fd = accept(sockfd, (struct sockaddr *)&remote_addr, &sin_size)) == -1) {
    perror("accept出錯");
    continue;
    }
    printf("received a connection from %s\n", inet_ntoa(remote_addr.sin_addr));
    if (!fork()) { /* 子進程代碼段 */
    if (send(client_fd, "Hello, you are connected!\n", 26, 0) == -1)
    perror("send出錯!");
    close(client_fd);
    exit(0);
    }
    close(client_fd);
    }
    }
    }
    服務器的工作流程是這樣的:首先調用socket函數創建一個Socket,然后調用bind函數將其與本機地址以及一個本地端口號綁定,然后調用 listen在相應的socket上監聽,當accpet接收到一個連接服務請求時,將生成一個新的socket。服務器顯示該客戶機的IP地址,并通過 新的socket向客戶端發送字符串"Hello,you are connected!"。最后關閉該socket。
    代碼實例中的fork()函數生成一個子進程來處理數據傳輸部分,fork()語句對于子進程返回的值為0。所以包含fork函數的if語句是子進程代碼部分,它與if語句后面的父進程代碼部分是并發執行的。

    客戶端程序代碼如下:
    #include<stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <string.h>
    #include <netdb.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #define SERVPORT 3333
    #define MAXDATASIZE 100 /*每次最大數據傳輸量 */
    main(int argc, char *argv[]){
    int sockfd, recvbytes;
    char buf[MAXDATASIZE];
    struct hostent *host;
    struct sockaddr_in serv_addr;
    if (argc < 2) {
    fprintf(stderr,"Please enter the server's hostname!\n");
    exit(1);
    }
    if((host=gethostbyname(argv[1]))==NULL) {
    herror("gethostbyname出錯!");
    exit(1);
    }
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
    perror("socket創建出錯!");
    exit(1);
    }
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_port=htons(SERVPORT);
    serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
    bzero(&(serv_addr.sin_zero),8);
    if (connect(sockfd, (struct sockaddr *)&serv_addr, \
    sizeof(struct sockaddr)) == -1) {
    perror("connect出錯!");
    exit(1);
    }
    if ((recvbytes=recv(sockfd, buf, MAXDATASIZE, 0)) ==-1) {
    perror("recv出錯!");
    exit(1);
    }
    buf[recvbytes] = '\0';
    printf("Received: %s",buf);
    close(sockfd);
    }
    客戶端程序首先通過服務器域名獲得服務器的IP地址,然后創建一個socket,調用connect函數與服務器建立連接,連接成功之后接收從服務器發送過來的數據,最后關閉socket。
    函數gethostbyname()是完成域名轉換的。由于IP地址難以記憶和讀寫,所以為了方便,人們常常用域名來表示主機,這就需要進行域名和IP地址的轉換。函數原型為:
    struct hostent *gethostbyname(const char *name);
    函數返回為hosten的結構類型,它的定義如下:
    struct hostent {
    char *h_name; /* 主機的官方域名 */
    char **h_aliases; /* 一個以NULL結尾的主機別名數組 */
    int h_addrtype; /* 返回的地址類型,在Internet環境下為AF-INET */
    int h_length; /* 地址的字節長度 */
    char **h_addr_list; /* 一個以0結尾的數組,包含該主機的所有地址*/
    };
    #define h_addr h_addr_list[0] /*在h-addr-list中的第一個地址*/
    當 gethostname()調用成功時,返回指向struct hosten的指針,當調用失敗時返回-1。當調用gethostbyname時,你不能使用perror()函數來輸出錯誤信息,而應該使用herror()函數來輸出。

      無連接的客戶/服務器程序的在原理上和連接的客戶/服務器是一樣的,兩者的區別在于無連接的客戶/服務器中的客戶一般不需要建立連接,而且在發送接收數據時,需要指定遠端機的地址。

    阻塞和非阻塞
    阻塞函數在完成其指定的任務以前不允許程序調用另一個函數。例如,程序執行一個讀數據的函數調用時,在此函數完成讀操作以前將不會執行下一程序語句。當 服務器運行到accept語句時,而沒有客戶連接服務請求到來,服務器就會停止在accept語句上等待連接服務請求的到來。這種情況稱為阻塞 (blocking)。而非阻塞操作則可以立即完成。比如,如果你希望服務器僅僅注意檢查是否有客戶在等待連接,有就接受連接,否則就繼續做其他事情,則 可以通過將Socket設置為非阻塞方式來實現。非阻塞socket在沒有客戶在等待時就使accept調用立即返回。
    #include <unistd.h>
    #include <fcntl.h>
    ……
    sockfd = socket(AF_INET,SOCK_STREAM,0);
    fcntl(sockfd,F_SETFL,O_NONBLOCK);
    ……
    通過設置socket為非阻塞方式,可以實現"輪詢"若干Socket。當企圖從一個沒有數據等待處理的非阻塞Socket讀入數據時,函數將立即返 回,返回值為-1,并置errno值為EWOULDBLOCK。但是這種"輪詢"會使CPU處于忙等待方式,從而降低性能,浪費系統資源。而調用 select()會有效地解決這個問題,它允許你把進程本身掛起來,而同時使系統內核監聽所要求的一組文件描述符的任何活動,只要確認在任何被監控的文件 描述符上出現活動,select()調用將返回指示該文件描述符已準備好的信息,從而實現了為進程選出隨機的變化,而不必由進程本身對輸入進行測試而浪費 CPU開銷。Select函數原型為:
    int select(int numfds,fd_set *readfds,fd_set *writefds,
    fd_set *exceptfds,struct timeval *timeout);
    其中readfds、writefds、exceptfds分別是被select()監視的讀、寫和異常處理的文件描述符集合。如果你希望確定是否可以 從標準輸入和某個socket描述符讀取數據,你只需要將標準輸入的文件描述符0和相應的sockdtfd加入到readfds集合中;numfds的值 是需要檢查的號碼最高的文件描述符加1,這個例子中numfds的值應為sockfd+1;當select返回時,readfds將被修改,指示某個文件 描述符已經準備被讀取,你可以通過FD_ISSSET()來測試。為了實現fd_set中對應的文件描述符的設置、復位和測試,它提供了一組宏:
    FD_ZERO(fd_set *set)----清除一個文件描述符集;
    FD_SET(int fd,fd_set *set)----將一個文件描述符加入文件描述符集中;
    FD_CLR(int fd,fd_set *set)----將一個文件描述符從文件描述符集中清除;
    FD_ISSET(int fd,fd_set *set)----試判斷是否文件描述符被置位。
    Timeout參數是一個指向struct timeval類型的指針,它可以使select()在等待timeout長時間后沒有文件描述符準備好即返回。struct timeval數據結構為:
    struct timeval {
    int tv_sec; /* seconds */
    int tv_usec; /* microseconds */
    };

    POP3客戶端實例
    下面的代碼實例基于POP3的客戶協議,與郵件服務器連接并取回指定用戶帳號的郵件。與郵件服務器交互的命令存儲在字符串數組POPMessage中,程序通過一個do-while循環依次發送這些命令。
    #include<stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <string.h>
    #include <netdb.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #define POP3SERVPORT 110
    #define MAXDATASIZE 4096

    main(int argc, char *argv[]){
    int sockfd;
    struct hostent *host;
    struct sockaddr_in serv_addr;
    char *POPMessage[]={
    "USER userid\r\n",
    "PASS password\r\n",
    "STAT\r\n",
    "LIST\r\n",
    "RETR 1\r\n",
    "DELE 1\r\n",
    "QUIT\r\n",
    NULL
    };
    int iLength;
    int iMsg=0;
    int iEnd=0;
    char buf[MAXDATASIZE];

    if((host=gethostbyname("your.server"))==NULL) {
    perror("gethostbyname error");
    exit(1);
    }
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
    perror("socket error");
    exit(1);
    }
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_port=htons(POP3SERVPORT);
    serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
    bzero(&(serv_addr.sin_zero),8);
    if (connect(sockfd, (struct sockaddr *)&serv_addr,sizeof(struct sockaddr))==-1){
    perror("connect error");
    exit(1);
    }

    do {
    send(sockfd,POPMessage[iMsg],strlen(POPMessage[iMsg]),0);
    printf("have sent: %s",POPMessage[iMsg]);

    iLength=recv(sockfd,buf+iEnd,sizeof(buf)-iEnd,0);
    iEnd+=iLength;
    buf[iEnd]='\0';
    printf("received: %s,%d\n",buf,iMsg);

    iMsg++;
    } while (POPMessage[iMsg]);

    close(sockfd);
    }

    來自:Li

    posted @ 2010-03-21 21:01 xiaoxinchen 閱讀(226) | 評論 (0)編輯 收藏
    總體思路是先打成jar再把jar打成exe。主要看1.3和2.3里的內容就可以了。


    1.將項目打成jar:


    1.1 要將項目打包成jar文件,方法很多,可以用Eclipse自帶的打包工具Ant打包,也可以用Eclipse的Export生成jar。經過嘗試后,我 不推薦用Ant打包,因為要自己編寫xml腳本語言,還要增加一些外部的jar,所以我打了好幾次都沒打成。


    1.2 在這里介紹兩種方法生成jar,第一種是用Eclpise的Export功能。在要打包的項目上擊右鍵,選擇Export,在窗口中選擇Java里的 JAR file。Next后的窗口中已經自動選好了要打包的項目,用戶可以點擊加號查看項目里被打包的內容。在下面的JAR file里設置你打包生成jar文件的輸出目錄,下一步在出現的窗口中選擇Use existing manifest from workspace,在下面的Main class后面直接點Browse,它會自動列出你項目中有主函數main的類。選擇主類后點Finish即可生成jar文件。在此說明一下,這種打包方 法不能把項目中的外部的jar包打進來,因該是也要編寫一些腳本語言,沒往深研究。所以生成后的jar有些是不能執行的。


    1.3 第二種方法是利用Eclipse的一個第三方插件fatjar生成jar文件,也是本人覺得最簡單最方便的一種生成方式。先從網上下載些 插件,解壓后是一個plugins的文件夾,里面只有一個文件夾,我的是“net.sf.fjep.fatjar_0.0.24”將它copy到 Eclipser plugins文件夾下,此插件就安裝成功了,重啟Eclipse在項目上右擊就會看到多出一個“Build Fat Jar”在前面有個綠色的“+”號,這時你就可以用此插件打包你的項目了。進去后第一個界面Jar-Name里增入要生成的jar文件名,我的是 “CAMP_fat.jar”。在Main-Class后點Browse像Export一樣它也會列出你項目中的主類,選擇后其它默認即可,Next后會 列出你要打包的所有內容,這個插件的優勢就是可以將你項目中的外部jar也打進來,有三個先項,其中Export ANT是生成build.xml腳本文件,方便用戶以后修改腳本,其它兩個按鈕沒用。在這里什么都不點,直接點Finish就可以生成jar文件。


    2.將jar打成.exe文件:


    2.1 雖然此時的jar文件已經可以執行了。生成.exe的文件我也是用兩種方法實現的,用到的打包工具是j2ewiz和exe4j,它們的不同會在我下面的介 紹中體現出來。


    2.2 首先是j2ewiz,這個軟件是綠色的,不用安裝,解壓后可以直接運行,但這個軟件生成的 .exe文件不是跨平臺的。運行此程序首先就是輸入要打包的jar文件,我們瀏覽JAR選擇我們之前用fatjar生成的“CAMP_fat.jar”項 目文件(詳見1.3),下面那個選項是提示用戶最低要求的JRE版本,一般選1.3。下一步,因為我們的寢室管理系統是圖形界面,所以在這里選 “Windows窗口程序”下一步它也是自動生成要執行的主類,你只要選擇就可以。下面的選框可以選擇你啟動程序顯示的圖片。下一步后這個窗可按個人喜好 選擇。下一步,如果你的程序還有什么依賴的外部jar文件,可以從這里加上,但因為之前的fatjar以經將我們項目所用的那三個連數據庫的外部類打進 CAMP_fat.jar包里了,所以這里不用再添加。如果你之前是用Export打的jar 包,那么這里就需要再把那個三個數據庫的包加進來了(詳見1.2)。下一步是添入要生成的.exe文件名,再選一個程序圖標就可以了,下一步后生 成.exe文件,點完成。雙擊生成的.exe文件就能看到運行效果了,這種exe文件還沒有脫離JDK環境,還不能跨平臺使用,只能用于小組成員測試使 用。


    2.3 下面進入最關鍵的,如何打包跨平臺的.exe文件。用到的軟件是exe4j,我用的是V4.0版的,此軟件需要破解。安裝后運行左窗窗口標有十步,其實打 包過程也非常簡單。第一步完全略過,直接點Next第二步我們選擇“JAR in EXE mode” 就是選擇我們已經有制作好的jar文件。第3步上面是項目名稱,可隨便填寫,下面一個寫出你想要將打包后的exe文件輸出的目錄我的是“桌 面\project\”。第4步,由于我的演示程序是圖形的,所以選第一個,如果你的程序是控制臺的,則選擇第二個,Executable name寫你將要生成的.exe文件的名字,Icon File可以選擇生成文件的圖標。第5步,先別管上面的,先在下面單擊綠色的“+”號,在彈出的窗口中點Archive,然后找到起初已經做好的 CAMP_fat.jar(詳見1.3)文件,"OK"后返回,在下面的Class Path里就出現jar文件路徑后,再在上面Main Class欄內點擊找到main所在的類。第6步,你系統的JRE版本,一般是填個1.3,下面填1.6在這里單擊advanced options,選擇search sequence。選這個就是因為我們要把JDK環境也打包進來,好讓程序能跨平臺使用。首先要從你系統的JDK下的JRE目錄copy到你.exe文件 的輸出目錄下“桌面\project\JRE”,然后回到exe4j中在彈出窗口刪除列表中的所有項。我的是三項,一個注冊表的,一個JAVA環境變量 的,一個JDK環境變量的,都不要。然后單擊綠“+”,選擇directory并選擇JRE的根目錄,我的是“桌面\project\JRE”就是 copy后的目錄,選完后exe4j彈出窗口中的Directory里會顯示“.\JRE”。點OK關閉該窗口,返回exe4j的主窗口,你就可以看到剛 加的路徑。再從主窗口左側窗口中單擊advanced options,并選擇preferred VM,在彈出的窗口中選擇client hostspot VM,單擊next按鈕繼續。7、8步是一些個性設置默認即可。第9步編譯完后第10步你點那個“Click Here to Start the Application”按鈕就可以看到程序運行效果了,然后再點”Seave as”保存一個exe4j生成的一個文件,隨便存哪里都行,和我們的.exe程序無關。全部制作過程就完工了。
    posted @ 2010-03-13 18:06 xiaoxinchen 閱讀(3914) | 評論 (0)編輯 收藏
    首先要弄清楚,在Linux系統中,內核為每一個新創建的文件分配一個Inode(索引結點),每個文件都有一個惟一的inode號。文件屬性保存在索引結點里,在訪問文件時,索引結點被復制到內存在,從而實現文件的快速訪問。

    鏈接是一種在共享文件和訪問它的用戶的若干目錄項之間建立聯系的一種方法。Linux中包括兩種鏈接:硬鏈接(Hard Link)和軟鏈接(Soft Link),軟鏈接又稱為符號鏈接(Symbolic link)。

    一、軟鏈接(符號鏈接)

    軟鏈接克服了硬鏈接的不足,沒有任何文件系統的限制,任何用戶可以創建指向目錄的符號鏈接。因而現在更為廣泛使用,它具有更大的靈活性,甚至可以跨越不同機器、不同網絡對文件進行鏈接。

    建立軟鏈接,只要在ln后面加上選項 –s。



    二、硬鏈接

    硬鏈接說白了是一個指針,指向文件索引節點,系統并不為它重新分配inode。可以用:ln命令來建立硬鏈接。語法

    ln [options] existingfile newfile
    ln[options] existingfile-list directory

    用法: 第一種:為”existingfile”創建硬鏈接,文件名為”newfile”。第二種:在”directory”目錄中, 為”existingfile-list”中包含的所有文件創建一個同名的硬鏈接。常用可選[options] –f 無論”newfile”存在與否,都創建鏈接。-n 如果”newfile”已存在,就不創建鏈接。 
    posted @ 2010-03-11 16:07 xiaoxinchen 閱讀(224) | 評論 (0)編輯 收藏
    (1)Jre 是java runtime environment, 是java程序的運行環境。既然是運行,當然要包含jvm,也就是大家熟悉的虛擬機啦, 還有所有java類庫的class文件,都在lib目錄下打包成了jar。大家可以自己驗證。至于在windows上的虛擬機是哪個文件呢? 學過MFC的都知道什么是dll文件吧,那么大家看看jre/bin/client里面是不是有一個jvm.dll呢?那就是虛擬機。

    (2)Jdk 是java development kit,是java的開發工具包,里面包含了各種類庫和工具。當然也包括了另外一個Jre. 那么為什么要包括另外一個Jre呢?而且jdk/jre/bin同時有client和server兩個文件夾下都包含一個jvm.dll。 說明是有兩個虛擬機的。這一點不知道大家是否注意到了呢?
      相信大家都知道jdk的bin下有各種java程序需要用到的命令,與jre的bin目錄最明顯的區別就是jdk下才有javac,這一點很好理解,因為 jre只是一個運行環境而已。與開發無關,正因為如此,具備開發功能的jdk自己的jre下才會同時有client性質的jvm和server性質的jvm, 而僅僅作為運行環境的jre下只需要client性質的jvm.dll就夠了。

    (3)記得在環境變量path中設置jdk/bin路徑麼?這應該是大家學習Java的第一步吧, 老師會告訴大家不設置的話javac和java是用不了的。確實jdk/bin目錄下包含了所有的命令。可是有沒有人想過我們用的java命令并不是 jdk/bin目錄下的而是jre/bin目錄下的呢?不信可以做一個實驗,大家可以把jdk/bin目錄下的java.exe剪切到別的地方再運行 java程序,發現了什么?一切OK!
      那么有人會問了?我明明沒有設置jre/bin目錄到環境變量中啊?
    試想一下如果java為了提供給大多數人使用,他們是不需要jdk做開發的,只需要jre能讓java程序跑起來就可以了,那么每個客戶還需要手 動去設置環境變量多麻煩啊?所以安裝jre的時候安裝程序自動幫你把jre的java.exe添加到了系統變量中,驗證的方法很簡單,大家看到了系統環境 變量的 path最前面有“%SystemRoot%\system32;%SystemRoot%;”這樣的配置,那么再去Windows/system32下 面去看看吧,發現了什么?有一個java.exe。
      如果強行能夠把jdk/bin挪到system32變量前面,當然也可以迫使使用jdk/jre里面的java,不過除非有必要,我不建議大家這么做。使用單獨的jre跑java程序也算是客戶環境下的一種測試。 
    posted @ 2010-03-11 12:27 xiaoxinchen 閱讀(278) | 評論 (0)編輯 收藏

    關于2009年12月全國大學英語四、六級考試成績發布時間的通知:

    2009年12月全國大學英語四、六級考試成績將于 2010年3月3日上午9點發布。

    成績查詢方式

    網上免費查分:

    網址: cet.99sushe.com

    運營商: 99宿舍網

    客服電話: 010-58699163轉867

    收費短信查分(2010年3月3日上午9點開始):

    中國移動、聯通、電信手機用戶:

    發送A 加 15位準考證號到 1066335577

    如A123456789012345到 1066335577查詢成績(1元/條,不含通信費)

    特別注意:

    河北省的中國移動手機用戶:發送 8 加 15位準考證號到 10661660

    如8123456789012345到 10661660 查詢成績(1元/條,不含通信費)

    運營商: 空中網

    客服電話: 010-68083018

    注:2009年12月網考成績發布方式和日期另行通知。

    全國大學英語四、六級考試委員會辦公室

    2010年2月24日

    posted @ 2010-03-03 00:50 xiaoxinchen 閱讀(237) | 評論 (0)編輯 收藏
    在許多平臺中,Browser控件皆被做為一個必需的控件給出,并提供了DOM接口,用于訪問Browser的內容,相對來說SWT中的Browser控件就比較薄弱,沒有提供DOM的可控制接口,那么,如何和控件所加載的頁面進行交互呢?比如需要在集成web應用環境中實現模仿登陸、自動填表等功能
    SWT中對Browser有不同的實現,目前實現的有IE和Mozilla。在Browser的構造函數中根據不同的平臺和不同的style設置類決定使用哪個類的實現。

    org.eclipse.swt.browser.Mozilla org.eclipse.swt.browser.IE 是已經實現的,而其他的 org.eclipse.swt.browser.Safari org.eclipse.swt.browser.Voyager
    來源:www.va1314.com/bc
    則沒有實現。


    public Browser (Composite parent, int style) {

    super (checkParent (parent), checkStyle (style));

    String platform = SWT.getPlatform ();

    Display display = parent.getDisplay ();

    if ("gtk".equals (platform)) display.setData (NO_INPUT_METHOD, null); //$NON-NLS-1$

    String className = null;

    if ((style & SWT.MOZILLA) != 0) {

    className = "org.eclipse.swt.browser.Mozilla"; //$NON-NLS-1$

    } else {

    if ("win32".equals (platform) || "wpf".equals (platform)) { //$NON-NLS-1$ $NON-NLS-2$

    className = "org.eclipse.swt.browser.IE"; //$NON-NLS-1$

    } else if ("motif".equals (platform)) { //$NON-NLS-1$

    className = "org.eclipse.swt.browser.Mozilla"; //$NON-NLS-1$

    } else if ("gtk".equals (platform)) { //$NON-NLS-1$

    className = "org.eclipse.swt.browser.Mozilla"; //$NON-NLS-1$

    } else if ("carbon".equals (platform)) { //$NON-NLS-1$

    className = "org.eclipse.swt.browser.Safari"; //$NON-NLS-1$

    } else if ("photon".equals (platform)) { //$NON-NLS-1$

    className = "org.eclipse.swt.browser.Voyager"; //$NON-NLS-1$

    } else {

    dispose ();

    SWT.error (SWT.ERROR_NO_HANDLES);

    }

    }



    try {

    Class clazz = Class.forName (className);

    webBrowser = (WebBrowser)clazz.newInstance ();

    } catch (ClassNotFoundException e) {

    } catch (IllegalAccessException e) {

    } catch (InstantiationException e) {

    }

    if (webBrowser == null) {

    dispose ();

    SWT.error (SWT.ERROR_NO_HANDLES);

    }



    webBrowser.setBrowser (this);

    webBrowser.create (parent, style);

    }

    public Browser (Composite parent, int style) {

    super (checkParent (parent), checkStyle (style));

    String platform = SWT.getPlatform ();

    Display display = parent.getDisplay ();

    if ("gtk".equals (platform)) display.setData (NO_INPUT_METHOD, null); //$NON-NLS-1$

    String className = null;

    if ((style & SWT.MOZILLA) != 0) {

    className = "org.eclipse.swt.browser.Mozilla"; //$NON-NLS-1$

    } else {

    if ("win32".equals (platform) || "wpf".equals (platform)) { //$NON-NLS-1$ $NON-NLS-2$

    className = "org.eclipse.swt.browser.IE"; //$NON-NLS-1$

    } else if ("motif".equals (platform)) { //$NON-NLS-1$

    className = "org.eclipse.swt.browser.Mozilla"; //$NON-NLS-1$

    } else if ("gtk".equals (platform)) { //$NON-NLS-1$

    className = "org.eclipse.swt.browser.Mozilla"; //$NON-NLS-1$

    } else if ("carbon".equals (platform)) { //$NON-NLS-1$

    className = "org.eclipse.swt.browser.Safari"; //$NON-NLS-1$

    } else if ("photon".equals (platform)) { //$NON-NLS-1$

    className = "org.eclipse.swt.browser.Voyager"; //$NON-NLS-1$

    } else {

    dispose ();

    SWT.error (SWT.ERROR_NO_HANDLES);

    }

    }



    try {

    Class clazz = Class.forName (className);

    webBrowser = (WebBrowser)clazz.newInstance ();

    } catch (ClassNotFoundException e) {

    } catch (IllegalAccessException e) {

    } catch (InstantiationException e) {

    }

    if (webBrowser == null) {

    dispose ();

    SWT.error (SWT.ERROR_NO_HANDLES);

    }



    webBrowser.setBrowser (this);

    webBrowser.create (parent, style);

    }

    其中對IE的實現主要是采用調用IE的Activex控件,間接加載IE,對Mozilla由于代碼過多,本人沒有具體研究,其本身開源,有興趣能夠參看。

    那么回歸主題,如何實現與Browser控件的交互呢? 其實仔細看Browser控件的API,能夠發覺一個execute()方法,這個方法適用于在web文檔加載完畢時能夠運行javascript code的。這樣的話,交互就變得簡單了,因為javascript是提供dom的支持的,既然能夠調用javascript,那么就能夠調用web頁面 中的每個節點了。控制的問題處理了,可是另外的問題來了。 如何從javascript的code里邊前往數據呢? 比如我需要將一個<input type=text id=textid />的值前往到java code中。其實采用的方法是很投機的,因為execute()方法前往的結果是true or false,那么對它做文章是沒有用的,我們看其他的api,能夠發覺:addStatusTextListener()方法。 這個方法能夠監聽web頁面對于statusbar文本改變的值,并反映在java code里面,那么我們只需通過javascript把前往的值寫到window.status,那么就能夠在javacode里取到了。 具體代碼請參考下面,對于Browser的承繼重寫,通過getValue能夠取得指定id的html 控件的值,通過setValue能夠設置值。 view plaincopy to clipboardprint?

    import org.eclipse.swt.browser.Browser;

    import org.eclipse.swt.browser.StatusTextEvent;

    import org.eclipse.swt.browser.StatusTextListener;

    import org.eclipse.swt.widgets.Composite;



    public class CoolBrowser extends Browser implements StatusTextListener {



    private final String DATA = "Browser_Data";



    public CoolBrowser(Composite parent, int style) {

    super(parent, style);

    addStatusTextListener(this);

    }



    @Override

    protected void checkSubclass() {

    }



    /**

    * Get the value of one input control in the web

    * @param id

    * @return

    */

    public String getValue(String id) {

    if (execute("var obj = document.getElementById('" + id + "');"

    + "if( obj != null ) window.status=obj.value;")) {

    return (String) getData(DATA);

    }

    return null;

    }



    /**

    * Set the value of the input control

    * @param id

    * @param value

    */

    public void setValue( String id, Object value ){

    if (execute("var obj = document.getElementById('" + id + "');"

    + "if( obj != null ) obj.value='" + value + "';")) {

    }

    }



    @Override

    public void changed(StatusTextEvent event) {

    setData(DATA, event.text);

    }



    }
    posted @ 2009-12-29 16:28 xiaoxinchen 閱讀(5284) | 評論 (1)編輯 收藏
    下載地址:http://www.iplaysoft.com/windows7-msdn-iso.html
    posted @ 2009-12-20 20:14 xiaoxinchen 閱讀(416) | 評論 (0)編輯 收藏
      Proxy代理模式是一種結構型設計模式,主要解決的問題是:在直接訪問對象時帶來的問題,比如說:要訪問的對象在遠程的機器上。在面向對象系統中,有些對象由于某些原因(比如對象創建開銷很大,或者某些操作需要安全控制,或者需要進程外的訪問),直接訪問會給使用者或者系統結構帶來很多麻煩,我們可以在訪問此對象時加上一個對此對象的訪問層。如下圖:
       



           比如說C和A不在一個服務器上,A要頻繁的調用C,我們可以在A上做一個代理類Proxy,把訪問C的工作交給Proxy,這樣對于A來說,就好像在直接訪問C的對象。在對A的開發中我們可以把注意力完全放在業務的實現上。

           GoF《設計模式》中說道:為其他對象提供一種代理以控制這個對象的訪問。

           Proxy模式的結構:
       



           下面通過一個場景來看看Proxy的實現,我們要使用代理類型ProxyClass的對象調用遠程機器上的一個類型LongDistanceClass的對象。

        首先我們先模擬一個遠程的類型:為了保持對被代理對象使用的透明性,我們使代理類型和被代理類型同時繼承同一個接口IProxy

        接口實現:

        interface IProxy

        {

            string Function1();

            string Function2();

        }

        遠程對象實現:

        /// <summary>

        /// 模擬的遠程對象

        /// </summary>

        public class LongDistanceClass:IProxy

        {

            #region IProxy 成員

            public string Function1()

            {

                //do someting

                return "LongDistanceClass.Function1";

            }

            public string Function2()

            {

                //do someting

                return "LongDistanceClass.Function2";

            }

            #endregion

        }

        接下來就要實現代理類型,使用代理對象訪問模擬的遠程對象,代理類型實現如下:

        public class ProxyClass:IProxy

        {

            #region IProxy 成員

            public string Function1()

            {

                //to access LongDistanceClass.Function1

                LongDistanceClass obj = new LongDistanceClass();

                return obj.Function1();

            }

            public string Function2()

            {

                //to access LongDistanceClass.Function2

                LongDistanceClass obj = new LongDistanceClass();

                return obj.Function2();

            }

            #endregion

        }

     

        最后實現客戶端代碼:

        class Class1

        {

            [STAThread]

            static void Main(string[] args)

            {

                IProxy pro = new ProxyClass();

                Console.WriteLine(pro.Function1());

                Console.WriteLine(pro.Function2());

                Console.Read();

            }

        }

        運行結果如下:

        LongDistanceClass.Function1

    LongDistanceClass.Function2

           Proxy模式的要點:

           1、“增加一層間接層”是軟件系統中對許多負責問題的一種常見解決方法。在面向對象系統中,直接使用某些對象會帶來很多問題,作為間接層的proxy對象便是解決這一問題的常用手段。

           在我們日常的工作中也常常用到代理模式,比如對于三層結構或者N- tiers結構中DAL數據訪問層,它把對數據庫的訪問進行封裝。BLL業務層的開發者只是調用DAL中的方法來獲得數據。

           在比如前一段時間看了看AOP和Remoting方面的資料,對于跨越應用程序域的訪問,要為客戶應用程序提供一個TransparentProxy(透明代理),客戶程序實際上是通過訪問這個代理來訪問實際的類型對象。

    2、具體proxy設計模式的實現方法、實現粒度都相差很大,有些可能對單個對象作細粒度的控制,有些可能對組件模塊提供抽象代理層,在架構層次對對象作proxy。

    3、proxy并不一定要求保持接口的一致性,只要能夠實現間接控制,有時候損及一些透明性是可以接受的。例如上面的那個例子,代理類型ProxyClass和被代理類型LongDistanceClass可以不用繼承自同一個接口,正像GoF《設計模式》中說的:為其他對象提供一種代理以控制這個對象的訪問。代理類型從某種角度上講也可以起到控制被代理類型的訪問的作用。
    posted @ 2009-08-04 14:44 xiaoxinchen 閱讀(149) | 評論 (0)編輯 收藏

    1.策略模式-Strategy

    策略模式
    是對算法的包裝,是把使用算法的責任和算法本身分割開來,委派給不同的對象管理。
    策略模式通常把一個系列的算法包裝到一系列的策略類里面,作為一個抽象策略類的子類。

    一句話來形容:準備一組算法,并將每一個算法封裝起來,使得他們可以互換


    策略模式的結構
    策略模式涉及到三個角色:
    • 環境角色:持有一個Strategy類(策略類)的引用
    • 抽象策略角色:策略類,通常由一個接口或者抽象類實現
    • 具體策略角色:包裝了相關的算法和行為




    《三國演義》中的故事
    諸葛亮的精囊妙計?三條妙計
    走喬國老的后門,求孫國太放人,請孫夫人退兵
    趙云?按計行事
    環境角色:趙云?由他來決定選擇策略
    抽象策略角色:(接口)精囊妙計?按計行事(抽象方法)
    具體策略角色:三條妙計(單獨使用的)


    例子:一個策略模式的加減乘除
    抽象策略角色: (精囊妙計)? Operation抽象類(oper抽象方法)
    具體策略角色: (三條妙計)? 計算乘積,計算除法,計算加法,計算減法
    環境角色:  (趙云)?  有一個策略類( Operation )的引用


    策略模式的優缺點:
    優點:
    1.提供了管理相關的算法族的辦法。
    2.提供了可以替換繼承關系的辦法。
    3.避免使用多重條件轉移語句

    缺點:
    1.客戶端必須知道所有的策略類,并自行決定使用哪一個策略類。
    2.造成很多的策略類。
    posted @ 2009-08-04 12:21 xiaoxinchen 閱讀(236) | 評論 (0)編輯 收藏
    原則內容:OCP原則就是"開-閉原則",一個軟件應該對擴展開放,對修改關閉。
    解釋 :在設計一個模塊的時候,應當使得這個模塊可以在不被修改的前提下面被擴展。換言之,應該可以在不必修改源代碼的情況下改變這個
    模塊的行為。這個原則有2點要求:
    ×:擴展開放
    ×:關閉修改
    滿足OCP原則系統的優點:
    ×:通過擴展已有的軟件系統,提供新的行為,可以使得軟件系統滿足新需求
    ×:已有的軟件模塊,特別是重要的抽象層模塊不能做變更,這使得變化中的軟件系統有一定的穩定性和延續性。
    如何實現OCP原則:
    1、抽象是關鍵。
       對需要設計的系統進行抽象,在此前提下面,讓抽象的實現機制千變萬化。這個抽象層必須預見到所有的可能的擴展,任何實現的改變都不會改變該抽象結構。這樣使得系統的抽象層無需修改,從而滿足OCP原則的第二條-關閉修改。
    2、對可變性的封裝原則
       OCP從另一個角度來說,就是EVP(principle of Encapsulation Variation)原則。即找到系統的可變因素,將之封裝起來。這個原則意味著2點:
    ×:一種可變性不應當散落在代碼的很多角落里,而應當被封裝到一個對象里面。繼承應當被看    做是封裝變化的方法,而不應當被認為是從一般的對象生成特殊的對象方法。
    ×:一種可變性不應當與另一種可變性混合在一起。所有的類圖的繼承結構一般不會超過兩層,不然就意味著將兩種不同的可變性混合在一起。
    與其他設計原則的關系
    LSP原則:這個原則是說任何基類出現的地方,子類一定可以出現。
    這個原則是對OCP原則的補充,基類和子類的關系就是抽象化的具體體現,所以LSP原則是對實現抽象化的具體步驟的規范。一般來說,違背了LSP原則,一定違反了OCP原則,反之不一定成立。
    CARP原則:這個原則講的是要盡可能的多用合成/聚合,而不是繼承關系達到復用的目的。
    CARP原則和LSP原則相輔相成。二者都是對實現OCP原則的具體步驟的規范。前者要求設計師首先考慮合成/聚合關系;后者要求在使用繼承關系的時候, 必須確定這個關系是符合一定條件的。CARP原則是OCP原則的必要條件,違反了這個原則,就無法使系統實現OCP原則這個目標。
    DIP原則:這個原則是說要依賴于抽象,不要依賴于實現。
    DIP原則和OCP原則是目標和手段的關系。OCP是目標,DIP是手段。要想實現OCP原則,必須堅持DIP原則。違反了DIP原則,則不可能達到OCP原則要求。

    LoD原則:這個原則講的是一個軟件實體應該盡可能少的和其他實體發生作用。
    當一個system面臨功能擴展的時候,其中會有一些模塊,他們需要修改的壓力比其他的模塊要大一些,如果這些模塊是相對孤立的,那么他們就不會將修改的 壓力傳遞給其他模塊。根據LoD原則設計的系統,在功能需要擴展的時候,會相對容易的做到對修改的關閉。LoD原則是一條通向OCP原則的道路。
    ISP原則:這個原則是說,應當為客戶端提供盡可能小的單獨接口,而不要提供大的總接口。ISP原則和LoD原則講的都是一個軟件實體與另一個軟件實體的通訊限制。廣義的LoD原則要求盡可能限制通訊的寬度和深度,ISP原則所限制的是通信寬度。
    一個重構方法的討論
    “將條件轉移語句改寫成為多態性”是一條廣為流傳的代碼重構做法。

    這一做法本身并不能保證“開-閉”原則,應當以“開-閉”原則判斷是否需要改寫成多態。條件轉移并不是錯誤,如果需要,完全可以選擇使用條件轉移。

    如果一個條件轉移語句確實封裝了某種商務邏輯的可變性,那么將此種可變性封裝起來就符合“開-閉”原則設計思想了。如果一個條件轉移語句沒有涉及重 要的商務邏輯,或者不會隨著時間的變化而變化,也不意味著任何的可擴展性,那么它就沒有涉及任何有意義的可變性。這時候將這個條件轉移語句改寫成多態性就 是一種沒有意義的浪費。
    抽象類應當擁有盡可能多的共同代碼

     在一個繼承的等級結構中,共同的代碼應當盡量向等級結構的上方移動。把重復的代碼從子類里面移動到超類里面,可以提高代碼的復用率。在代碼發生改變時,設計師之需要修改一個地方。

    抽象類應當擁有盡可能少的數據

    與代碼的移動方向相反,數據的移動方向是從抽象類到具體類,向等級結構的下方移動。一個對象的數據不論是否使用都會占用資源,所以應當放到等級結構的低端。

     

    什么時候才應當使用繼承復用

    1.子類是超類的一個特殊種類,而不是超類的一個角色,Is-A才符合繼承關系。

    2.永遠不會出現需要將子類換成另一個類的子類的情況。

    3.子類具有擴展超類的責任,而不是具有置換掉(Override)和注銷掉(Nullify)超類的責任。

    4.只有在分類學角度上有意義時,才可以使用繼承,不要從工具類繼承。

    posted @ 2009-08-03 23:15 xiaoxinchen 閱讀(975) | 評論 (0)編輯 收藏
    什么是內聚?什么是耦合?
    內聚是從功能角度來度量模塊內的聯系,一個好的內聚模塊應當恰好做一件事。它描述
    的是模塊內的功能聯系; 耦合是軟件結構中各模塊之間相互連接的一種度量,耦合強弱取決
    于模塊間接口的復雜程度、進入或訪問一個模塊的點以及通過接口的數據。
    耦合性也稱塊間聯系。指軟件系統結構中各模塊間相互聯系緊密程度的一種度量。模塊之間聯系越緊密,其耦合性就越強,模塊的獨立性則越差。模塊間耦合高低取決于模塊間接口的復雜性、調用的方式及傳遞的信息。

    2. 內聚分為哪幾類?耦合分為哪幾類?
          內聚有如下的種類,它們之間的內聚度由弱到強排列如下:
    (1) 偶然內聚。模塊中的代碼無法定義其不同功能的調用。但它使該模塊能執行不同
    的功能,這種模塊稱為巧合強度模塊。
    (2) 邏輯內聚。這種模塊把幾種相關的功能組合在一起, 每次被調用時,由傳送給模
    塊參數來確定該模塊應完成哪一種功能
    (3) 時間內聚:把需要同時執行的動作組合在一起形成的模塊為時間內聚模塊。
    (4) 過程內聚:構件或者操作的組合方式是,允許在調用前面的構件或操作之后,馬上調用后面的構件或操作,即使兩者之間沒有數據進行傳遞。
    (5) 通信內聚:指模塊內所有處理元素都在同一個數據結構上操作(有時稱之為信息內聚),或者指各處理使用相同的輸入數據或者產生相同的輸出數據。
    (6) 順序內聚:指一個模塊中各個處理元素都密切相關于同一功能且必須順序執行,前一功能元素輸出就是下一功能元素的輸入。
    (7) 功能內聚:這是最強的內聚,指模塊內所有元素共同完成一個功能,缺一不可。
    耦合可以分為以下幾種,它們之間的耦合度由高到低排列如下:
    (1) 內容耦合:如果發生下列情形,兩個模塊之間就發生了內容耦合
    一個模塊直接訪問另一個模塊的內部數據
    一個模塊不通過正常入口轉到另一模塊內部;
    兩個模塊有一部分程序代碼重疊(只可能出現在匯編語言中);
    一個模塊有多個入口。
    (2) 公共耦合:若一組模塊都訪問同一個公共數據環境,則它們之間的耦合就稱為公共耦合。公共的數據環境可以是全局數據結構、共享的通信區、內存的公共覆蓋區等。
    (3) 外部耦合:一組模塊都訪問同一全局簡單變量而不是同一全局數據結構,而且不是通過參數表傳遞該全局變量的信息,則稱之為外部耦合。
    (4) 控制耦合:如果一個模塊通過傳送開關、標志、名字等控制信息,明顯地控制選擇另一模塊的功能,就是控制耦合
    (5) 標記耦合:一組模塊通過參數表傳遞記錄信息,就是標記耦合。這個記錄是某一數據結構的子結構,而不是簡單變量。其實傳遞的是這個數據結構的地址;也就是地址傳遞。
    (6) 數據耦合:指兩個模塊之間有調用關系,傳遞的是簡單的數據值,一個模塊訪問另一個模塊時,彼此之間是通過簡單數據參數 (不是控制參數、公共數據結構或外部變量) 來交換輸入、輸出信息的,相當于高級語言的值傳遞。
    (7) 非直接耦合:兩個模塊之間沒有直接關系,它們之間的聯系完全是通過主模塊的控制和調用來實現的。
    耦合強度,依賴于以下幾個因素:
    (1)一個模塊對另一個模塊的調用;
    (2)一個模塊向另一個模塊傳遞的數據量;
    (3)一個模塊施加到另一個模塊的控制的多少;
    (4)模塊之間接口的復雜程度。


    參考資料:

    http://baike.baidu.com/view/156245.html

    http://www.cnblogs.com/dqshll/articles/1116799.html

    http://blog.zol.com.cn/858/article_857495.html

    posted @ 2009-08-03 23:06 xiaoxinchen 閱讀(1602) | 評論 (0)編輯 收藏

    用 jQuery 的都知道,jQuery 的 get 和 post 方法有三個參數:地址,數據 和回調函數,但我們知道地址也可以跟隨數據的(形如:get_data.php?v1=1&v2=2),而且第二個參數可以省略,即第二個參數可 以直接寫回調函數,那么數據寫在地址后面和寫在 data 參數里有什么區別呢?

    剛剛做了幾個實驗,看看下面的代碼就清楚了:
    以下內容需要回復才能看到

    jquery_data.php

    echo "post: ";
    print_r($_POST);
    echo "get: ";
    print_r($_GET);
    ?>

    jquery_test.html

    實驗1:

    $(function() {
    // post 方法,兩處都有數據
    $.post('jquery_data.php?v1=1', {v2: 2}, function(data) {
    $('

    ').append(data).appendTo('body');
    });
    });

    返回結果:
    post: Array
    (
    [v2] => 2
    )
    get: Array
    (
    [v1] => 1
    )

    實驗2:

    $(function()
    {
    // post 方法,數據在地址后面, 第二個參數為回調函數
    $.post('jquery_data.php?v1=1', function(data)
    {
    $('<pre/>').append(data).appendTo('body');
    });
    });

    返回結果,數據在 get 中:
    post: Array
    (
    )
    get: Array
    (
    [v1] => 1
    )

    實驗3:

    $(function()
    {
    // get 方法,用 data 參數傳值
    $.get('jquery_data.php', {v2: 2}, function(data)
    {
    $('<pre/>').append(data).appendTo('body');
    });
    });

    返回結果,數據在 get 中:
    post: Array
    (
    )
    get: Array
    (
    [v2] => 2
    )

    實驗4:

    $(function()
    {
    // get 方法,兩處都有數據
    $.get('jquery_data.php?v1=1', {v2: 2}, function(data)
    {
    $('<pre/>').append(data).appendTo('body');
    });
    });

    返回結果,兩處數據被合并了,都在 get 中:
    post: Array
    (
    )
    get: Array
    (
    [v1] => 1
    [v2] => 2
    )

    實驗5:

    $(function()
    {
    // get 方法,兩處都有數據,且變量名相同
    $.get('jquery_data.php?v2=1', {v2: 2}, function(data)
    {
    $('<pre/>').append(data).appendTo('body');
    });
    });

    返回結果,數據在 get 中,且 data 參數中的數據覆蓋了地址后面的數據:
    post: Array
    (
    )
    get: Array
    (
    [v2] => 2
    )

    通過這幾個簡單的小例子不難看出,地址后面的數據永遠是以 get 形式傳遞的,無論使用的是 get 方法還是 post 方法;而 data 參數中的數據是根據方法決定傳遞方式的。

    因此,為了避免混淆,建議大家盡量不要把數據寫在地址后面,而是統一放在 data 參數中。

    當然,如果你想在用 post 方法時,同時利用 get 傳值,那么就可以把要以 get 方式傳遞的數據寫在地址后面,把要以 post 方式傳遞的數據寫在 data 參數中。

    總之方法是死的,人是活的,怎么用還要看實際情況。子曾經曰過:實踐是檢驗真理的唯一標準。沒事做做實驗,掌握知識更牢固。
    posted @ 2009-07-29 19:22 xiaoxinchen 閱讀(914) | 評論 (1)編輯 收藏

    (zz from http://blog.csdn.net/heiyeshuwu)

    Web Service就是為了異構系統的通信而產生的,它基本的思想就是使用基于XML的HTTP的遠程調用提供一種標準的機制,而省去建立一種新協議的需求。 目前進行Web Service通信有兩種協議標準,一種是XML-RPC,另外一種是SOAP。XML-RPC比較簡單,出現時間比較早,SOAP比較復雜,主要是一些 需要穩定、健壯、安全并且復雜交互的時候使用。

    Web Service介紹

    Web Service就是為了異構系統的通信而產生的,它基本的思想就是使用基于XML的HTTP的遠程調用提供一種標準的機制,而省去建立一種新協議的需求。 目前進行Web Service通信有兩種協議標準,一種是XML-RPC,另外一種是SOAP。XML-RPC比較簡單,出現時間比較早,SOAP比較復雜,主要是一些 需要穩定、健壯、安全并且復雜交互的時候使用。

    PHP中集成了XML-RPC和SOAP兩種協議的訪問,都是集中在xmlrpc擴 展當中。另外,在PHP的PEAR中,不管是PHP 4還是PHP 5,都已經默認集成了XML-RPC擴展,而且該擴展跟xmlrpc擴展無關,能夠獨立實現XML-RPC的協議交互,如果沒有xmlrpc擴展,建議使 用PEAR::XML-RPC擴展。

    我們這里主要是以XML-RPC來簡單描述Web Service的交互過程,部分內容來自PHP手冊,更詳細內容,建議參考手冊。

    安裝xmlrpc擴展

    如果你的系統中沒有安裝xmlrpc的php擴展,那么請正確安裝。

    在 Windows平臺下,首先把PHP安裝目錄下的擴展php_xmlrpc.dll放到C:\Windows或者C:\Winnt目錄下, (PHP4的擴展在C:\php\extensions目錄中,PHP5的擴展在C:\php\ext目錄中),同時在C:\Windows \php.ini或者C:\Winnt\php.ini中把extension=php_xmlrpc.dll前面的分號";"去掉,然后重啟Web服務 器后查看phpinfo()有沒有XML-RPC項目就能夠確定是否已經正確安裝xmlrpc擴展。

    在Unix/Linux平臺下,如果沒有安裝xmlrpc擴展,請在重新編譯PHP,在configure的時候請加入 --with-xmlrpc 選項,然后查看phpinfo()看是否正常安裝xmlrpc。

    (注意:以下操作都是建立在xmlrpc擴張正常安裝前提下,請務必正確安裝。)

    XML-RPC工作原理

    XML-RPC大致就是整個過程就是使用XML來進行通信。首先構造一個RPC 服務器端用來出來從RPC客戶端傳遞過來的使用XML封裝的請求,并且把處理結果通過XML的形式返回給RPC客戶端,客戶端就去分析XML獲取自己需要的數據。

    XML-RPC的服務器端必須有現成的函數提供給客戶端調用,并且客戶端提交的請求中的函數和方法必須和服務器端的一致,否則將無法獲取所需要的結果。

    下面我進行簡單的代碼來描述整個過程。

    XML-RPC實踐

    服務器端使用xmlrpc_server_create函數產生一個服務器端,然后把需要需要暴露的RPC調用接口進行注冊,接受RPC客戶端POST過來的XML數據,然后進行處理,處理結果通過XML的形式顯示給客戶端。

    代碼如下: rpc_server.php

    <?php
    /**
    * 函數:提供給RPC客戶端調用的函數
    * 參數:
    * $method 客戶端需要調用的函數
    * $params 客戶端需要調用的函數的參數數組
    * 返回:返回指定調用結果
    */
    function rpc_server_func($method, $params) {
    $parameter = $params[0];
    if ($parameter == "get")
    {
    $return = 'This data by get method';
    }
    else
    {
    $return = 'Not specify method or params';
    }
    return $return;
    }

    //產生一個XML-RPC的服務器端
    $xmlrpc_server = xmlrpc_server_create();

    //注冊一個服務器端調用的方法rpc_server,實際指向的是rpc_server_func函數
    xmlrpc_server_register_method($xmlrpc_server, "rpc_server", "rpc_server_func");

    //接受客戶端POST過來的XML數據
    $request = $HTTP_RAW_POST_DATA;

    //執行調用客戶端的XML請求后獲取執行結果
    $xmlrpc_response = xmlrpc_server_call_method($xmlrpc_server, $request, null);

    //把函數處理后的結果XML進行輸出
    header('Content-Type: text/xml');
    echo $xmlrpc_response;

    //銷毀XML-RPC服務器端資源
    xmlrpc_server_destroy($xmlrpc_server);
    ?>

    服務器端構造好了,那么再構造我們的RPC客戶端。客戶端大致通過Socket訪問XML-RPC服務器端的80端口,然后把需要調用的RPC接口封裝到XML里,通過POST請求提交給RPC服務器端,最后獲取服務器端返回結果。

    代碼如下:rpc_client.php

    <?php
    /**
    * 函數:提供給客戶端進行連接XML-RPC服務器端的函數
    * 參數:
    * $host 需要連接的主機
    * $port 連接主機的端口
    * $rpc_server XML-RPC服務器端文件
    * $request 封裝的XML請求信息
    * 返回:連接成功成功返回由服務器端返回的XML信息,失敗返回false
    */
    function rpc_client_call($host, $port, $rpc_server, $request) {

    //打開指定的服務器端
    $fp = fsockopen($host, $port);

    //構造需要進行通信的XML-RPC服務器端的查詢POST請求信息
    $query = "POST $rpc_server HTTP/1.0\nUser_Agent: XML-RPC Client\nHost: ".$host."\nContent-Type: text/xml\nContent-Length: ".strlen($request)."\n\n".$request."\n";

    //把構造好的HTTP協議發送給服務器,失敗返回false
    if (!fputs($fp, $query, strlen($query)))
    {
    $errstr = "Write error";
    return false;
    }

    //獲取從服務器端返回的所有信息,包括HTTP頭和XML信息
    $contents = '';
    while (!feof($fp))
    {
    $contents .= fgets($fp);
    }

    //關閉連接資源后返回獲取的內容
    fclose($fp);
    return $contents;
    }

    //構造連接RPC服務器端的信息
    $host = 'localhost';
    $port = 80;
    $rpc_server = '/~heiyeluren/rpc_server.php';

    //把需要發送的XML請求進行編碼成XML,需要調用的方法是rpc_server,參數是get
    $request = xmlrpc_encode_request('rpc_server', 'get');

    //調用rpc_client_call函數把所有請求發送給XML-RPC服務器端后獲取信息
    $response = rpc_client_call($host, $port, $rpc_server, $request);

    //分析從服務器端返回的XML,去掉HTTP頭信息,并且把XML轉為PHP能識別的字符串
    $split = '<?xml version="1.0" encoding="iso-8859-1"?>';
    $xml = explode($split, $response);
    $xml = $split . array_pop($xml);
    $response = xmlrpc_decode($xml);

    //輸出從RPC服務器端獲取的信息
    print_r($response);

    ?>

    大致我們上面的例子就是提交一個叫做rpc_server的方法過去,參數是get,然后獲取服務器端的返回,服務器端返回的XML數據是:

    <?xml version="1.0" encoding="iso-8859-1"?>
    <methodResponse>
    <params>
    <param>
    <value>
    <string>This data by get method</string>
    </value>
    </param>
    </params>
    </methodResponse>

    那么我們再通過xmlrpc_decode函數把這個XML編碼為PHP的字符串,我們就能夠隨意處理了,整個Web Service交互完成。

    結束語

    不 管是XML-RPC也好,SOAP也罷,只要能夠讓我們穩定、安全的進行遠程過程的調用,完成我們的項目,那么就算整個Web Service就是成功的。另外,如果可以的話,也可以嘗試使用PEAR中的XML-RPC來實現上面類似的操作,說不定會更簡單,更適合你使用。

    參考

    http://phpxmlrpc.sourceforge.net/

    posted @ 2009-07-02 09:42 xiaoxinchen 閱讀(231) | 評論 (0)編輯 收藏
    JPA

    JPA常用標記說明

    Table

    Table用來定義entity主表的name,catalog,schema等屬性。
    元數據屬性說明:

    • name: 表名
    • catalog: 對應關系數據庫中的catalog
    • schema:對應關系數據庫中的schema
    • UniqueConstraints:定義一個UniqueConstraint數組,指定需要建唯一約束的列
         
    @Entity
    @Table(name="CUST")
    public class Customer { ... }

    SecondaryTable

    一個entity class可以映射到多表,SecondaryTable用來定義單個從表的名字,主鍵名字等屬性。
    元數據屬性說明:

    • name: 表名
    • catalog: 對應關系數據庫中的catalog
    • schema:對應關系數據庫中的schema
    • pkJoin: 定義一個PrimaryKeyJoinColumn數組,指定從表的主鍵列
    • UniqueConstraints:定義一個UniqueConstraint數組,指定需要建唯一約束的列

    下面的代碼說明Customer類映射到兩個表,主表名是CUSTOMER,從表名是CUST_DETAIL,從表的主鍵列和主表的主鍵列類型相同,列名為CUST_ID。

                     
    @Entity
    @Table(name="CUSTOMER")
    @SecondaryTable(name="CUST_DETAIL",pkJoin=@PrimaryKeyJoinColumn(name="CUST_ID"))
    public class Customer { ... }

    SecondaryTables

    當一個entity class映射到一個主表和多個從表時,用SecondaryTables來定義各個從表的屬性。
    元數據屬性說明:

    • value: 定義一個SecondaryTable數組,指定每個從表的屬性。
                     
    @Table(name = "CUSTOMER")
    @SecondaryTables( value = {
    @SecondaryTable(name = "CUST_NAME", pkJoin = { @PrimaryKeyJoinColumn(name = "STMO_ID", referencedColumnName = "id") }),
    @SecondaryTable(name = "CUST_ADDRESS", pkJoin = { @PrimaryKeyJoinColumn(name = "STMO_ID", referencedColumnName = "id") }) })
    public class Customer {}

    UniqueConstraint

    UniqueConstraint定義在Table或SecondaryTable元數據里,用來指定建表時需要建唯一約束的列。
    元數據屬性說明:

    • columnNames:定義一個字符串數組,指定要建唯一約束的列名。
                     
    @Entity
    @Table(name="EMPLOYEE", uniqueConstraints={@UniqueConstraint(columnNames={"EMP_ID", "EMP_NAME"})})
    public class Employee { ... }

    Column

    Column元數據定義了映射到數據庫的列的所有屬性:列名,是否唯一,是否允許為空,是否允許更新等。
    元數據屬性說明:

    • name:列名。
    • unique: 是否唯一
    • nullable: 是否允許為空
    • insertable: 是否允許插入
    • updatable: 是否允許更新
    • columnDefinition: 定義建表時創建此列的DDL
    • secondaryTable: 從表名。如果此列不建在主表上(默認建在主表),該屬性定義該列所在從表的名字。
             
    public class Person {
    @Column(name = "PERSONNAME", unique = true, nullable = false, updatable = true)
    private String name;

    @Column(name = "PHOTO", columnDefinition = "BLOB NOT NULL", secondaryTable="PER_PHOTO")
    private byte[] picture;

    ...

    JoinColumn

    如果在entity class的field上定義了關系(one2one或one2many等),我們通過JoinColumn來定義關系的屬性。JoinColumn的大部分屬性和Column類似。
    元數據屬性說明:

    • name:列名。
    • referencedColumnName:該列指向列的列名(建表時該列作為外鍵列指向關系另一端的指定列)
    • unique: 是否唯一
    • nullable: 是否允許為空
    • insertable: 是否允許插入
    • updatable: 是否允許更新
    • columnDefinition: 定義建表時創建此列的DDL
    • secondaryTable: 從表名。如果此列不建在主表上(默認建在主表),該屬性定義該列所在從表的名字。

    下面的代碼說明Custom和Order是一對一關系。在Order對應的映射表建一個名為CUST_ID的列,該列作為外鍵指向Custom對應表中名為ID的列。

                     
    public class Custom {
    @OneToOne
    @JoinColumn(name="CUST_ID", referencedColumnName="ID", unique=true, nullable=true, updatable=true)
    public Order getOrder() {
    return order;
    }

    JoinColumns

    如果在entity class的field上定義了關系(one2one或one2many等),并且關系存在多個JoinColumn,用JoinColumns定義多個JoinColumn的屬性。
    元數據屬性說明:

    • value: 定義JoinColumn數組,指定每個JoinColumn的屬性。

    下面的代碼說明Custom和Order是一對一關系。在Order對應的映射表建兩列,一列名為CUST_ID,該列作為外鍵指向Custom對應表中名為ID的列,另一列名為CUST_NAME,該列作為外鍵指向Custom對應表中名為NAME的列。

                     
    public class Custom {
    @OneToOne
    @JoinColumns({
    @JoinColumn(name="CUST_ID", referencedColumnName="ID"),
    @JoinColumn(name="CUST_NAME", referencedColumnName="NAME")
    })

    public Order getOrder() {
    return order;
    }

    Id

    聲 明當前field為映射表中的主鍵列。id值的獲取方式有五種:TABLE, SEQUENCE, IDENTITY, AUTO, NONE。Oracle和DB2支持SEQUENCE,SQL Server和Sybase支持IDENTITY,mysql支持AUTO。所有的數據庫都可以指定為AUTO,我們會根據不同數據庫做轉換。 NONE需要用戶自己指定Id的值。元數據屬性說明:

    • generate():主鍵值的獲取類型
    • generator():TableGenerator的名字(當generate=GeneratorType.TABLE才需要指定該屬性)

    下面的代碼聲明Task的主鍵列id是自動增長的。(Oracle和DB2從默認的SEQUENCE取值,SQL Server和Sybase該列建成IDENTITY,mysql該列建成auto increment。)

                    
    @Entity
    @Table(name = "OTASK")
    public class Task {
    @Id(generate = GeneratorType.AUTO)
    public Integer getId() {
    return id;
    }
    }

    IdClass

    當entity class使用復合主鍵時,需要定義一個類作為id class。id class必須符合以下要求:類必須聲明為public,并提供一個聲明為public的空構造函數。必須實現Serializable接,覆寫 equals()和hashCode()方法。entity class的所有id field在id class都要定義,且類型一樣。
    元數據屬性說明:

    • value: id class的類名
                     
    public class EmployeePK implements java.io.Serializable{
    String empName;
    Integer empAge;
    public EmployeePK(){}

    public boolean equals(Object obj){ ......}
    public int hashCode(){......}
    }

    @IdClass(value=com.acme.EmployeePK.class)
    @Entity(access=FIELD)
    public class Employee {
    @Id String empName;
    @Id Integer empAge;
    }

    MapKey

    在一對多,多對多關系中,我們可以用Map來保存集合對象。默認用主鍵值做key,如果使用復合主鍵,則用id class的實例做key,如果指定了name屬性,就用指定的field的值做key。
    元數據屬性說明:

    • name: 用來做key的field名字

    下面的代碼說明Person和Book之間是一對多關系。Person的books字段是Map類型,用Book的isbn字段的值作為Map的key。

                     
    @Table(name = "PERSON")
    public class Person {
    @OneToMany(targetEntity = Book.class, cascade = CascadeType.ALL, mappedBy = "person")
    @MapKey(name = "isbn")
    private Map books = new HashMap();
    }

    OrderBy

    在一對多,多對多關系中,有時我們希望從數據庫加載出來的集合對象是按一定方式排序的,這可以通過OrderBy來實現,默認是按對象的主鍵升序排列。
    元數據屬性說明:

    • value: 字符串類型,指定排序方式。格式為"fieldName1 [ASC|DESC],fieldName2 [ASC|DESC],......",排序類型可以不指定,默認是ASC。

    下面的代碼說明Person和Book之間是一對多關系。集合books按照Book的isbn升序,name降序排列。

                    
    @Table(name = "MAPKEY_PERSON")
    public class Person {
    @OneToMany(targetEntity = Book.class, cascade = CascadeType.ALL, mappedBy = "person")
    @OrderBy(name = "isbn ASC, name DESC")
    private List books = new ArrayList();
    }

    PrimaryKeyJoinColumn

    在三種情況下會用到PrimaryKeyJoinColumn。

    • 繼承。
    • entity class映射到一個或多個從表。從表根據主表的主鍵列(列名為referencedColumnName值的列),建立一個類型一樣的主鍵列,列名由name屬性定義。
    • one2one關系,關系維護端的主鍵作為外鍵指向關系被維護端的主鍵,不再新建一個外鍵列。

    元數據屬性說明:

    • name:列名。
    • referencedColumnName:該列引用列的列名
    • columnDefinition: 定義建表時創建此列的DDL

    下面的代碼說明Customer映射到兩個表,主表CUSTOMER,從表CUST_DETAIL,從表需要建立主鍵列CUST_ID,該列和主表的主鍵列id除了列名不同,其他定義一樣。

                 
    @Entity
    @Table(name="CUSTOMER")
    @SecondaryTable(name="CUST_DETAIL",pkJoin=@PrimaryKeyJoinColumn(name="CUST_ID",referencedColumnName="id"))
    public class Customer {
    @Id(generate = GeneratorType.AUTO)
    public Integer getId() {
    return id;
    }
    }

    下面的代碼說明Employee和EmployeeInfo是一對一關系,Employee的主鍵列id作為外鍵指向EmployeeInfo的主鍵列INFO_ID。

                 
    @Table(name = "Employee")
    public class Employee {
    @OneToOne
    @PrimaryKeyJoinColumn(name = "id", referencedColumnName="INFO_ID")
    EmployeeInfo info;
    }

    PrimaryKeyJoinColumns

    如果entity class使用了復合主鍵,指定單個PrimaryKeyJoinColumn不能滿足要求時,可以用PrimaryKeyJoinColumns來定義多個PrimaryKeyJoinColumn。
    元數據屬性說明:

    • value: 一個PrimaryKeyJoinColumn數組,包含所有PrimaryKeyJoinColumn。

    下面的代碼說明了Employee和EmployeeInfo是一對一關系。他們都使用復合主鍵,建表時需要在Employee表建立一個外鍵,從Employee的主鍵列id,name指向EmployeeInfo的主鍵列INFO_ID和INFO_NAME.

                 
    @Entity
    @IdClass(EmpPK.class)
    @Table(name = "EMPLOYEE")
    public class Employee {
    private int id;
    private String name;

    private String address;

    @OneToOne(cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumns({
    @PrimaryKeyJoinColumn(name="id", referencedColumnName="INFO_ID"),
    @PrimaryKeyJoinColumn(name="name" , referencedColumnName="INFO_NAME")})
    EmployeeInfo info;
    }

    @Entity
    @IdClass(EmpPK.class)
    @Table(name = "EMPLOYEE_INFO")
    public class EmployeeInfo {

    @Id
    @Column(name = "INFO_ID")
    private int id;

    @Id
    @Column(name = "INFO_NAME")
    private String name;
    }

    Transient

    Transient用來注釋entity的屬性,指定的這些屬性不會被持久化,也不會為這些屬性建表。

         
    @Transient
    private String name;

    Version

    Version指定實體類在樂觀事務中的version屬性。在實體類重新由EntityManager管理并且加入到樂觀事務中時,保證完整性。每一個類只能有一個屬性被指定為version,version屬性應該映射到實體類的主表上。

    下面的代碼說明versionNum屬性作為這個類的version,映射到數據庫中主表的列名是OPTLOCK。

                     
    @Version
    @Column("OPTLOCK")
    protected int getVersionNum() { return versionNum; }

    Lob

    Lob指定一個屬性作為數據庫支持的大對象類型在數據庫中存儲。使用LobType這個枚舉來定義Lob是二進制類型還是字符類型。
    LobType枚舉類型說明:

    • BLOB 二進制大對象,Byte[]或者Serializable的類型可以指定為BLOB。
    • CLOB 字符型大對象,char[]、Character[]或String類型可以指定為CLOB。

    元數據屬性說明:

    • fetch: 定義這個字段是lazy loaded還是eagerly fetched。數據類型是FetchType枚舉,默認為LAZY,即lazy loaded.
    • type: 定義這個字段在數據庫中的JDBC數據類型。數據類型是LobType枚舉,默認為BLOB。

    下面的代碼定義了一個BLOB類型的屬性和一個CLOB類型的屬性。

                     
    @Lob
    @Column(name="PHOTO" columnDefinition="BLOB NOT NULL")
    protected JPEGImage picture;

    @Lob(fetch=EAGER, type=CLOB)
    @Column(name="REPORT")
    protected String report;

    JoinTable

    JoinTable在many-to-many關系的所有者一邊定義。如果沒有定義JoinTable,使用JoinTable的默認值。
    元數據屬性說明:

    • table:這個join table的Table定義。
    • joinColumns:定義指向所有者主表的外鍵列,數據類型是JoinColumn數組。
    • inverseJoinColumns:定義指向非所有者主表的外鍵列,數據類型是JoinColumn數組。

    下面的代碼定義了一個連接表CUST和PHONE的join table。join table的表名是CUST_PHONE,包含兩個外鍵,一個外鍵是CUST_ID,指向表CUST的主鍵ID,另一個外鍵是PHONE_ID,指向表PHONE的主鍵ID。

                     
    @JoinTable(
    table=@Table(name=CUST_PHONE),
    joinColumns=@JoinColumn(name="CUST_ID", referencedColumnName="ID"),
    inverseJoinColumns=@JoinColumn(name="PHONE_ID", referencedColumnName="ID")
    )

    TableGenerator

    TableGenerator定義一個主鍵值生成器,在Id這個元數據的generate=TABLE時,generator屬性中可以使用生成器的名字。生成器可以在類、方法或者屬性上定義。
    生成器是為多個實體類提供連續的ID值的表,每一行為一個類提供ID值,ID值通常是整數。
    元數據屬性說明:

    • name:生成器的唯一名字,可以被Id元數據使用。
    • table:生成器用來存儲id值的Table定義。
    • pkColumnName:生成器表的主鍵名稱。
    • valueColumnName:生成器表的ID值的列名稱。
    • pkColumnValue:生成器表中的一行數據的主鍵值。
    • initialValue:id值的初始值。
    • allocationSize:id值的增量。

    下面的代碼定義了兩個生成器empGen和addressGen,生成器的表是ID_GEN。

                     
    @Entity
    public class Employee {
    ...
    @TableGenerator(
    name="empGen",
    table=@Table(name="ID_GEN"),
    pkColumnName="GEN_KEY",
    valueColumnName="GEN_VALUE",
    pkColumnValue="EMP_ID",
    allocationSize=1
    )
    @Id(generate=TABLE, generator="empGen")
    public int id;
    ...
    }

    @Entity
    public class Address {
    ...
    @TableGenerator(
    name="addressGen",
    table=@Table(name="ID_GEN"),
    pkColumnValue="ADDR_ID"
    )
    @Id(generate=TABLE, generator="addressGen")
    public int id;
    ...
    }

    SequenceGenerator

    SequenceGenerator定義一個主鍵值生成器,在Id這個元數據的generator屬性中可以使用生成器的名字。生成器可以在類、方法或者屬性上定義。生成器是數據庫支持的sequence對象。
    元數據屬性說明:

    • name:生成器的唯一名字,可以被Id元數據使用。
    • sequenceName:數據庫中,sequence對象的名稱。如果不指定,會使用提供商指定的默認名稱。
    • initialValue:id值的初始值。
    • allocationSize:id值的增量。

    下面的代碼定義了一個使用提供商默認名稱的sequence生成器。

                     
    @SequenceGenerator(name="EMP_SEQ", allocationSize=25)

    DiscriminatorColumn

    DiscriminatorColumn定義在使用SINGLE_TABLE或JOINED繼承策略的表中區別不繼承層次的列。
    元數據屬性說明:

    • name:column的名字。默認值為TYPE。
    • columnDefinition:生成DDL的sql片斷。
    • length:String類型的column的長度,其他類型使用默認值10。

    下面的代碼定義了一個列名為DISC,長度為20的String類型的區別列。

                     
    @Entity
    @Table(name="CUST")
    @Inheritance(strategy=SINGLE_TABLE, discriminatorType=STRING, discriminatorValue="CUSTOMER")
    @DiscriminatorColumn(name="DISC", length=20)
    public class Customer { ... }
    posted @ 2009-07-02 09:41 xiaoxinchen 閱讀(176) | 評論 (0)編輯 收藏

    web.xml的配置

    在web.xml中添加

    <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
    classpath:/applicationContext-resources.xml
    classpath:/applicationContext-dao.xml
    classpath:/applicationContext-service.xml
    classpath*:/applicationContext.xml
    /WEB-INF/applicationContext*.xml
    /WEB-INF/security.xml
    /WEB-INF/dealer-security.xml
    </param-value>
    </context-param>

    <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <listener>
    <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
    </listener>
    <listener>
    <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
    </listener>

    事務的配置

    <aop:config>
    <aop:advisor id="managerTx" advice-ref="txAdvice" pointcut="execution(* *..service.*Manager.*(..))" order="0" />
    </aop:config>

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
    <tx:method name="find*" read-only="true"/>
    <tx:method name="search*" read-only="true"/>
    <tx:method name="get*" read-only="true"/>
    <tx:method name="*" rollback-for="Throwable"/>
    </tx:attributes>
    </tx:advice>

    ApplicationContext.xml的配置(spring bean 的配置)

    <!--ProductManager-START-->
    <bean id="productManager" class="com.eryiju.service.product.impl.ProductManagerImpl">
    <constructor-arg ref="productDao" />
    <property name="brandDao" ref="brandDao"></property>
    </bean>

    <!--ProductManager-END-->
    <bean id="productDao" class="com.eryiju.dao.product.impl.ProductDaoHibernate">
    <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>

    <bean id="brandDao" class="com.eryiju.dao.product.impl.BrandDaoHibernate">
    <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>

    常見問題

    如何與hibernate整合

    <!-- Hibernate SessionFactory -->
    <bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="configLocation" value="classpath:hibernate.cfg.xml" />
    <property name="hibernateProperties">
    <value>
    hibernate.dialect=${hibernate.dialect}
    hibernate.query.substitutions=true 'Y', false 'N'
    hibernate.cache.use_second_level_cache=true
    hibernate.cache.use_query_cache=true
    hibernate.cache.provider_class=com.eryiju.util.cache.EhCacheProvider
    hibernate.show_sql=false
    hibernate.connection.release_mode=after_statement
    hibernate.cglib.use_reflection_optimizer=false
    hibernate.search.default.directory_provider=org.hibernate.search.store.FSDirectoryProvider
    hibernate.search.default.indexBase=/opt/dev/static/index
    hibernate.jdbc.batch_size=50
    hibernate.jdbc.fetch_size=50
    </value>
    <!-- Turn batching off for better error messages under PostgreSQL -->
    </property>
    </bean>
    h2. (1)使用spring過濾器解決中文問題

    在web.xml中添加:
    <filter>
    <filter-name>Spring character encoding filter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
    <param-name>encoding</param-name>
    <param-value>GBK</param-value>
    </init-param>
    </filter>

    <filter-mapping>
    <filter-name>Spring character encoding filter</filter-name>
    <url-pattern>/*</url-pattern>
    </filter-mapping>

    h2. (2)將applicationContext.xml分解成多個文件

    applicationContext-common.xml
    "dataSource"
    "sessionFactory"
    事務相關

    applicationContext-dao.xml
    UserDAO

    applicationContext-biz.xml
    UserManager

    applicationContext-action.xml
    Action

    struts-config.xml中要作修改:
    <plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
    <set-property property="contextConfigLocation" value="/WEB-INF/classes/applicationContext-*.xml" />
    </plug-in>
    h2. Spring2.0中的注解實現事務管理

    第一步:引入<tx:>命名空間 ,在spring的配置文件中修改, beans根元素里多了三行,如下
    Xml代碼

    <?xml version="1.0" encoding="UTF-8"?>
    <beans
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

    第二步:在spring的配置文件中修改,將所有具有@Transactional 注解的bean自動配置為聲明式事務支持
    Java代碼

    <!--JDBC事務管理器,根據你的情況使用不同的事務管理器,如果工程中有Hibernate,就用Hibernate的事務管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource">
    <ref local="dataSource"/>
    </property>
    </bean>

    <!-- 用注解來實現事務管理 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

    第三步: 在接口或類的聲明處 ,寫一個@Transactional. 要是只的接口上寫, 接口的實現類就會繼承下來.
    接口的實現類的具體方法,還可以覆蓋類聲明處的設置.
    Java代碼

    @Transactional
    public class TestPOAOImpl extends POAOBase implements TestPOAO
    {
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void test1()
    {
    String sql = "INSERT INTO sy_test (NAME,AGE) VALUES('注解趙云',30)";
    execute(sql);

    sql = "INSERT INTO sy_test (NAME,AGE) VALUES('注解張飛',26)";
    execute(sql);

    int a = 9 / 0; //異常

    sql = "INSERT INTO sy_test (NAME,AGE) VALUES('注解關羽',33)";
    execute(sql);
    System.out.println("走完了");
    }
    //execute() 方法略...
    }

    注意的幾點:

    1 @Transactional 只能被應用到public方法上, 對于其它非public的方法,如果標記了@Transactional也不會報錯,但方法沒有事務功能.
    2 默認情況下,一個有事務方法, 遇到RuntiomeException 時會回滾 . 遇到 受檢查的異常 是不會回滾 的. 要想所有異常都回滾,要加上 @Transactional( rollbackFor={Exception.class,其它異常}) .
    posted @ 2009-07-02 09:40 xiaoxinchen 閱讀(185) | 評論 (0)編輯 收藏
         摘要: 概述 Maven的目標 對Maven的錯誤理解 Maven的版本 Maven的安裝 Windows 2000/xp下的安裝 基于Unxi-based的操作系統(Linux,Solaris and Mac OS X) 查看依賴庫的位置 http://mvnrepository.com/ Maven主要功能 ...  閱讀全文
    posted @ 2009-07-02 09:39 xiaoxinchen 閱讀(533) | 評論 (0)編輯 收藏

    安裝

    sudo apt-get install mysql

    命令行操作

    登錄

    mysql -u用戶名 -p密碼 -h數據庫地址(ip) 數據庫名稱

    注意:盡量不要在-p后直接跟密碼,否則其他人很容易通過查閱命令行歷史記錄(比如,history命令)看到你的密碼。

    SQL參考

    MySQL參考

    常見數據類型

    integer(11) 11位字節的整數
    tinyint(1)
    bigint(20)
    decimal(10,2) 小數
    varchar(20) 最長為20位字節的可變字符串
    char(20) 最長為20位字節的定長字符串(定長指的是存儲空間定長)
    text 文本,用于存大量不固定長度的文本信息
    blob 二級制信息

    常見函數

    length(str) 字符串的長度
    trim(str) 去掉字符串前后的空格
    substring(str,1) 獲取子串
    convert(str using gbk) 將字符串轉化為gbk編碼
    reverse(str) 倒序

    增刪改查

    insert into product (sku,name) values ('123456','productname')

    向表中添加sku=123456,name='productname' 的數據

    update product set name='updated product name' where sku='123456'

    將表product中的sku為'123456'的數據的name字段的值設置為'updated product name'

    select sku,name from product where sku='123456'

    從表product 中查詢 sku為'123456'的數據

    delete from product where sku='123456'

    其他操作實例

    多表查詢

    select p.sku,b.name from product p,brand b where p.brand_id=b.id and p.sku='123456'

    子查詢

    select p.sku,p.name from product p where p.brand_id in (select id from brand where id=123)

    左連接

    select p.sku,p.name,b.name from product p left join brand b on p.brand_id=b.id

    從一個表導入數據到另一個表

    insert into product1 (sku,name,brand_id) (select sku,name,brand_id from product2)

    查找不同的數據

    select distinct p.sku from product p

    查詢時按照某個字段排序(asc升序,desc降序)

    select * from product order by name desc

    常見問題

    如何創建表

    CREATE TABLE  product (
    `sku` char(6) NOT NULL COMMENT '商品的唯一標識\n',
    `brand_id` int(11) default NULL,
    `name` varchar(50) default NULL,
    PRIMARY KEY (`sku`),
    CONSTRAINT `brand_fk_constraint` FOREIGN KEY (`brand_id`) REFERENCES `brand` (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8

    如何創建外鍵

    alter table product add CONSTRAINT `brand_fk_constraint` FOREIGN KEY (`brand_id`) REFERENCES `brand` (`id`)

    如何修改表中的字段

    alter table product modify name varchar(200)

    如何向表中添加字段

    alter table product add comment varchar(200)

    如何刪除表中字段

    alter table product drop name

    存儲過程和觸發器

    h3.mysql創建表

    drop table if exists news;

    /*==========================*/
    /* Table: 消息表 */
    /*==========================*/
    create table news
    (
    NewsId bigint(20) not null unsigned primary key auto_increment,
    NewsContext varchar(50) not null,
    NewsType varchar(50) not null,
    UsersId int(11) not null
    );
    alter table news add constraint FK_Id foreign key (NewsId)
    references users (UsersId);

    資源

    官方參考:http://dev.mysql.com/doc/

    posted @ 2009-07-02 09:38 xiaoxinchen 閱讀(118) | 評論 (0)編輯 收藏

    基本語法

    聲明

    #set ($var=XXX)
    左邊可以是以下的內容:
    • Variable reference
    • String literal
    • Property reference
    • Method reference
    • Number literal #set ($i=1)
    • ArrayList #set ($arr=["yt1","t2"])
    • 算術運算符
    • References 引用的類型

    注釋

    單行:

    ## this is a comment

    多行:

    #* this line
    and this line
    and this line, are comments...*#

    變量 Variables

    以 "$" 開頭,第一個字符必須為字母。character followed by a VTL Identifier. (a .. z or A .. Z).
    變量可以包含的字符有以下內容:
    • alphabetic (a .. z, A .. Z)
    • numeric (0 .. 9)
    • hyphen ("-")
    • underscore ("_")

    Properties

    $Identifier.Identifier
    $user.name
    hashtable user中的的name值.類似:user.get("name")

    h2、Methods

    object user.getName() = $user.getName()

    h2、Formal Reference Notation

    用{}把變量名跟字符串分開。如

    #set ($user="csy"}
    ${user}name

    返回csyname

    $與$!的區別

    當找不到username的時候,$username返回字符串"$username",而$!username返回空字符串""

    雙引號 與 引號

    #set ($var="helo")

    則 test"$var" 返回testhello,test'$var' 返回test'$var'。
    可以通過設置 stringliterals.interpolate=false改變默認處理方式

    條件語句

    #if( $foo )
    <strong>Velocity!</strong>
    #end
    #if($foo)
    #elseif()
    #else
    #end

    當$foo為null或為Boolean對象的false值執行.

    邏輯運算符:

    == && || !

    循環語句

    #foreach($var in $arrays ) // 集合包含下面三種Vector, a Hashtable or an Array
    #end

    #foreach( $product in $allProducts )
    <li>$product</li>
    #end

    #foreach( $key in $allProducts.keySet() )
    <li>Key: $key -> Value: $allProducts.get($key)</li>
    #end

    #foreach( $customer in $customerList )
    <tr><td>$velocityCount</td><td>$customer.Name</td></tr>
    #end

    velocityCount變量在配置文件中定義

    1. Default name of the loop counter
    2. variable reference.
      directive.foreach.counter.name = velocityCount
    3. Default starting value of the loop
    4. counter variable reference.
      directive.foreach.counter.initial.value = 1

    包含文件

    #include( "one.gif","two.txt","three.htm" )

    Parse導入腳本

    #parse("me.vm" )

    #stop 停止執行并返回

    定義宏

    Velocimacros, 相當于函數支持包含功能:

    #macro( d )
    <tr><td></td></tr>
    #end
    調用
    #d()

    帶參數的宏

    #macro( tablerows $color $somelist )
    #foreach( $something in $somelist )
    <tr><td bgcolor=$color>$something</td></tr>
    #end
    #end

    Range Operator

    #foreach( $foo in [1..5] )

    與struts2的整合

    模板裝載位置(按順序依次搜索)

    • Web application 應用程序路徑,會覆蓋掉類路徑下的同名配置文件;
    • Class path 類路徑,一般為缺省模板的配置,公共模板位置;

    數據來源(按順序依次搜索)

    • The value stack
    • The action context
    • Built-in variables

    Struts2-Velocity集成的一些隱含變量

    • stack - ValueStack自身。調用方式:${stack.findString('ognl expr')}
    • action - 最新操作的action
    • reponse
    • request
    • session
    • applicaion - 獲得servlet的環境
    • base - 請求環境的路徑

    Velocity Result 輸出模板

    模擬jsp執行環境,使用velocity的模板直接顯示到servelet的輸出流。

    location - 模板位置,沒有其它參數時的缺省配置。
    parse - 默認true ,false將不解析Ognl expressions.

    配置范例:

    <result name="success" type="velocity">
    <param name="location">foo.vm</param>
    </result>

    等價于以下的配置方式:

    <result name="success" type="velocity">
    foo.vm
    </result>

    Velocity語法

    http://blog.csdn.net/alexwan/archive/2007/10/29/1853285.aspx

    Struts 與 Velocity 的集成

    http://www.ibm.com/developerworks/cn/java/j-sr1.html

    posted @ 2009-07-02 09:38 xiaoxinchen 閱讀(161) | 評論 (0)編輯 收藏
    cdn

    介紹

    CDN 的全稱是Content Delivery Network,即內容分發網絡。其目的是通過在現有的Internet中增加一層新的網絡架構, 將網站的內容發布到最接近用戶的網絡"邊緣", 使用戶可以就近取得所需的內容, 解決Internet網絡擁擠的狀況, 提高用戶訪問網站的響應速度。從技術上全面解決由于網絡帶寬小, 用戶訪問量大, 網點分布不均等原因所造成的用戶訪問網站響應速度慢的問題。

    CDN互聯網內容發布網絡(Content Delivery Network)

    CDN 技術是近年來在美國首先興起并迅速發展起來的一種解決互聯網性能不佳問題的有效手段。其基本思路就是盡可能避開互聯網上有可能影響數據傳輸速度和穩定性的 瓶頸和環節,使內容傳輸的更快、更穩。通過在網絡各處放置節點服務器所構成的在現有的互聯網基礎之上的一層智能虛擬網絡,cdn系統能夠實時地根據網絡流 量和各節點的連接、負載狀況以及到用戶的距離和響應時間等綜合信息將用戶的請求重新導向離用戶最近的服務節點上。

    實際上,內容分發 布網絡(CDN)是一種新型的網絡構建方式,它是為能在傳統的IP網發布寬帶豐富媒體而特別優化的網絡覆蓋層;而從廣義的角度,CDN代表了一種基于質量 與秩序的網絡服務模式。簡單地說,內容發布網(CDN)是一個經策略性部署的整體系統,包括分布式存儲、負載均衡、網絡請求的重定向和內容管理4個要件, 而內容管理和全局的網絡流量管理(Traffic Management)是CDN的核心所在。通過用戶就近性和服務器負載的判斷,CDN確保內容以一種極為高效的方式為用戶的請求提供服務。總的來說,內 容服務基于緩存服務器,也稱作代理緩存(Surrogate),它位于網絡的邊緣,距用戶僅有"一跳"(Single Hop)之遙。同時,代理緩存是內容提供商源服務器(通常位于CDN服務提供商的數據中心)的一個透明鏡像。這樣的架構使得CDN服務提供商能夠代表他們 客戶,即內容供應商,向最終用戶提供盡可能好的體驗,而這些用戶是不能容忍請求響應時間有任何延遲的。據統計,采用CDN技術,能處理整個網站頁面的 70%~95%的內容訪問量,減輕服務器的壓力,提升了網站的性能和可擴展性。

    與目前現有的內容發布模式相比較,CDN強調了網絡 在內容發布中的重要性。通過引入主動的內容管理層的和全局負載均衡,CDN從根本上區別于傳統的內容發布模式。在傳統的內容發布模式中,內容的發布由 ICP的應用服務器完成,而網絡只表現為一個透明的數據傳輸通道,這種透明性表現在網絡的質量保證僅僅停留在數據包的層面,而不能根據內容對象的不同區分 服務質量。此外,由于IP網的"盡力而為"的特性使得其質量保證是依靠在用戶和應用服務器之間端到端地提供充分的、遠大于實際所需的帶寬通量來實現的。在 這樣的內容發布模式下,不僅大量寶貴的骨干帶寬被占用,同時ICP的應用服務器的負載也變得非常重,而且不可預計。當發生一些熱點事件和出現浪涌流量時, 會產生局部熱點效應,從而使應用服務器過載退出服務。這種基于中心的應用服務器的內容發布模式的另外一個缺陷在于個性化服務的缺失和對寬帶服務價值鏈的扭 曲,內容提供商承擔了他們不該干也干不好的內容發布服務。

    縱觀整個寬帶服務的價值鏈,內容提供商和用戶位于整個價值鏈的兩端,中間 依靠網絡服務提供商將其串接起來。隨著互聯網工業的成熟和商業模式的變革,在這條價值鏈上的角色越來越多也越來越細分。比如內容/應用的運營商、托管服務 提供商、骨干網絡服務提供商、接入服務提供商等等。在這一條價值鏈上的每一個角色都要分工合作、各司其職才能為客戶提供良好的服務,從而帶來多贏的局面。 從內容與網絡的結合模式上看,內容的發布已經走過了ICP的內容(應用)服務器和IDC這兩個階段。IDC的熱潮也催生了托管服務提供商這一角色。但 是,IDC并不能解決內容的有效發布問題。內容位于網絡的中心并不能解決骨干帶寬的占用和建立IP網絡上的流量秩序。因此將內容推到網絡的邊緣,為用戶提 供就近性的邊緣服務,從而保證服務的質量和整個網絡上的訪問秩序就成了一種顯而易見的選擇。而這就是內容發布網(CDN)服務模式。CDN的建立解決了困 擾內容運營商的內容"集中與分散"的兩難選擇,無疑對于構建良好的互聯網價值鏈是有價值的,也是不可或缺的最優網站加速服務。

    目前,國內訪問量較高的大型網站如新浪、網易等,均使用CDN網絡加速技術,雖然網站的訪問巨大,但無論在什么地方訪問都會感覺速度很快。而一般的網站如果服務器在網通,電信用戶訪問很慢,如果服務器在電信,網通用戶訪問又很慢。

    它 采取了分布式網絡緩存結構(即國際上流行的web cache技術),通過在現有的Internet中增加一層新的網絡架構,將網站的內容發布到最接近用戶的cache服務器內,通過DNS負載均衡的技 術,判斷用戶來源就近訪問cache服務器取得所需的內容,解決Internet網絡擁塞狀況,提高用戶訪問網站的響應速度,如同提供了多個分布在各地的 加速器,以達到快速、可冗余的為多個網站加速的目的。

    CDN的特點

    1. 本地Cache加速 提高了企業站點(尤其含有大量圖片和靜態頁面站點)的訪問速度,并大大提高以上性質站點的穩定性
    2. 鏡像服務 消除了不同運營商之間互聯的瓶頸造成的影響,實現了跨運營商的網絡加速,保證不同網絡中的用戶都能得到良好的訪問質量。
    3. 遠程加速 遠程訪問用戶根據DNS負載均衡技術 智能自動選擇Cache服務器,選擇最快的Cache服務器,加快遠程訪問的速度
    4. 帶寬優化 自動生成服務器的遠程Mirror(鏡像)cache服務器,遠程用戶訪問時從cache服務器上讀取數據,減少遠程訪問的帶寬、分擔網絡流量、減輕原站點WEB服務器負載等功能。
    5. 集群抗攻擊 廣泛分布的CDN節點加上節點之間的智能冗于機制,可以有效地預防黑客入侵以及降低各種D.D.o.S攻擊對網站的影響,同時保證較好的服務質量 。

    關鍵技術

    1. 內容發布:它借助于建立索引、緩存、流分裂、組播(Multicast)等技術,將內容發布或投遞到距離用戶最近的遠程服務點(POP)處;
    2. 內容路由:它是整體性的網絡負載均衡技術,通過內容路由器中的重定向(DNS)機制,在多個遠程POP上均衡用戶的請求,以使用戶請求得到最近內容源的響應;
    3. 內容交換:它根據內容的可用性、服務器的可用性以及用戶的背景,在POP的緩存服務器上,利用應用層交換、流分裂、重定向(ICP、WCCP)等技術,智能地平衡負載流量;
    4. 性能管理:它通過內部和外部監控系統,獲取網絡部件的狀況信息,測量內容發布的端到端性能(如包丟失、延時、平均帶寬、啟動時間、幀速率等),保證網絡處于最佳的運行狀態。

    P4P與傳統CDN、P2P的對比

    7 月30日消息:德國一個名為iPoque的研究機構在2007年研究了一百多萬網民將近 3TB的匿名數據流量,調查地區包括澳大利亞、東歐、德國、中東和南歐地區。調查發現,目前網絡帶寬“消費大戶”是P2P文件共享,在中東占據了49%, 東歐地區占據了84%。從全球來看,晚上時段的網絡帶寬有95%被P2P占據。據國內權威部門統計,當前P2P流量已經占整個互聯網流量的約70%,并且 正在以每年350%的速度增長。P2P流量消耗了巨大的網絡帶寬,尤其是國際帶寬,使網絡基礎設施不堪重負,運營商苦不堪言。

    問題 的癥結不在于P2P,而在于交換的機制。P2P過于強調“對等”,每個節點之間的交換完全是無序的。一個北京的用戶,既可能和廣州的用戶進行文件片段的交 換,也可能和遠在美國的某用戶進行交換。顯然,無序的交換導致了無謂的跨地區甚至是跨國的 “流量旅行”,這耗費了寶貴的國內和國際帶寬資源,代價巨大。

    如 果正好用戶都在同一個地區,那么,本地化的交換的成本就會大大降低。這也正是P4P的簡單原理——讓P2P也玩“同城”。 P4P全稱是“Proactive network Provider Participation for P2P(電信運營商主動參與P2P網絡)”。與P2P隨機挑選Peer(對等機)不同,P4P協議可以協調網絡拓撲數據,能夠有效選擇節點,從而提高網絡 路由效率。仍以上述例子來說,北京的用戶就可以優先和北京同城的用戶來實現文件片段的交換,再擴展至較遠的地區,有十分的必要時,才會出國進行文件片段交 換。當然,P4P的運行機制,要遠遠超過“同城交換”的概念,它還會根據用戶的上行、下載帶寬進行綜合判斷,以進行最有效選擇,最大化整體交換的效率。

    舉 幾個例子可以說明CDN的普遍作用。例如2008年,北京奧運會之前,關于門票網絡出售,很多國內的朋友都去登陸,而大家的登錄的時刻幾乎千篇一律,導致 中國政府的網站服務器支撐不了這么大的請求,誰都進去不了,都被堵死在門外。這中現象在國內許多網站都出現過,比如高考時期,學生上網填申請,大家都說是 自己網絡原因,其實不然,這都跟被請求的服務器有關,來自四面八方的請求去請求他一個網站,他自然接受不了這么大的帶寬,給人一種印象,就是互聯網太慢, 落后了。這樣的例子很多,我們就不紛紛介紹了。不過互聯網,網聚全球人的智慧。解決方法很多,美國作為世界的互聯網中心,提供了CDN技術,內容網絡分 發。美國很多軍方工程都是采用該技術。我國國內的香港海洋科技集團的GTONE產品,專業為我國國內大型公司提供海外服務,在全球都有很廣的資源。國內很 多很有實力的公司都和他們合作了。

    新建文件

    posted @ 2009-07-02 09:37 xiaoxinchen 閱讀(119) | 評論 (0)編輯 收藏
    1. 基本命令
    1. 常見文件操作
            建立目錄:mkdir 目錄名
    刪除空目錄:rmdir 目錄名
    無條件刪除子目錄: rm -rf 目錄名
    改變當前目錄:cd 目錄名 (進入用戶home目錄:cd ~;進入上一級目錄:cd -)
    查看自己所在目錄:pwd
    查看當前目錄大小:du
    顯示目錄文件列表:ls -l (-a:增加顯示隱含目錄)
    其中:藍:目錄;綠:可執行文件;紅:壓縮文件;淺藍:鏈接文件;灰:其他文件;紅底白字:錯誤的鏈接文件
    瀏覽文件:more 文件名.txt;less 文件名.txt
    復制文件: cp 源文件 目標文件 (-r:包含目錄)
    查找文件:(1)find (2)locate 命令名
    鏈接:(1)建立hard鏈接:ln 來源文件 鏈接文件(-d:創建目錄鏈接);(2)建立符號鏈接:ln -s 來源文件 鏈接文件
    文本編碼轉換工具iconv:iconv -f gb2312 -t utf-8 -o new.txt old.txt
    輸入/輸出格式規范
    -f, --from-code=NAME 原始文本編碼,
    -t,--to-code=NAME 輸出編碼,信息
    -l, --list 列出所有已知編碼字符集

    輸出控制:

    -c 忽略輸出中的無效字符
    -o, --output=FILE 輸出文件
    -s, --silent suppress warnings
    # 進程管理
            列出當前進程ID:ps -auxw
    終止進程:(1)終止單一進程:kill 進程ID號
    終止該程序所有進程:killall 程序名
    終止X-Window程序:xkill
    查看資源占用情況:(1)top (2)free (3)dmesg
    查看環境變量值:env
    重啟:(1)reboot (2)Ctrl Alt Del (3)init 6
    關機:(1)shutdown -h now (2)halt (3)init 0
    # 網絡管理
            顯示網絡接口參數:ifconfig
    聯機狀況:ping xxx.xxx.xxx.xxx
    顯示網絡狀況:netstat ,其中:options:-a==所有sockets;-l==包含網絡設備;-n==數字IP;-o==其他信息;-r==路由表;-t==只列TCP sockets;-u==只列UDP sockets;-w==只列raw sockets;
    -x==只列Unix Domain sockets

    # 權限設定
            (1)chmod -a|u|g|o |-|=r|w|x 文件/目錄名
    其中:a--所有用戶(all);u--本用戶(user);g--用戶組(group);o--其他用戶(other users)
    --增加權限;---刪除權限;=--設置權限
    文件:r--只讀權限(read);w--寫權限(write);x--執行權限(execute)
    目錄:r--允許列目錄下文件和子目錄;w--允許生成和刪除目錄下文件;x--允許訪問該目錄
    (2)chmod xxx 文件/目錄名
    其中:execute=1;write=2;read=4
    x取值:0--沒有任何權限(常用);1--只能執行(不常見);2--只能寫(不常見);3--只能寫和執行(不常見);4--只讀(常見);5--只讀和執行(常見);6--讀和寫(常見);7--讀.寫和執行
    # vim 常見命令
            進入后為命令模式:(1)插入i;(2)打開0;(3)修改c;(4)取代r;(5)替換s
    經(1)后進入全屏幕編輯模式。
    命令模式-->編輯模式(a/i);編輯模式-->命令模式(Esc);命令模式-->末行模式(:)。
    :w/w newfile保存
    :q/q!退出iv;:wq保存退出
    http://vimcdoc.sourceforge.net/doc/help.html

    #ln命令

    ln 命令

    用途 : 鏈接文件。

    語法

    1>將某個文件鏈接到一個文件上 ln [ -f | -n] [ -s ] SourceFile [ TargetFile ]

    2>將一個或多個文件鏈接到一個目錄上 ln [ -f | -n] [ -s ] SourceFile ... TargetDirectory

    描述 ln 命令將在 SourceFile 參數中指定的文件鏈接到在 TargetFile 參數中指定的文件,或將其鏈接到在 TargetDirectory 參數中指定的另一個目錄中的文件。

    在缺省情況下,ln 命令會創建硬鏈接。如果需要使用 ln 命令來創建符號鏈接,請指明 -s 標志。

    符號鏈接是指向文件的一個間接指針;它的目錄項中包含了它所鏈接的文件名。符號鏈接可能會跨越文件系統,可能指向目錄。

    如果正在將某個文件鏈接到新的名字,那么只能列出一個文件。如果鏈接到一個目錄,那么可以列出多個文件。

    TargetFile 參數是可選的。

    如果不指定目標文件,ln 命令會在當前的目錄中創建一個新的文件。新的文件繼承了指定在 SourceFile 參數中的文件名。

    注意: 如果不使用 -s 標志,就不能在文件系統之間鏈接文件。 如果 TargetDirectory 已經是鏈接到目錄上的一個符號鏈接,那么 ln 命令將現有的目標視為文件。 這意味著,類似于 ln -fs somepath/lname symdir 的命令不會遵循現有的 symdir 符號鏈接,作為代替,它會創建一個從 somepath/lname 到 symdir 的新的符號鏈接。

    參數

    -f 促使 ln 命令替換掉任何已經存在的目的路徑。如果目的路徑已經存在,而沒有指定 -f 標志,ln 命令不會創建新的鏈接,而是向標準錯誤寫一條診斷消息并繼續鏈接剩下的 SourceFiles。

    -n 指定,如果鏈接是一個現有的文件,那么不要覆蓋文件的內容。 -f 標志重設了這個標志。這是缺省的行為。

    -s 促使 ln 命令創建符號鏈接。符號鏈接中包含了它所鏈接的文件的名字。當對鏈接執行打開操作的時候,會使用到引用文件。對符號鏈接的 stat 調用會返回鏈接的目標文件;必須完成lstat 調用來獲取鏈接的信息。可以使用 readlink 調用來讀取符號鏈接的內容。符號鏈接可能跨越文件系統,指向目錄。

    注意:當為 -s 標志指定 SourceFile 參數的時候,必須使用絕對路徑。如果沒有指明絕對路徑,那么當 SourceFile 和 TargetFile 參數位于不同的目錄中的時候,可能會發生意外的結果。在創建符號鏈接之前,不需要存在源文件。

    退出狀態 此命令返回以下的退出值:

    0 所有指定的文件都成功鏈接上了。

    0 出現一次錯誤。

    示例

    1>為了創建到一個文件的另一個鏈接(別名),請輸入:

    ln -f file1 file2 這會將 file1 鏈接到新的名稱, file2。如果 file2 不存在,那么會創建該文件名。如果 file2 已經存在了,那么這個文件會被替換為指向 file1的一個鏈接。然后 file1 和 file2 文件名會指向同一個文件。對其中任何一個的更改都會出現在另一個中。如果一個文件名被 rm 命令刪除,那么該文件并沒有完全被刪除,因為它仍然以其它的名字存在。

    2>為了將文件鏈接為另一個目錄中的相同名字,請輸入:

    ln index dir1 這會將 index 鏈接到新的名稱,dir1/index。

    注意:在示例 1 中的 file2 是一個文件的名稱;在示例 2 中的 dir1 是一個已經存在的目錄。

    3>為了將幾個文件鏈接為另一個目錄中的名稱,請輸入:

    ln file2 dir2/file3 /home/dir1 這會將 file2 鏈接到新的名稱 /home/dir1/file2;將 dir2/file3 鏈接到新的名稱 /home/dir1/file3。

    4>如果想要在 ln 命令中使用模式匹配字符,請輸入:

    ln dir1/* . 這會將 dir1 目錄中的所有文件鏈接到當前目錄中, . (點),給他們在 dir1 目錄中同樣的名稱。

    注意: 必須在星號和句點之間輸入一個空格。

    5>為了創建一個符號鏈接,輸入:

    ln -s /tmp/test test

    這會在當前的目錄中創建符號鏈接 test。 test 文件指向 /tmp/test 文件。如果 /tmp/test 文件已經存在了,那么 cat test 命令可以列出其內容。

    6>如果想要在不指明 TargetFile 參數的情況下得到相同的結果,請輸入:

    ln -s /tmp/test

    文件

    /usr/bin/ln 包含了 ln 命令。

    1. 常見配置文件
    posted @ 2009-07-02 09:37 xiaoxinchen 閱讀(126) | 評論 (0)編輯 收藏

    介紹

    入門

    安裝

    不用安裝,直接解壓即可。如果進行JEE開發,可以用eclipse-jee-ganymede-SR2文件,而PHP的話可以直接用PDT。Python可以用基于Apanta的PyDev。

    基本操作

    選項:Windows -> Preference。
    安裝新特性/插件:Help -> SoftwareUpdates

    常用快捷鍵

    快捷鍵的設置和修改在Windows -> Preference ->General -> Keys;

    我最常用的:
    Ctrl+Space 代碼助手完成一些代碼的插入(但一般和輸入法有沖突,可以Alt+/來代替,在keys里找到command 為Content Assist的,把其Binding改為Alt+/) 這個是我最喜歡的功能,你可以把變量命名的很長,下次引用時只要打首個字母,再打Alt+/,就能寫出變量。
    Ctrl+Q 定位到最后編輯的地方
    Ctrl+Shift+R 全局 打開資源
    Ctrl+E 快速顯示當前Editer的下拉列表(如果當前頁面沒有顯示的用黑體表示)
    Java編輯器 組織導入 Ctrl+Shift+O
    Java編輯器 添加導入 Ctrl+Shift+M
    Alt+← 前一個編輯的頁面
    Alt+→ 下一個編輯的頁面(當然是針對上面那條來說了)
    Ctrl+1 快速修復
    Ctrl+/ 注釋當前行,再按則取消注釋
    Ctrl+D: 刪除當前行
    Java編輯器 格式化 Ctrl+Shift+F
    Alt+↓ 當前行和下面一行交互位置(特別實用,可以省去先剪切,再粘貼了)
    Alt+↑ 當前行和上面一行交互位置(同上)
    Ctrl+L 定位在某行 (對于程序超過100的人就有福音了)
    Alt+Shift+R 重命名 (是我自己最愛用的一個了,尤其是變量和類的Rename,比手工方法能節省很多勞動力)
    Alt+Shift+Z 使用try/catch塊來包圍

    下面是網上轉過來
    Eclipse快捷鍵大全(轉載)
    Ctrl+1 快速修復(最經典的快捷鍵,就不用多說了)
    Ctrl+D: 刪除當前行
    Ctrl+Alt+↓ 復制當前行到下一行(復制增加)
    Ctrl+Alt+↑ 復制當前行到上一行(復制增加)
    Alt+↓ 當前行和下面一行交互位置(特別實用,可以省去先剪切,再粘貼了)
    Alt+↑ 當前行和上面一行交互位置(同上)
    Alt+← 前一個編輯的頁面
    Alt+→ 下一個編輯的頁面(當然是針對上面那條來說了)
    Alt+Enter 顯示當前選擇資源(工程,or 文件 or文件)的屬性
    Shift+Enter 在當前行的下一行插入空行(這時鼠標可以在當前行的任一位置,不一定是最后)
    Shift+Ctrl+Enter 在當前行插入空行(原理同上條)
    Ctrl+Q 定位到最后編輯的地方
    Ctrl+L 定位在某行 (對于程序超過100的人就有福音了)
    Ctrl+M 最大化當前的Edit或View (再按則反之)
    Ctrl+/ 注釋當前行,再按則取消注釋
    Ctrl+O 快速顯示 OutLine
    Ctrl+T 快速顯示當前類的繼承結構
    Ctrl+W 關閉當前Editer
    Ctrl+K 參照選中的Word快速定位到下一個
    Ctrl+E 快速顯示當前Editer的下拉列表(如果當前頁面沒有顯示的用黑體表示)
    Ctrl+/(小鍵盤) 折疊當前類中的所有代碼
    Ctrl+×(小鍵盤) 展開當前類中的所有代碼
    Ctrl+Space 代碼助手完成一些代碼的插入(但一般和輸入法有沖突,可以修改輸入法的熱鍵,也可以暫用Alt+/來代替)
    Ctrl+Shift+E 顯示管理當前打開的所有的View的管理器(可以選擇關閉,激活等操作)
    Ctrl+J 正向增量查找(按下Ctrl+J后,你所輸入的每個字母編輯器都提供快速匹配定位到某個單詞,如果沒有,則在stutes line中顯示沒有找到了,查一個單詞時,特別實用,這個功能Idea兩年前就有了)
    Ctrl+Shift+J 反向增量查找(和上條相同,只不過是從后往前查)
    Ctrl+Shift+F4 關閉所有打開的Editer
    Ctrl+Shift+X 把當前選中的文本全部變味小寫
    Ctrl+Shift+Y 把當前選中的文本全部變為小寫
    Ctrl+Shift+F 格式化當前代碼
    Ctrl+Shift+P 定位到對于的匹配符(譬如{}) (從前面定位后面時,光標要在匹配符里面,后面到前面,則反之)

    下面的快捷鍵是重構里面常用的,本人就自己喜歡且常用的整理一下(注:一般重構的快捷鍵都是Alt+Shift開頭的了)
    Alt+Shift+R 重命名 (是我自己最愛用的一個了,尤其是變量和類的Rename,比手工方法能節省很多勞動力)
    Alt+Shift+M 抽取方法 (這是重構里面最常用的方法之一了,尤其是對一大堆泥團代碼有用)
    Alt+Shift+C 修改函數結構(比較實用,有N個函數調用了這個方法,修改一次搞定)
    Alt+Shift+L 抽取本地變量( 可以直接把一些魔法數字和字符串抽取成一個變量,尤其是多處調用的時候)
    Alt+Shift+F 把Class中的local變量變為field變量 (比較實用的功能)
    Alt+Shift+I 合并變量(可能這樣說有點不妥Inline)
    Alt+Shift+V 移動函數和變量(不怎么常用)
    Alt+Shift+Z 重構的后悔藥(Undo)

    編輯
    作用域 功能 快捷鍵
    全局 查找并替換 Ctrl+F
    文本編輯器 查找上一個 Ctrl+Shift+K
    文本編輯器 查找下一個 Ctrl+K
    全局 撤銷 Ctrl+Z
    全局 復制 Ctrl+C
    全局 恢復上一個選擇 Alt+Shift+↓
    全局 剪切 Ctrl+X
    全局 快速修正 Ctrl1+1
    全局 內容輔助 Alt+/
    全局 全部選中 Ctrl+A
    全局 刪除 Delete
    全局 上下文信息 Alt+?
    Alt+Shift+?
    Ctrl+Shift+Space
    Java編輯器 顯示工具提示描述 F2
    Java編輯器 選擇封裝元素 Alt+Shift+↑
    Java編輯器 選擇上一個元素 Alt+Shift+←
    Java編輯器 選擇下一個元素 Alt+Shift+→
    文本編輯器 增量查找 Ctrl+J
    文本編輯器 增量逆向查找 Ctrl+Shift+J
    全局 粘貼 Ctrl+V
    全局 重做 Ctrl+Y

    查看
    作用域 功能 快捷鍵
    全局 放大 Ctrl+=
    全局 縮小 Ctrl+-

    窗口
    作用域 功能 快捷鍵
    全局 激活編輯器 F12
    全局 切換編輯器 Ctrl+Shift+W
    全局 上一個編輯器 Ctrl+Shift+F6
    全局 上一個視圖 Ctrl+Shift+F7
    全局 上一個透視圖 Ctrl+Shift+F8
    全局 下一個編輯器 Ctrl+F6
    全局 下一個視圖 Ctrl+F7
    全局 下一個透視圖 Ctrl+F8
    文本編輯器 顯示標尺上下文菜單 Ctrl+W
    全局 顯示視圖菜單 Ctrl+F10
    全局 顯示系統菜單 Alt+-

    導航
    作用域 功能 快捷鍵
    Java編輯器 打開結構 Ctrl+F3
    全局 打開類型 Ctrl+Shift+T
    全局 打開類型層次結構 F4
    全局 打開聲明 F3
    全局 打開外部javadoc Shift+F2
    全局 打開資源 Ctrl+Shift+R
    全局 后退歷史記錄 Alt+←
    全局 前進歷史記錄 Alt+→
    全局 上一個 Ctrl+,
    全局 下一個 Ctrl+.
    Java編輯器 顯示大綱 Ctrl+O
    全局 在層次結構中打開類型 Ctrl+Shift+H
    全局 轉至匹配的括號 Ctrl+Shift+P
    全局 轉至上一個編輯位置 Ctrl+Q
    Java編輯器 轉至上一個成員 Ctrl+Shift+↑
    Java編輯器 轉至下一個成員 Ctrl+Shift+↓
    文本編輯器 轉至行 Ctrl+L

    搜索
    作用域 功能 快捷鍵
    全局 出現在文件中 Ctrl+Shift+U
    全局 打開搜索對話框 Ctrl+H
    全局 工作區中的聲明 Ctrl+G
    全局 工作區中的引用 Ctrl+Shift+G

    文本編輯
    作用域 功能 快捷鍵
    文本編輯器 改寫切換 Insert
    文本編輯器 上滾行 Ctrl+↑
    文本編輯器 下滾行 Ctrl+↓

    文件
    作用域 功能 快捷鍵
    全局 保存 Ctrl+X
    Ctrl+S
    全局 打印 Ctrl+P
    全局 關閉 Ctrl+F4
    全局 全部保存 Ctrl+Shift+S
    全局 全部關閉 Ctrl+Shift+F4
    全局 屬性 Alt+Enter
    全局 新建 Ctrl+N

    項目
    作用域 功能 快捷鍵
    全局 全部構建 Ctrl+B

    源代碼
    作用域 功能 快捷鍵
    Java編輯器 格式化 Ctrl+Shift+F
    Java編輯器 取消注釋 Ctrl+\
    Java編輯器 注釋 Ctrl+/
    Java編輯器 添加導入 Ctrl+Shift+M
    Java編輯器 組織導入 Ctrl+Shift+O
    Java編輯器 使用try/catch塊來包圍 未設置,太常用了,所以在這里列出,建議自己設置。
    也可以使用Ctrl+1自動修正。

    運行
    作用域 功能 快捷鍵
    全局 單步返回 F7
    全局 單步跳過 F6
    全局 單步跳入 F5
    全局 單步跳入選擇 Ctrl+F5
    全局 調試上次啟動 F11
    全局 繼續 F8
    全局 使用過濾器單步執行 Shift+F5
    全局 添加/去除斷點 Ctrl+Shift+B
    全局 顯示 Ctrl+D
    全局 運行上次啟動 Ctrl+F11
    全局 運行至行 Ctrl+R
    全局 執行 Ctrl+U

    重構
    作用域 功能 快捷鍵
    全局 撤銷重構 Alt+Shift+Z
    全局 抽取方法 Alt+Shift+M
    全局 抽取局部變量 Alt+Shift+L
    全局 內聯 Alt+Shift+I
    全局 移動 Alt+Shift+V
    全局 重命名 Alt+Shift+R
    全局 重做 Alt+Shift+Y

    檢出項目

    首先安裝SVN插件(subversive或者subclipse),然后新建Project,從SVN檢出項目。需要特別注意的是,要在general / workspace選項中正確設置字符編碼,否則可能會出現編譯錯誤。

    用Maven構建項目

    安裝IAM之后,在項目的context菜單中選“Maven 2 / Use Maven Dependency Management”,然后就可以管理依賴。

    查看數據庫

    打開 Database Perspective 即可。注意要先選擇驅動程序,即mysql的java connector的jar,然后才是配置各個連接的jdbc地址。

    關于插件

    插件安裝方法(zz)

    英文教程:http://www.venukb.com/2006/08/20/install-eclipse-plugins-the-easy-way/

    有關插件安裝問題,四種常用的方法在此特別注明:

    #“幫助”->“軟件更新”->“查找并安裝”->“搜索要安裝的新功能部件”->“新建遠程站點”(此種方式用于在線更新)
    #“幫助”->“軟件更新”->“查找并安裝”->“搜索要安裝的新功能部件”->“新建本地站點”(如果插件已經下載到了本地,請不要用第一種方法)
    1. 直接拷貝plugins和features兩個目錄下的內容置于$Eclipse_Home$/對應的plugins和features下面
    2. 用link外鏈接與外部插件關聯

    最菜的,一般用第一種方法,而大部分生手一般選擇第二或者第三種方法,用得習慣的一般選擇最后一種方式。此四類方法優劣勢對比如下:
    前三種方法都會將插件文件拷貝至相$Eclipse_Home$/對應的plugins和features目錄下,從本質上看,沒多大區別,并且插件只能 安裝和禁用,不能卸載(當然,如果你對插件對應的目錄和文件都很熟悉的話,可以通過直接刪除拷進去的文件來達到卸載插件的目的),但方法一和方法二在安裝 插件的時候很容易出錯或者是產生沖突,特別是當你用了Myeclipse插件、中文包的同時,又想安裝HibernateSynchronizer、 Jode Compiler(Class反編譯工具)、Visual Editor等插件時,及有可能導致Myeclipse插件和中文包失效。

    所以,如果插件已經下載到了本地,請直接拷貝至$Eclipse_Home$/對應的plugins和features目錄下,也就是用方法三,這樣能避免沖突。

    方 法四是將所有的插件用一個外部目錄存放起來,假如是D:\plug-in,將上面所示的插件目錄文件全部拷貝到該目錄下,比如Tomcat插件,此時的文 件路徑就是D:\plug-in\tomcat_plug\eclipse\plugins \com.sysdeo.eclipse.tomcat_3.1.0.beta(請注意,方法四一定要嚴格這樣的目錄路徑放置文件)。然后 在$Eclipse_Home$下新建一個links目錄,并在links目錄下建立關聯文件,假如是tomcat.link,在建立的關聯文件中加入如 下語句:

    path=D:/plug-in/tomcat_plug

    還可以寫成相對路徑的形式。剩下的事情,不用我說你肯定都知道了,就是重啟Eclipse,在Dos窗口下進入Eclipse安 裝目錄,鍵入命令eclipse -clean,回車,或者進入$Eclipse_Home$/configuration目錄,刪除org.eclipse.update后再重新啟動 Eclipse。

    QA相關插件

    參考:http://www.ibm.com/developerworks/cn/java/j-ap01117/

    CheckStyle http://eclipse-cs.sourceforge.net/update/
    Coverlipse http://coverlipse.sf.net/update
    PMD http://pmd.sourceforge.net/eclipse/
    JDepend http://andrei.gmxhome.de/eclipse/
    Metrics http://metrics.sourceforge.net/update
    FindBugs http://findbugs.cs.umd.edu/eclipse

    參考資源

    http://www.ibm.com/developerworks/cn/eclipse/resources.html
    http://www.eclipseplugincentral.com/

    posted @ 2009-07-02 09:36 xiaoxinchen 閱讀(269) | 評論 (0)編輯 收藏

    Web標準

    我理解的Web標準
    web標準就是瀏覽器的開發廠商和界面開發人員共同遵循的標準。由萬維網聯盟W3C(World Wide Web Consortium)組織制定。
    Web標準化無異于工業標準化,就好像改錐和螺絲一樣,他們都遵循同樣的標準,易用,有效,甚至能贏得戰爭(注1)。
    遵循Web標準的好處
    • 高效率。開發人員可以更容易地理解彼此的編碼,web開發的團隊協作將得到簡化。
    • 易用性。大家都遵循標準,未來也許就不存在跨瀏覽器問題,也可以跨平臺(比如移動電話),另外,標準也可以使殘疾人士更容易地使用web。
    • 利于訪問。更易被搜索引擎訪問,也更易被準確地索引。

    注1:德國的工業非常發達,二戰時德國設計的武器都是標準件,即使一輛趟克被炸爛,其中的零件都還可以再利用,甚至可以應用到槍支或其他武器上。

    Acid3

    測試瀏覽器支持Acid3的程度:http://acid3.acidtests.org/

    為什么要使用Web標準(zz)?

    英文版:http://www.webstandards.org/learn/faq/
    中文版:http://www.webstandards.org/learn/faq/faq_zh-simplified/

    posted @ 2009-07-02 09:35 xiaoxinchen 閱讀(109) | 評論 (0)編輯 收藏

    快速入門

    歡迎使用BeanShell.這是一個速成課程。我們將省去一些重要的選項和細節。要學習更多的內容請看本User's Guide的其它部分。

    下載和運行BeanShell

    請到http://www.beanshell.org下載最新的JAR文件。你可以用圖形桌面模式和命令行模式起動BeanShell。
    如果你只是要玩一玩BeanShell,你可以在BeanShell的jar文件上雙擊來起動BeanShell的桌面。但不管怎樣,如果你要讓BeanShell與你的類與應用程序一起工作就必須將BeanShell的jar文件加到classpath中。
    你可以將BeanShell的jar文件拖到JAVA_HOME的ext目錄也可以直接加到classpath中。

    • windows用戶請將bsh.jar放在JAVA_HOME/jre/lib/ext文件夾,OSX用戶可以放在/Library/Java/Extensions.
      或者增加BeanShell到你的classpath目錄,如:
      unix: export CLASSPATH=$CLASSPATH:bsh-xx.jar
      windows:set classpath %classpath%;bsh-xx.jar

    然后你就可以運行BeanShell在GUI或命令行模式:

    •  java bsh.Console       // run the graphical desktop
      or
           java bsh.Interpreter   // run as text-only on the command line
      or
           java bsh.Interpreter filename [ args ] // run script file

    可以在你的應用程序內部來運行,也可以作為debug及servlet的遠程服務器模式,或一個Applet內部來運行BeanShell。請參考"BeanShell Modes of Operation"獲得更多詳情。

    BeanShell GUI

    用GUI模式啟動BeanShell后,將打開一個桌面視窗。用鼠標右擊在桌面的背景上,你可以打開另一個控制臺視窗及其它的工具如一個簡單的類游覽器。
    每一個控制臺視窗運行一個獨立的BeanShell解釋器。這個圖形化的控制臺支持基本的歷史命令,行編輯,剪切和粘貼,甚至類和變量名的自動完成功能。從控制臺你能開啟一個簡單的編輯視窗。在它里面,你可以編寫腳本和使用‘eval’選項求表達式的值。

    Java語句和表達式

    BeanShell能理解標準的JAVA語句,表達式,和方法宣告。語句和表達式的內容可以是:變量,宣告,賦值,方法調用,循環,條件等。
    在 Java程序中你必須嚴格的使用它們,但在BeanShell中,你可以用“寬松類型”(loosely typed)的方式來使用它們。也就是說,你可以在寫腳本時偷懶,不進行變量類型的宣告(在原始數據類型和對象都可以)。如果你試著用錯變量類 型,BeanShell將會給出一個錯誤。
    這里有一些例子:

    • foo = "Foo";   
      four = (2 + 2)*2/2;
      print( foo + " = " + four );  // print() is a BeanShell command
      // Do a loop
      for (i=0; i<5; i++)
          print(i);  
      // Pop up a frame with a button in it
      button = new JButton( "My Button" );
      frame = new JFrame( "My Frame" );
      frame.getContentPane().add( button, "Center" );
      frame.pack();
      frame.setVisible(true);

    有用的BeanShell命令

    在 剛才那個例子中我們用了一個內建在BeanShell中的一個方便的命令print(),來顯示變量的值。print()跟ava的 System.out.println()非常的相像,除非它能保證輸出總是命令行。print()也可以顯示一些對象的類型(如數組),但比Java的 更詳細。另一個相關的命令是show(),用來開啟與關閉顯示你輸入的每一行的結果。
    這兒是一些其它的BeanShell的命令:
    source(), run() - 將一個bsh腳本讀到解釋器或運行在另一個解釋器。
    frame() - 顯示一個Frame或JFrame的GUI組件.
    load(), save() - 載入和保存一個序列化的對象到一個文件.
    cd(), cat(), dir(), pwd(), etc. - 類unix的shell命令。
    exec() - 運行一個本地的程序。
    javap() - 打印一個對象的方法和字段,類似于Java的javap命令。
    setAccessibility() - 開啟無限制的存取private 和protected的組件。
    要獲得更多的信息請查看BeanShell命令的詳細清單。

    提示:
    BeanShell命令并不是真的"內建"其中的,而是作為腳本方法自動從classpath載入的. 你可以擴展基本命令集并加到classpath中作為自訂義的腳本來使用。

    腳本方法

    你可以在bsh中宣告和使用方法,就像在java的類中一樣。

    • int addTwoNumbers( int a, int b ) {
          return a + b;
      }
      sum = addTwoNumbers( 5, 7 );  // 12

    bsh的方法可以有動態的(寬松的)參數和返回類型。

    • add( a, b ) {
          return a + b;
      }
      foo = add(1, 2);            // 3
      foo = add("Oh", " baby");   // "Oh baby"

    實現Interface

    注意:如果要BeanShell能實現任意的Interface,必須有jdk1.3及以上支持。
    你可以在腳本中用標準的Java內部類的語法來實現Interface.例如:

    • ActionListener scriptedListener = new ActionListener() {
          actionPerformed( event ) { ... }
      }

    你 可以不用實現Interface的所有方法,而只用實現你需要的方法。如果代碼中調用了未被實現的方法,將丟出異常。如果你想重載大量的方法的行為--例 如為日志生成一個"啞"適配器--你可以在腳本對象中實現一個特殊的方法:invoke(name,args)。invoke()方法用來處理任何未被定 義的方法的調用:

    • ml = new MouseListener() {
          mousePressed( event ) { ... }
          // handle the rest
          invoke( name, args ) { print("Method: "+name+" invoked!");
      }

    腳本對象

    在 BeanShell中,和在JavaScript與Perl中一樣,腳本對象是用封閉的方法體一構成的。通過在方法未尾返回一個特殊值"this",你就 可以像使用方法一樣調用這個對象了。在這個方法調用時,你可以給與它任何的值。通常對象內部需要包括方法,所以BeanShell的腳本方法在一定程度上 可再包含一些方法以構成腳本對象。例如:

    • foo() {
          print("foo");
          x=5;
          bar() {
              print("bar");
          }
          return this;
      }
      myfoo = foo();    // prints "foo"
      print( myfoo.x ); // prints "5"
      myfoo.bar();      // prints "bar"

    如果這些代碼對你來說很陌生,別急,請用戶手冊可得到更透徹的解釋。

    在 你的腳本中,BeanShell腳本對象(也就是先前例子中的"this"參照)能自動實現任何JAVA介面類型。當JAVA代碼調用相應與之通訊的腳本 方法內的方法。當你試著將腳本對象作為參數傳給Java方法時,BeanShell會自動將它造型(cast)為相應的類型。如要傳遞BeanShell 外部的對象時,你可以在需要時顯式的進行造型(cast).請看用戶手冊中的詳細內容。

    從你的應用程序調用BeanShell

    通過建立一個BeanShell解釋器,使用eval()或source()命令,你可以在你的應用程序中求文本表達式的值和運行腳本。如果你希望在你的腳本內部使用一個對象,可以用set()方法傳遞對象的變量參照給BeanShell,并通過get()方法取得結果。

    • import bsh.Interpreter;
      Interpreter i = new Interpreter();  // Construct an interpreter
      i.set("foo", 5);                    // Set variables
      i.set("date", new Date() );
      Date date = (Date)i.get("date");    // retrieve a variable
      // Eval a statement and get the result
      i.eval("bar = foo*10");            
      System.out.println( i.get("bar") );
      // Source an external script file
      i.source("somefile.bsh");

     

    BeanShell將成為Java平臺上的第三種編程語言
    2005-06-08  點擊:8  來源:CSDN  作者:CSDN
    JCP接納了一個新的技術規范進入標準化進程,這個編號為JSR-274的技術規范將把BeanShell引入為Java平臺上支持的又一種編程語言。

    JSR- 274(http://jcp.org/en/jsr/detail?id=274)是由 Patrick Niemeyer提交的技術規范,其目標是將BeanShell腳本語言(http://www.beanshell.org/)規范化為Java虛擬機 平臺上支持的第三種編程語言。除了Java之外,Java虛擬機還支持Groovy腳本語言。Doug Lea、Apache和Google三個JCP執委會成員對此規范表示了支持。

    按照Java最初的設計思路,有很多語言都可以在JVM上 運行(詳細列表參見http://en.wikipedia.org/wiki/List_of_Java_scripting_languages), 但這些語言大多沒有流行起來。直到2004年為止,Java平臺事實上只有一種編程語言,也就是Java。2004年3月,Groovy(JSR- 241)成為了Java平臺上的第二種編程語言。

    消息全文請看:http://rmh.blogs.com/weblog/2005/05/beanshell_the_3.html

    http://www.cn-java.com/target/news.php?news_id=2450

    簡介:
    BeanShell是一種腳本語言,一種完全符合java語法的java腳本語言,并且又擁有自己的一些語法和方法,beanShell是一種松散類型的腳本語言(這點和JS類似)。
    下載地址:http://www.beanshell.org

    設置環境
    l 把;bsh-xx.jar放到$JAVA_HOME/jre/lib/ext文件夾下
    l unix: export CLASSPATH=$CLASSPATH:bsh-xx.jar
    l windows: set classpath %classpath%;bsh-xx.jar

    運行方式:
    l 界面UI方式 :java bsh.Console
    l 命令行方式 :java bsh.Interpreter
    l 運行腳本文件:java bsh.Interpreter filename [ args ]



    簡單舉例:
    在classpath中設置好環境變量,打開dos窗口,鍵入:java bsh.Console命令
    出現BeanShell圖片代表設置成功,beanshell開始運行





    測試內容:
    設置變量
    foo = "Foo";
    four = (2 + 2)*2/2;
    打印變量
    print( foo + " = " + four );
    循環
    for (i=0; i<5; i++)
    print(i);
    在窗口中打印按鈕
    button = new JButton( "My Button" );
    frame = new JFrame( "My Frame" );
    frame.getContentPane().add( button, "Center" );
    frame.pack();
    frame.setVisible(true);

    完整代碼:
    foo = "Foo";
    four = (2 + 2)*2/2;
    print( foo + " = " + four );

    for (i=0; i<5; i++)
    print(i);

    button = new JButton( "My Button" );
    frame = new JFrame( "My Frame" );
    frame.getContentPane().add( button, "Center" );
    frame.pack();
    frame.setVisible(true);


    在窗口中輸入上面的代碼

    敲回車執行,運行結果如圖


    說明:
    因為beanshell是松散類型的腳本語言因此可以直接寫
    foo = "Foo";
    four = (2 + 2)*2/2;

    print是beanshell提供一種簡單的打印命令相當于java中的System.out.println()





    其他的beanshell腳本命令
    · source(), run() – 讀取,或者運行一個腳本文件
    · frame() – 顯示一個窗口
    · load(), save() – 讀取或者保存一個腳本對象到文件
    · cd(), cat(), dir(), pwd(), etc. 使用Unix下面的命令
    · exec() – 運行一個本地的方法
    · javap() –使用javap命令.
    · setAccessibility() – 設置可以接收private和protected類型的變量
    BeanShell命令不一定都是內置的腳本命令,腳本方法會自動從classpath中取方法使用,因此你可以添加你自己的腳本到classpath中來擴充基本的命令






    腳本方法
    一般的方法:
    int addTwoNumbers( int a, int b ) {
    return a + b;
    }

    sum = addTwoNumbers( 5, 7 ); // 12
    也可以使用動態的變量類型(無狀態)方法
    add( a, b ) {
    return a + b;
    }
    foo = add(1, 2); // 3
    foo = add(1, “2”); //”12” 只要有一個為字符串全部按照字符串處理,系統不會根據1是數字在前把“2”轉換成數字處理(特別注意)
    foo = add("Oh", " baby"); // "Oh baby"




    實現接口
    實現任何接口需要java1.3或者更高
    可以使用缺省的java匿名類的語法實現一個接口類,例如:
    ActionListener scriptedListener = new ActionListener() {
    actionPerformed( event ) { ... }
    }
    不需要實現接口的所有的方法,只需要實現你使用的方法即可,如果使用你沒有實現的方法,beanshell將拋出一個錯誤,
    ml = new MouseListener() {
    mousePressed( event ) { ... }
    // handle the rest
    invoke( name, args ) { print("Method: "+name+" invoked!");
    }








    腳本對象
    使用特殊的關鍵字this可以創建一個對象(根JS類似)
    foo() {
    print("foo");
    x=5;

    bar() {
    print("bar");
    }

    return this;
    }

    myfoo = foo(); // prints "foo"
    print( myfoo.x ); // prints "5"
    myfoo.bar(); // prints "bar"

    從應用程序中調用BeanShell
    創建一個BeanShell的解釋器(interpreter)用eval()和source()命令可以對一個字符串求值和運行一個腳本文件
    使用set()方法可以給一個對象傳入一個變量的參考
    使用get()方法可以重新得到一個變量的結果


    完整代碼:
    package cn.com.sparknet.util;

    import bsh.*;
    import java.util.*;

    public class BeanShell {
    public static void main(String[] args) {
    try {
    Interpreter interpreter = new Interpreter(); // 構造一個解釋器
    interpreter.set("foo", 5); // 設置變量
    interpreter.set("date", new Date()); //設置一個時間對象
    Date date = (Date) interpreter.get("date"); // 重新得到時間變量
    interpreter.println(date.toString()); //打印時間變量
    interpreter.eval("bar = foo*10"); // 對一段腳本求值,并得到結果
    System.out.println(interpreter.get("bar")); //打印變量
    interpreter.source("d:\\helloWorld.bsh"); // 導入并執行一個腳本文件
    }
    catch (Exception e) {
    //如果發生異常,寫入日志文件
    Log.error(new BeanShell(), "main", FormatDate.getCurrDate(), e.getMessage());
    }
    }
    }






    BeanShell語法
    BeanShell是一種最原始的java解釋器。
    標準的java語法
    /*
    Standard Java syntax
    */

    // Use a hashtable
    Hashtable hashtable = new Hashtable();
    Date date = new Date();
    hashtable.put( "today", date );

    // Print the current clock value
    print( System.currentTimeMillis() );

    // Loop
    for (int i=0; i<5; i++)
    print(i);

    // Pop up a frame with a button in it
    JButton button = new JButton( "My Button" );
    JFrame frame = new JFrame( "My Frame" );
    frame.getContentPane().add( button, "Center" );
    frame.pack();
    frame.setVisible(true);
    松散類型的java語法
    /*
    Loosely Typed Java syntax
    */

    // Use a hashtable
    hashtable = new Hashtable();
    date = new Date();
    hashtable.put( "today", date );

    // Print the current clock value
    print( System.currentTimeMillis() );

    // Loop
    for (i=0; i<5; i++)
    print(i);

    // Pop up a frame with a button in it
    button = new JButton( "My Button" );
    frame = new JFrame( "My Frame" );
    frame.getContentPane().add( button, "Center" );
    frame.pack();
    frame.setVisible(true);
    異常處理
    標準的java異常
    try {
    int i = 1/0;
    } catch ( ArithmeticException e ) {
    print( e );
    }
    松散的異常處理(類似JS)
    try {
    ...
    } catch ( e ) {
    ...
    }
    松散類型變量的作用范圍
    標 準的java程序的變量作用范圍是在一個模塊中的(在模塊中聲明的變量),而在松散類型的語言中如果在一個模塊中沒有指定一個變量的類型,則認為是一個全 局變量(只有它以后的代碼可以使用該變量,系統在調用該變量的時候自動生成一個全局變量,也就為什么在調用模塊之前不能使用該變量的原因)
    // Arbitrary code block
    {
    y = 2; // Untyped variable assigned
    int x = 1; // Typed variable assigned
    }
    print( y ); // 2
    print( x ); // Error! x is undefined.

    // Same with any block statement: if, while, try/catch, etc.
    if ( true ) {
    y = 2; // Untyped variable assigned
    int x = 1; // Typed variable assigned
    }
    print( y ); // 2
    print( x ); // Error! x is undefined.

    同樣也使用于for-loop, if-else等循環語句
    for( int i=0; i<10; i++ ) { // typed for-init variable
    j=42;
    }
    print( i ); // Error! 'i' is undefined.
    print( j ); // 42

    for( z=0; z<10; z++ ) { } // untyped for-init variable
    print( z ); // 10









    方便靈活的語法
    標準的java語法
    java.awt.Button button = new java.awt.Button();
    button.setLabel(“javaButton”);
    松散的語法
    button = new java.awt.Button();
    button.label = "my button";
    你也可以使用{}來對一個對象設置屬性
    b = new java.awt.Button();
    b{"label"} = "my button"; // Equivalent to: b.setLabel("my button");

    h = new Hashtable();
    h{"foo"} = "bar"; // Equivalent to: h.put("foo", "bar");




    包裝和未包裝(box和unbox)
    BeanShell自動轉為簡單類型
    i=5;
    iw=new Integer(5);
    print( i * iw ); // 25
    導入類和包
    import javax.xml.parsers.*;
    import mypackage.MyClass;
    超級導入法:
    import *;
    BeanShell默認導入下面的包
    · java.lang
    · java.io
    · java.util
    · java.net
    · java.awt
    · java.awt.event
    · javax.swing
    · javax.swing.event
    友好文檔實體
    BeanShell支持特殊的文檔操作類型內容
    @gt > @lt <
    @lteq <= @gteq >=
    @or || @and &&
    @bitwise_and & @bitwise_or |
    @left_shift << @right_shift >>
    @right_unsigned_shift >>> @and_assign &=
    @or_assign |= @left_shift_assign <<=
    @right_shift_assign >>= @right_unsigned_shift_assign >>>=
    腳本方法
    你可以定義方法象java中的定義方法一樣
    int addTwoNumbers( int a, int b ) {
    return a + b;
    }
    你可以使用內餡的BeanShell方法使用他們
    sum = addTwoNumbers( 5, 7 );
    只有BeanShell變量可以被動態定義為動態類型,方法可以有動態的參數以及返回類型
    add( a, b ) {
    return a + b;
    }
    在這個方法中,BeanShell將動態的決定類型當這個方法被調用時并且能夠準確的計算出你想要的結果
    foo = add(1, 2);
    print( foo ); // 3

    foo = add("Oh", " baby");
    print( foo ); // Oh baby
    在第一個例子中BeanShell將把參數定義為數字型,并返回數字型
    在第二個例子中BeanShell將把參數定義為字符型,并返回字符對象
    變量和方法的可見范圍
    就像您所預期的那樣,在方法內您可以參考到上下文中上面的變量和方法
    a = 42;
    someMethod() { ... }

    foo() {
    print( a );
    someMethod(); // invoke someMethod()
    }

    // invoke foo()
    foo(); // prints 42
    如果一個變量只有在方法內使用請定義成局部變量,即加上類型,如果是全局變量請在方法外定義
    var = "global";
    foo() {
    print(var);
    String var = "local";
    print(var);
    }
    foo();
    print(var);
    將打印出
    global
    local
    global
    方法內的var(第四行)變量屬于局部變量,不會覆蓋全局變量var(第一行)的因此改變var(第四行)變量不會影響到全局變量var(第一行)
    范圍參考:super
    使用super關鍵字可以在局部參考到全局變量
    var = "global";
    foo() {
    String var = "local";
    print(var);
    print(super.var);
    }
    foo();
    將輸出
    local
    global







    腳本對象
    this對象
    在java標準語言中可以使用this返回一個類的一個實例
    // MyClass.java
    MyClass {
    Object getObject() {
    return this; // return a reference to our object
    }
    }
    在這個例子中getObject() 方法是返回MyClass類的一個實例
    在BeanShell中對象中的變量只是局部的變量在對象內可以使用,在對象外是不可以使用(不同于前面for-loop,if-else中的使用);
    // Define the foo() method:
    foo() {
    bar = 42;
    print( bar );
    }

    // Invoke the foo() method:
    foo(); // prints 42

    print( bar ); // Error, bar is undefined here
    可以使用this返回對象,使用對象加上“.”運算符參考屬性(類似JS)
    foo() {
    bar = 42;
    return this;
    }

    fooObj = foo();
    print( fooObj.bar ); // prints 42!
    同樣對象中也可以定義一些方法
    foo() {
    bar() {
    ...
    }
    }
    例如
    foo() {
    int a = 42;
    bar() {
    print("The bar is open!");
    }

    bar();
    return this;
    }

    // Construct the foo object
    fooObj = foo(); // prints "the bar is open!"
    // Print a variable of the foo object
    print ( fooObj.a ) // 42
    // Invoke a method on the foo object
    fooObj.bar(); // prints "the bar is open!"
    也可以把bar()和foo也可以代參數
    foo() {
    return this;
    }
    bar(int a) {
    print("The bar is open!" + a);
    }
    foo().bar(1);
    也可以把bar()方法定義到對象外面

    foo() {
    bar(int a) {
    print("The bar is open!" + a);
    }
    return this;
    }
    foo().bar(1);
    BeanShell一種松散的腳本語言,有很多中聲明的方法可以使用
    This super global
    This是參考當前對象
    Super是參考父親對象
    Global是參考最上層對象
    super.super.super...foo = 42; // Chain super. to reach the top
    global.foo = 42;








    簡單例子:
    文本拖動:

    dragText() {
    f = new Frame("Drag in the box");
    f.setFont( new Font("Serif", Font.BOLD, 24) );
    f.setSize(300, 300);
    f.show();
    gc = f.getGraphics();
    gc.setColor(Color.cyan);
    mouseDragged( e ) {
    gc.drawString("Drag Me!", e.getX(), e.getY());
    }
    mouseMoved( e ) { }
    f.addMouseMotionListener( this );
    }
    簡單畫圖

    import bsh.util.BshCanvas; // BshCanvas simply buffers graphics

    graph( int width, int height ) {
    canvas=new BshCanvas();
    canvas.setSize( width, height );
    frame=frame( canvas );
    graphics=canvas.getBufferedGraphics();
    // draw axis
    graphics.setColor( Color.red );
    graphics.drawLine( 0, height/2, width, height/2 );
    graphics.drawLine( width/2, 0, width/2, height );
    graphics.setColor( Color.black );

    plot(int x, int y) {
    graphics.fillOval( (x+width/2-1), (y+height/2-1), 3, 3);
    canvas.repaint();
    }

    return this;
    }

    drawSin( graph ) {
    for (int x=-100; x<100; x++ ) {
    y=(int)(50*Math.sin( x/10.0 ));
    graph.plot( x, y );
    }
    }
    簡單web瀏覽器

    import javax.swing.*;
    import javax.swing.event.*;
    import javax.swing.text.*;
    import java.awt.event.*;
    import java.awt.*;

    JFrame browser( startingUrl ) {
    invoke( method, args ) {}

    windowClosing(WindowEvent we) {
    we.getWindow().setVisible(false);
    }

    setPage( url ) {
    try {
    pane.setPage( url );
    } catch(Exception e) {
    statusBar.setText("Error opening page: "+url);
    }
    }

    hyperlinkUpdate( HyperlinkEvent he ) {
    type = he.getEventType();
    if (type == HyperlinkEvent.EventType.ENTERED) {
    pane.setCursor(
    Cursor.getPredefinedCursor( Cursor.HAND_CURSOR) );
    statusBar.setText(he.getURL().toString());
    } else
    if (type == HyperlinkEvent.EventType.EXITED) {
    pane.setCursor( Cursor.getDefaultCursor() );
    statusBar.setText(" ");
    } else {
    setPage( he.getURL() );
    if (urlField != null)
    urlField.setText(he.getURL().toString());
    }
    }

    frame = new JFrame("Browser");
    frame.setSize(400,300);
    frame.addWindowListener( this );

    urlPanel = new JPanel();
    urlPanel.setLayout(new BorderLayout());
    urlField = new JTextField(startingUrl);
    urlPanel.add(new JLabel("Site: "), BorderLayout.WEST);
    urlPanel.add(urlField, BorderLayout.CENTER);

    statusBar = new JLabel(" ");
    pane = new JEditorPane();
    pane.setEditable(false);
    setPage( startingUrl );
    jsp = new JScrollPane(pane);

    frame.getContentPane().add(jsp, BorderLayout.CENTER);
    frame.getContentPane().add(urlPanel, BorderLayout.SOUTH);
    frame.getContentPane().add(statusBar, BorderLayout.NORTH);

    // This is the equivalent of an inner class in bsh.
    urlTextHandler() {
    actionPerformed(ActionEvent ae) {
    setPage( ae.getActionCommand() );
    }
    return this;
    }

    urlField.addActionListener( urlTextHandler() );
    pane.addHyperlinkListener( (HyperlinkListener)this );

    return frame;
    }
    browser = browser("http://java.sun.com/");
    browser.show();




    更多的文檔參考BeanShell網站

    http://www.beanshell.org
    posted @ 2008-10-08 14:34 xiaoxinchen 閱讀(2471) | 評論 (0)編輯 收藏

    在處理一個大數據量數據庫的時候
    突然發現mysql對于count(*)的不同處理會造成不同的結果

    比如執行
    SELECT count(*) FROM tablename
    即使對于千萬級別的數據mysql也能非常迅速的返回結果
    而對于
    SELECT count(*) FROM tablename WHERE…..
    mysql的查詢時間開始攀升

    仔細查閱累下手冊,發現當沒有WHERE語句對于整個mysql的表進行count運算的時候
    MyISAM類型的表中保存有總的行數,而當添加有WHERE限定語句的時候Mysql需要對整個表進行檢索
    從而得出count的數值

    突然又想起來看到的不少新興的php程序對于count的處理并沒有很好的意思到這點
    記錄下

    順便提下mysql的DISTINCT的關鍵字有很多你想不到的用處
    1.在count 不重復的記錄的時候能用到
    比如SELECT COUNT( DISTINCT id ) FROM tablename;
    就是計算talbebname表中id不同的記錄有多少條

    2,在需要返回記錄不同的id的具體值的時候可以用
    比如SELECT DISTINCT id FROM tablename;
    返回talbebname表中不同的id的具體的值

    3.上面的情況2對于需要返回mysql表中2列以上的結果時會有歧義
    比如SELECT DISTINCT id, type FROM tablename;
    實際上返回的是 id與type同時不相同的結果,也就是DISTINCT同時作用了兩個字段,必須得id與tyoe都相同的才被排除了,與我們期望的結果不一樣

    4.這時候可以考慮使用group_concat函數來進行排除,不過這個mysql函數是在mysql4.1以上才支持的

    5.其實還有另外一種解決方式,就是使用
    SELECT id, type, count(DISTINCT id) FROM tablename
    雖然這樣的返回結果多了一列無用的count數據(或許你就需要這個我說的無用數據)
    返回的結果是 只有id不同的所有結果和上面的4類型可以互補使用,就是看你需要什么樣的數據了


    posted @ 2008-10-07 14:46 xiaoxinchen 閱讀(251) | 評論 (0)編輯 收藏
    主站蜘蛛池模板: 亚洲伊人久久大香线蕉| 免费又黄又爽的视频| 99在线在线视频免费视频观看| 香蕉免费看一区二区三区| 国产日韩AV免费无码一区二区三区| 国产亚洲人成在线播放| 国产亚洲精品成人久久网站| 亚洲国产午夜精品理论片在线播放| 亚洲一区二区三区免费视频| 亚洲国产av美女网站| 国产色在线|亚洲| 亚洲人成网站999久久久综合| 亚洲人成网亚洲欧洲无码| 亚洲Av永久无码精品一区二区| 国产精品观看在线亚洲人成网| 色窝窝亚洲AV网在线观看| 美女扒开尿口给男人爽免费视频 | 亚洲国产黄在线观看| 亚洲精品人成无码中文毛片| 三上悠亚亚洲一区高清| 亚洲国产精品无码专区在线观看| 亚洲人成在线电影| 亚洲AV无码乱码在线观看代蜜桃| 亚洲天堂男人影院| 亚洲爆乳大丰满无码专区| 免费人妻精品一区二区三区| 在线视频网址免费播放| 2021在线观看视频精品免费| 100000免费啪啪18免进| 免费高清在线爱做视频| 国产yw855.c免费视频| 亚洲国产中文字幕在线观看| 亚洲综合图色40p| 亚洲最大在线观看| 国产精品亚洲一区二区麻豆| 国产亚洲美女精品久久| 黄色网址免费在线观看| 一级毛片免费观看| 国产美女无遮挡免费视频网站 | 亚洲一级毛片免费观看| 在线免费观看中文字幕|